MVVM и свойство TextBox SelectedText
У меня есть текстовое поле с ContextMenu в нем. Когда пользователь щелкает правой кнопкой мыши внутри текстового поля и выбирает соответствующий MenuItem, я хотел бы захватить SelectedText в моей viewmodel. Я не нашел хорошего способа сделать это способом" MVVM".
до сих пор у меня есть мое приложение, использующее способ Джоша Смита MVVM. Я ищу, чтобы перейти к Cinch. Не уверен, что платформа Cinch будет обрабатывать такие проблемы. Мысли?
3 ответов
нет простого способа привязать SelectedText к источнику данных, потому что это не DependencyProperty... однако довольно легко создать вложенное свойство, которое вы могли бы привязать вместо этого.
вот основная реализация:
public static class TextBoxHelper
{
public static string GetSelectedText(DependencyObject obj)
{
return (string)obj.GetValue(SelectedTextProperty);
}
public static void SetSelectedText(DependencyObject obj, string value)
{
obj.SetValue(SelectedTextProperty, value);
}
// Using a DependencyProperty as the backing store for SelectedText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedTextProperty =
DependencyProperty.RegisterAttached(
"SelectedText",
typeof(string),
typeof(TextBoxHelper),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedTextChanged));
private static void SelectedTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
TextBox tb = obj as TextBox;
if (tb != null)
{
if (e.OldValue == null && e.NewValue != null)
{
tb.SelectionChanged += tb_SelectionChanged;
}
else if (e.OldValue != null && e.NewValue == null)
{
tb.SelectionChanged -= tb_SelectionChanged;
}
string newValue = e.NewValue as string;
if (newValue != null && newValue != tb.SelectedText)
{
tb.SelectedText = newValue as string;
}
}
}
static void tb_SelectionChanged(object sender, RoutedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
SetSelectedText(tb, tb.SelectedText);
}
}
}
затем вы можете использовать его так в XAML:
<TextBox Text="{Binding Message}" u:TextBoxHelper.SelectedText="{Binding SelectedText}" />
примеры приложений в WPF Application Framework (WAF) выбрал другой способ решить эту проблему. Там ViewModel разрешен доступ к представлению через интерфейс (IView), и поэтому он может запросить текущий SelectedText.
Я считаю, что привязка не должна использоваться в каждом сценарии. Иногда запись нескольких строк в коде намного чище, чем использование высокоразвитых вспомогательных классов. Но это только мое мнение :-)
jbe
Я знаю, что на него ответили и приняли, но я думал, что добавлю свое решение. Я использую поведение для моста между моделью представления и текстовым полем. Поведение имеет свойство зависимости (CaretPositionProperty), которое может быть привязано к модели представления. Внутренне поведение имеет дело с обновлениями в / из текстового поля.
public class SetCaretIndexBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty CaretPositionProperty;
private bool _internalChange;
static SetCaretIndexBehavior()
{
CaretPositionProperty = DependencyProperty.Register("CaretPosition", typeof(int), typeof(SetCaretIndexBehavior), new PropertyMetadata(0, OnCaretPositionChanged));
}
public int CaretPosition
{
get { return Convert.ToInt32(GetValue(CaretPositionProperty)); }
set { SetValue(CaretPositionProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.KeyUp += OnKeyUp;
}
private static void OnCaretPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (SetCaretIndexBehavior)d;
if (!behavior._internalChange)
{
behavior.AssociatedObject.CaretIndex = Convert.ToInt32(e.NewValue);
}
}
private void OnKeyUp(object sender, KeyEventArgs e)
{
_internalChange = true;
CaretPosition = AssociatedObject.CaretIndex;
_internalChange = false;
}
}