Hi I'm trying to bind to the TextBox.CaretIndex
property which isn't a DependencyProperty
, so I created a Behavior
, but it doesn't work as expected.
Expectation (when focused)
Current behavior
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
<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 DependencyObject
which was never needed before, because CursorPositionProperty
is already a DependencyProperty
, so this should be enough. I also think I need to use some events in my Behavior
to set my CursorPositionProperty
correctly, but I don't know which.
After fighting with my Behavior i can present you a 99% working solution
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
<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
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:
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With