2010-02-11 51 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; 
    } 
}