2010-02-11 4 views
12

У меня есть TextBox с ContextMenu в нем. Когда пользователь щелкнет правой кнопкой мыши внутри TextBox и выберет соответствующий MenuItem, я хотел бы захватить SelectedText в моей модели viewmodel. Я не нашел хорошего способа сделать это «MVVM».MVVM и свойство SelectedText TextBox

До сих пор у меня было мое приложение, использующее способ Джош Смита в MVVM. Я ищу, чтобы перебраться в Чинч. Не уверен, что Cinch Framework будет обрабатывать такие проблемы. Мысли?

ответ

18

Нет простого способа привязать 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}" /> 
+0

Спасибо! Это сделал трюк. Так очевидно, и я пропустил это. Еще раз спасибо. – Eric

+0

Я пытаюсь сделать то же самое для свойства CaretIndex, но кажется, что он не работает. Можете ли вы помочь – TheITGuy

+0

@TheITGuy, не видя ваш код ... Вероятно, вы должны создать новый вопрос (вы можете опубликовать ссылку здесь, я отвечу, если смогу) –

2

Образцы приложений в WPF Application Framework (WAF) выбрал другой способ решить эту проблему. Там ViewModel разрешен доступ к представлению через интерфейс (IView), и поэтому он может запрашивать текущий SelectedText.

Я считаю, что привязка не должна использоваться в каждом сценарии. Иногда писать несколько строк в коде намного проще, чем использовать высокоразвитые вспомогательные классы. Но это только мое мнение :-)

JBE

+0

Это решение имеет то преимущество, что можно вывести значение выбранного текста в любое свойство строки с помощью общедоступного средства настройки. Гибкость, возможно, перевешивает дополнительные строки кода. Кроме того, с несколькими небольшими изменениями решение может быть использовано для привязки свойств SelectionStart и SelectionEnd, позволяющих легко моделировать модели просмотра и получать выбор текста. – Gusdor

1

Я знаю, что это был ответ принят, но я думал, что я хотел бы добавить свое решение. Я использую Поведение для соединения между моделью просмотра и TextBox. Поведение имеет свойство зависимости (CaretPositionProperty), которое может быть связано двумя способами с моделью представления. Внутренне поведение связано с обновлениями в/из TextBox.

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; 
    } 
} 
Смежные вопросы