2010-02-11 52 views
12

我有一個TextBox,裏面有一個ContextMenu。當用戶在TextBox內單擊右鍵並選擇適當的MenuItem時,我想抓住ViewModel中的SelectedText。我還沒有找到一個很好的方法來實現這種「MVVM」方式。MVVM和TextBox的SelectedText屬性

到目前爲止,我有我的申請利用喬希史密斯的MVVM的方式。我正在轉移到Cinch。不知道Cinch框架是否會處理這樣的問題。思考?

回答

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 2010-02-12 15:15:23

+0

我正在嘗試爲CaretIndex屬性做同樣的事情,但它似乎沒有工作。你可以幫助 – TheITGuy 2011-03-17 17:22:59

+0

@TheITGuy,而不是沒有看到你的代碼...你應該可能創建一個新的問題(你可以發佈鏈接在這裏,我會回答,如果我可以) – 2011-03-17 18:43:40

2

WPF Application Framework (WAF)示例應用程序選擇了另一種方式來解決這個問題。在那裏ViewModel被允許通過接口(IView)訪問視圖,因此它可以請求當前的SelectedText。

我相信綁定不應該用在每個場景中。有時在代碼後面寫幾行比使用高級輔助類更清晰。但是,這只是我的意見:-)

乙腦

+0

此解決方案的優點是能夠使用公共設置器來推送任何字符串屬性上的選定文本的值。靈活性可以說超過了額外的代碼行。此外,通過一些小的調整,該解決方案可用於綁定SelectionStart和SelectionEnd屬性,允許視圖模型輕鬆設置,並接收文本選擇。 – Gusdor 2011-05-18 13:39:21

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