2013-02-13 110 views
8

我在我的项目中使用了基于WPF和MVVM的AvalonEdit。 阅读this post后,我创建了以下类:AvalonEdit中的双向绑定不起作用

public class MvvmTextEditor : TextEditor, INotifyPropertyChanged 
{ 
    public static DependencyProperty DocumentTextProperty = 
     DependencyProperty.Register("DocumentText", 
            typeof(string), typeof(MvvmTextEditor), 
     new PropertyMetadata((obj, args) => 
     { 
      MvvmTextEditor target = (MvvmTextEditor)obj; 
      target.DocumentText = (string)args.NewValue; 
     }) 
    ); 

    public string DocumentText 
    { 
     get { return base.Text; } 
     set { base.Text = value; } 
    } 

    protected override void OnTextChanged(EventArgs e) 
    { 
     RaisePropertyChanged("DocumentText"); 
     base.OnTextChanged(e); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    public void RaisePropertyChanged(string info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
} 

,并用下面的XAML使用此控件:

<avalonedit:MvvmTextEditor x:Name="xmlMessage"> 
    <avalonedit:MvvmTextEditor.DocumentText> 
     <Binding Path ="MessageXml" Mode="TwoWay" 
       UpdateSourceTrigger="PropertyChanged"> 
     <Binding.ValidationRules> 
      <local:XMLMessageValidationRule /> 
      </Binding.ValidationRules> 
     </Binding> 
    </avalonedit:MvvmTextEditor.DocumentText> 
</avalonedit:MvvmTextEditor> 

但绑定工作OneWay并且不更新我的字符串属性,也没有运行验证规则。

如何修复绑定按预期工作TwoWay

+0

请问你能告诉我你是如何得到这个工作的?我问另一个问题[这里](http://stackoverflow.com/questions/18964176/two-way-binding-to-avalonedit-document-text-using-mvvm?noredirect=1#comment28010773_18964176)... – MoonKnight 2013-09-23 19:46:00

回答

6

WPF绑定不使用您的DocumentText属性;而是直接访问依赖项属性的基础值。

您的OnTextChanged方法实际上并未更改基础依赖项属性的值。您需要的值从base.Text复制到依赖属性上的每一个变化:

protected override void OnTextChanged(EventArgs e) 
{ 
    SetCurrentValue(DocumentTextProperty, base.Text); 
    base.OnTextChanged(e); 
} 

,如果你遵循了正确的模式实现DependencyProperty这个问题会更容易地看到:DocumentText属性应使用GetValue/SetValue方法,而不是访问不同的后台存储。

+1

谢谢非常多的答复 - 我已经帮助。然而,我对如何在'MvvmTextEditor'类的代码中使用'GetValue' /'SetValue'感到困惑?它似乎与目前的实施不兼容。 – MoonKnight 2013-09-23 09:41:19

4

即使使用GetValue和SetValue,也无法让TextProperty在文本更改时更新绑定,因此无论如何都必须遵循Daniel答案。

我确实改变了一下,以使其更直观不必使用文本作为正常dependecy模式最终用户:

public new string Text 
    { 
     get { return (string)GetValue(TextProperty); } 
     set { SetValue(TextProperty, value); } 
    } 

    internal string baseText { get { return base.Text; } set { base.Text = value; } } 

    public static DependencyProperty TextProperty = 
    DependencyProperty.Register("Text", typeof(string), typeof(MvvmTextEditor), 
     // binding changed callback: set value of underlying property 
     new PropertyMetadata((obj, args) => 
     { 
      MvvmTextEditor target = (MvvmTextEditor)obj; 
      if(target.baseText != (string)args.NewValue) //avoid undo stack overflow 
       target.baseText = (string)args.NewValue; 
     }) 
    ); 

    protected override void OnTextChanged(EventArgs e) 
    {    
     SetCurrentValue(TextProperty, baseText); 
     RaisePropertyChanged("Text"); 
     base.OnTextChanged(e); 
    } 

我不得不检查,如果相同的文本已经在那里了,以避免撤消堆栈引擎异常。性能也是一个很好的交易。

1

我根据上面的答案尝试了一些代码,稍作修改,因为绑定对我来说都不适用。下面的内容应该允许绑定两种方式。

public static readonly DependencyProperty MyContentProperty = DependencyProperty.Register(
     "MyContent", typeof(string), typeof(MyTextEditor), new PropertyMetadata("", OnMyContentChanged)); 

    private static void OnMyContentChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     var control = (MyTextEditor)sender; 
     if (string.Compare(control.MyContent, e.NewValue.ToString()) != 0) 
     { 
      //avoid undo stack overflow 
      control.MyContent = e.NewValue.ToString(); 
     } 
    } 

    public string MyContent 
    { 
     get { return Text; } 
     set { Text = value; } 
    } 

    protected override void OnTextChanged(EventArgs e) 
    { 
     SetCurrentValue(MyContentProperty, Text); 
     base.OnTextChanged(e); 
    }