如何绑定到CaretIndex又称文本框的光标位置

Wii*_*axx 3 c# wpf binding textbox mvvm

嗨,我正在尝试绑定到TextBox.CaretIndex不是的属性DependencyProperty,所以我创建了一个Behavior,但它没有按预期工作.

期望(专注时)

  • 默认= 0
  • 如果我在视图中更改了值,它应该更改viewmodel中的值
  • 如果我更改了viewmodel中的值,它应该更改我视图中的值

目前的行为

  • 当窗口打开时,viewmodel值被调用

代码隐藏

public class TextBoxBehavior : DependencyObject
{
    public static readonly DependencyProperty CursorPositionProperty =
        DependencyProperty.Register(
            "CursorPosition",
            typeof(int),
            typeof(TextBoxBehavior),
            new FrameworkPropertyMetadata(
                default(int),
                new PropertyChangedCallback(CursorPositionChanged)));

    public static void SetCursorPosition(DependencyObject dependencyObject, int i)
    {
        // breakpoint get never called
        dependencyObject.SetValue(CursorPositionProperty, i); 
    }

    public static int GetCursorPosition(DependencyObject dependencyObject)
    {
        // breakpoint get never called
        return (int)dependencyObject.GetValue(CursorPositionProperty);
    }

    private static void CursorPositionChanged(
        DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        // breakpoint get never called
        //var textBox = dependencyObject as TextBox;
        //if (textBox == null) return;
    }
}
Run Code Online (Sandbox Code Playgroud)

XAML

<TextBox Text="{Binding TextTemplate,UpdateSourceTrigger=PropertyChanged}"
         local:TextBoxBehavior.CursorPosition="{Binding CursorPosition}"/>
Run Code Online (Sandbox Code Playgroud)

更多的信息

我认为这里确实存在一些问题,因为我需要从中获取它DependencyObject之前从未需要的,因为CursorPositionProperty它已经是一个DependencyProperty,所以这应该足够了.我还认为我需要使用一些事件BehaviorCursorPositionProperty正确设置,但我不知道哪个.

Wii*_*axx 7

与我的行为斗争后,我可以为您提供99%的工作解决方案

行为

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfMVVMTextBoxCursorPosition
{
    public class TextBoxCursorPositionBehavior : DependencyObject
    {
        public static void SetCursorPosition(DependencyObject dependencyObject, int i)
        {
            dependencyObject.SetValue(CursorPositionProperty, i);
        }

        public static int GetCursorPosition(DependencyObject dependencyObject)
        {
            return (int)dependencyObject.GetValue(CursorPositionProperty);
        }

        public static readonly DependencyProperty CursorPositionProperty =
                                           DependencyProperty.Register("CursorPosition"
                                                                       , typeof(int)
                                                                       , typeof(TextBoxCursorPositionBehavior)
                                                                       , new FrameworkPropertyMetadata(default(int))
                                                                       {
                                                                           BindsTwoWayByDefault = true
                                                                           ,DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                                                                       }
                                                                       );

        public static readonly DependencyProperty TrackCaretIndexProperty =
                                                    DependencyProperty.RegisterAttached(
                                                        "TrackCaretIndex",
                                                        typeof(bool),
                                                        typeof(TextBoxCursorPositionBehavior),
                                                        new UIPropertyMetadata(false
                                                                                , OnTrackCaretIndex));

        public static void SetTrackCaretIndex(DependencyObject dependencyObject, bool i)
        {
            dependencyObject.SetValue(TrackCaretIndexProperty, i);
        }

        public static bool GetTrackCaretIndex(DependencyObject dependencyObject)
        {
            return (bool)dependencyObject.GetValue(TrackCaretIndexProperty);
        }

        private static void OnTrackCaretIndex(DependencyObject dependency, DependencyPropertyChangedEventArgs e)
        {
            var textbox = dependency as TextBox;

            if (textbox == null)
                return;
            bool oldValue = (bool)e.OldValue;
            bool newValue = (bool)e.NewValue;

            if (!oldValue && newValue) // If changed from false to true
            {
                textbox.SelectionChanged += OnSelectionChanged;
            }
            else if (oldValue && !newValue) // If changed from true to false
            {
                textbox.SelectionChanged -= OnSelectionChanged;
            }
        }

        private static void OnSelectionChanged(object sender, RoutedEventArgs e)
        {
            var textbox = sender as TextBox;

            if (textbox != null)
                SetCursorPosition(textbox, textbox.CaretIndex); // dies line does nothing
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

XAML

    <TextBox Height="50" VerticalAlignment="Top"
             Name="TestTextBox"
             Text="{Binding MyText}"
             vm:TextBoxCursorPositionBehavior.TrackCaretIndex="True"
             vm:TextBoxCursorPositionBehavior.CursorPosition="{Binding CursorPosition,Mode=TwoWay}"/>

    <TextBlock Height="50" Text="{Binding CursorPosition}"/>
Run Code Online (Sandbox Code Playgroud)

只是因为我不知道为什么它不起作用=> BindsTwoWayByDefault = true.据我所知,它对绑定没有影响因为我需要在XAML中设置绑定模式

  • 这完美地设置了选择更改时的 videmodel 属性,但是当我从代码中更改 viewmodel 属性时,它不会更改 UI 上的插入索引。有什么遗漏的吗? (2认同)

Vla*_*lov 5

我遇到了类似的问题,对我来说最简单的解决方案是继承 TextBox 并添加 DependencyProperty。所以它看起来像这样:

namespace UI.Controls
{
    public class MyTextBox : TextBox
    {
        public static readonly DependencyProperty CaretPositionProperty =
            DependencyProperty.Register("CaretPosition", typeof(int), typeof(MyTextBox),
                new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCaretPositionChanged));

        public int CaretPosition
        {
            get { return (int)GetValue(CaretPositionProperty); }
            set { SetValue(CaretPositionProperty, value); }
        }

        public MyTextBox()
        {
            SelectionChanged += (s, e) => CaretPosition = CaretIndex;
        }

        private static void OnCaretPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            (d as MyTextBox).CaretIndex = (int)e.NewValue;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

...在我的 XAML 中:

xmlns:controls="clr-namespace:IU.Controls"
...
<controls:MyTextBox CaretPosition="{Binding CaretPosition}"/>
Run Code Online (Sandbox Code Playgroud)

...当然还有视图模型中的 CaretPosition 属性。如果您不打算将视图模型绑定到其他文本编辑控件,这可能就足够了,如果是的话 - 您可能需要另一个解决方案。