【问题标题】:Binding Failure in WPF using MVVM使用 MVVM 在 WPF 中绑定失败
【发布时间】:2014-07-12 17:22:50
【问题描述】:

我创建了一个继承自 AvalonEdit 的自定义 TextEditor 控件。我这样做是为了方便使用此编辑器控件使用 MVVM 和 Caliburn Micro。 [为显示目的而缩减]MvvTextEditor 类是

public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
    public MvvmTextEditor()
    {
        TextArea.SelectionChanged += TextArea_SelectionChanged;
    }

    void TextArea_SelectionChanged(object sender, EventArgs e)
    {
        this.SelectionStart = SelectionStart;
        this.SelectionLength = SelectionLength;
    }

    public static readonly DependencyProperty SelectionLengthProperty =
         DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor),
         new PropertyMetadata((obj, args) =>
             {
                 MvvmTextEditor target = (MvvmTextEditor)obj;
                 target.SelectionLength = (int)args.NewValue;
             }));

    public new int SelectionLength
    {
        get { return base.SelectionLength; }
        set { SetValue(SelectionLengthProperty, value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged([CallerMemberName] string caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
    }
}

现在,在拥有此控件的视图中,我有以下 XAML:

    <Controls:MvvmTextEditor 
        Caliburn:Message.Attach="[Event TextChanged] = [Action DocumentChanged()]"
        TextLocation="{Binding TextLocation, Mode=TwoWay}"
        SyntaxHighlighting="{Binding HighlightingDefinition}" 
        SelectionLength="{Binding SelectionLength, 
                                  Mode=TwoWay, 
                                  NotifyOnSourceUpdated=True, 
                                  NotifyOnTargetUpdated=True}" 
        Document="{Binding Document, Mode=TwoWay}"/>

我的问题是SelectionLength(和SelectionStart,但让我们现在只考虑长度,因为问题是一样的)。如果我用鼠标选择了一些东西,从视图到我的视图模型的绑定效果很好。现在,我编写了一个查找和替换实用程序,我想从后面的代码中设置SelectionLength(在TextEditor 控件中提供getset)。在我的视图模型中,我只是设置SelectionLength = 50,我在视图模型中实现了这一点,如

private int selectionLength;
public int SelectionLength
{
    get { return selectionLength; }
    set
    {
        if (selectionLength == value)
            return;
        selectionLength = value;
        Console.WriteLine(String.Format("Selection Length = {0}", selectionLength));
        NotifyOfPropertyChange(() => SelectionLength);
    }
}

当我设置SelectionLength = 50 时,DependencyProperty SelectionLengthProperty 不会在MvvmTextEditor 类中更新,就像绑定到我的控件的TwoWay 失败但使用Snoop 时没有任何迹象。我认为这只会通过绑定起作用,但事实并非如此。

我是否缺少一些简单的东西,或者我必须在 MvvmTextEditor 类中设置和事件处理程序,它侦听我的视图模型中的更改并更新 DP 本身 [这提出了它自己的问题] ?

感谢您的宝贵时间。

【问题讨论】:

    标签: c# wpf mvvm binding avalonedit


    【解决方案1】:

    这是因为来自DependencyPropertyGetterSetter 只是一个.NET Wrapper。框架将使用 GetValueSetValue 本身。

    您可以尝试从您的DependencyProperty 访问PropertyChangedCallback 并设置正确的值。

     public int SelectionLength
            {
                get { return (int)GetValue(SelectionLengthProperty); }
                set { SetValue(SelectionLengthProperty, value); }
            }
    
            // Using a DependencyProperty as the backing store for SelectionLength.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty SelectionLengthProperty =
                DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor), new PropertyMetadata(0,SelectionLengthPropertyChanged));
    
    
            private static void SelectionLengthPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
                var textEditor = obj as MvvmTextEditor;
    
                textEditor.SelectionLength = e.NewValue;
            }
    

    【讨论】:

    • 你在构造函数中注册SelectionLengthPropertyChanged吗?
    • 不,对不起。您正在Dependencyproperty Metadata 中注册它。在我的示例中忘记了这一点。另外:尝试将值直接设置为控件。我猜这将是最好的方式。
    • 一件事是我需要调用get { retrun base.SelectionLength; },因为我需要从底层控件(覆盖控件)返回它。
    • 在这里感谢您的帮助,但这没有奏效。它的行为方式与以前完全相同......
    【解决方案2】:

    如果您仍然开放,这是另一个答案。由于 SelectionLength 已经定义为基类上的依赖属性,而不是创建派生类(或向派生类添加已经存在的属性),我会使用附加属性来实现相同的功能。

    关键是使用 System.ComponentModel.DependencyPropertyDescriptor 订阅已经存在的 SelectionLength 依赖属性的更改事件,然后在事件处理程序中执行所需的操作。

    示例代码如下:

    public class SomeBehavior
    {
        public static readonly DependencyProperty IsEnabledProperty
            = DependencyProperty.RegisterAttached("IsEnabled",
            typeof(bool), typeof(SomeBehavior), new PropertyMetadata(OnIsEnabledChanged));
    
        public static void SetIsEnabled(DependencyObject dpo, bool value)
        {
            dpo.SetValue(IsEnabledProperty, value);
        }
    
        public static bool GetIsEnabled(DependencyObject dpo)
        {
            return (bool)dpo.GetValue(IsEnabledProperty);
        }
    
        private static void OnIsEnabledChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs args)
        {
            var editor = dpo as TextEditor;
            if (editor == null)
                return;
    
            var dpDescriptor = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(TextEditor.SelectionLengthProperty,editor.GetType());
            dpDescriptor.AddValueChanged(editor, OnSelectionLengthChanged);
        }
    
        private static void OnSelectionLengthChanged(object sender, EventArgs e)
        {
            var editor = (TextEditor)sender;
            editor.Select(editor.SelectionStart, editor.SelectionLength);
        }
    }
    

    Xaml 如下:

      <Controls:TextEditor Behaviors:SomeBehavior.IsEnabled="True">
        </Controls:TextEditor>
    

    【讨论】:

      【解决方案3】:

      我就是这样做的...

      public static readonly DependencyProperty SelectionLengthProperty =
           DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor),
           new PropertyMetadata((obj, args) =>
               {
                   MvvmTextEditor target = (MvvmTextEditor)obj;
                   if (target.SelectionLength != (int)args.NewValue)
                   {
                       target.SelectionLength = (int)args.NewValue;
                       target.Select(target.SelectionStart, (int)args.NewValue);
                   }
               }));
      
      public new int SelectionLength
      {
          get { return base.SelectionLength; }
          //get { return (int)GetValue(SelectionLengthProperty); }
          set { SetValue(SelectionLengthProperty, value); }
      }
      

      很抱歉浪费了您的时间。我希望这对其他人有帮助...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-27
        • 2013-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-11
        相关资源
        最近更新 更多