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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28233878/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:53:37  来源:igfitidea点击:

How to Bind to CaretIndex aka curser position of an Textbox

c#wpfmvvmbindingtextbox

提问by WiiMaxx

Hi I'm trying to bind to the TextBox.CaretIndexproperty which isn't a DependencyProperty, so I created a Behavior, but it doesn't work as expected.

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

Expectation (when focused)

期望(专注时)

  • default = 0
  • if I change the value in my viewit should change the value in my viewmodel
  • if I change the value in my viewmodelit should change the value in my view
  • 默认值 = 0
  • 如果我更改视图中的值,它应该更改我的视图模型中的值
  • 如果我更改视图模型中的值,它应该更改视图中的值

Current behavior

当前行为

  • viewmodel value gets called ones when the window opens
  • viewmodel 值在窗口打开时被调用

Code-behind

代码隐藏

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;
    }
}

XAML

XAML

<TextBox Text="{Binding TextTemplate,UpdateSourceTrigger=PropertyChanged}"
         local:TextBoxBehavior.CursorPosition="{Binding CursorPosition}"/>

Further Information

更多信息

I think there is something really wrong here because I need to derive it from DependencyObjectwhich was never needed before, because CursorPositionPropertyis already a DependencyProperty, so this should be enough. I also think I need to use some events in my Behaviorto set my CursorPositionPropertycorrectly, but I don't know which.

我认为这里真的有问题,因为我需要从中派生出DependencyObject以前从未需要的东西,因为CursorPositionProperty已经是 a DependencyProperty,所以这应该足够了。我也认为我需要使用一些事件BehaviorCursorPositionProperty正确设置我的,但我不知道是哪个。

回答by WiiMaxx

After fighting with my Behavior i can present you a 99%working solution

在与我的行为作斗争后,我可以向您展示99% 的工作解决方案

Behavior

行为

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
        }
    }
}

XAML

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}"/>

there is just on thing i don't know why it doesn't work => BindsTwoWayByDefault = true. it has no effect on the binding as far as i can tell you because of this i need to set the binding mode explicit in XAML

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

回答by Vladimir Krilov

I encountered a similar problem, and the easiest solution for me was to inherit from TextBox and add a DependencyProperty. So it looks like this:

我遇到了类似的问题,对我来说最简单的解决方案是从 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;
        }
    }
}

... and in my XAML:

...在我的 XAML 中:

xmlns:controls="clr-namespace:IU.Controls"
...
<controls:MyTextBox CaretPosition="{Binding CaretPosition}"/>

... and CaretPosition property in the View Model of course. If you're not going to bind your View Model to other text-editing controls, this may be sufficient, if yes - you'll probably need another solution.

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

回答by Sheridan

As you said, the TextBox.CaretIndexPropertyis nota DependencyProperty, so you cannot data bind to it. Even with your own DependencyProperty, it won't work... how would you expect to be notified when TextBox.CaretIndexProperty changes?

正如你所说,该TextBox.CaretIndex物业不是一个DependencyProperty,所以你无法将数据绑定到它。即使使用您自己的DependencyProperty,它也不起作用...当TextBox.CaretIndex属性更改时,您如何期望得到通知?