【问题标题】:Mvvm Binding with AvalonEdit SelectionStart, SelectionLengthMvvm 与 A​​valonEdit SelectionStart、SelectionLength 的绑定
【发布时间】:2024-04-21 09:15:02
【问题描述】:

我正在尝试使用 MVVM 模式让 AvalonEdit 正常工作,但我真的不知道该怎么做。我想将 SelectionLength 和 SelectionStart 绑定到我的 ViewModel,以便在执行某些业务逻辑时可以访问这两个值。

我开始像这样创建 DependencyProperties:

public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
    /// <summary>
    /// A bindable Text property
    /// </summary>
    public new string Text
    {
        get { return base.Text; }
        set { 
            base.Text = value; 
            RaisePropertyChanged();
        }
    }

    /// <summary>
    /// A bindable SelectionStart property
    /// </summary>
    public new int SelectionStart
    {
        get { return base.SelectionStart; }
        set {base.SelectionStart = value; }
    }

    /// <summary>
    /// A bindable SelectionLength property
    /// </summary>
    public new int SelectionLength
    {
        get { return base.SelectionLength; }
        set { base.SelectionLength = value; }
    }

    /// <summary>
    /// The bindable selection start property dependency property
    /// </summary>
    public static readonly DependencyProperty SelectionStartProperty =
        DependencyProperty.Register("SelectionStart", typeof(int), typeof(MvvmTextEditor),
            new PropertyMetadata((o, args) =>
            {
                var target = (TextEditor)o;
                target.SelectionStart = (int)args.NewValue;
            }));

    /// <summary>
    /// The bindable selection length property dependency property
    /// </summary>
    public static readonly DependencyProperty SelectionLengthProperty =
        DependencyProperty.Register("SelectionLength", typeof(int), typeof(MvvmTextEditor),
            new PropertyMetadata((o, args) =>
            {
                var target = (MvvmTextEditor) o;

                target.SelectionLength = (int)args.NewValue;
                Debug.WriteLine(target.SelectionLength);

            }));

    /// <summary>
    /// The bindable text property dependency property
    /// </summary>
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(MvvmTextEditor),
            new PropertyMetadata((o, args) =>
            {
                var target = (MvvmTextEditor)o;
                target.Text = (string)args.NewValue;
            }));

    /// <summary>
    /// Raises a property changed event
    /// </summary>
    /// <param name="property">The name of the property that updates</param>
    public void RaisePropertyChanged([CallerMemberName] string caller = "")
    {
        var handler = PropertyChanged;
        if (handler != null)
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

TextDependencyProperty 工作正常,但 SelectionLength 和 SelectionStart 不工作。

我已经为 SelectionChanged 添加了一个事件处理程序(但我不完全是我在这里使用 SetValue 所做的:

    public MvvmTextEditor()
    {
        TextArea.SelectionChanged += TextArea_SelectionChanged;
    }

    void TextArea_SelectionChanged(object sender, EventArgs e)
    {
        SetValue(SelectionStartProperty, SelectionStart);
        SetValue(SelectionLengthProperty, SelectionLength);
    }

现在可以进行选择,但是向后进行选择时会出现问题。在这种情况下,SelectionStart 始终为 0。如果到目前为止我所做的一切都是正确的,那么我将创建一个逻辑,如果有人向后选择,它将正确转换索引和长度。我必须在 PropertyMetaDataDelegate 中实现这个逻辑吗?

【问题讨论】:

  • 您在这方面取得了进展吗?
  • 不,对不起。但我不希望将 AvalonEdit 与 MVVM 一起使用。如果你这样做,你会遇到太多问题。
  • 没错,但我真的别无选择。您没有调查为什么无法反向选择的原因?

标签: c# mvvm dependency-properties avalonedit


【解决方案1】:

是的,一旦我查看了它,原因就很清楚了。在支持 SelectionStartSelectionEnd 的 DP 的属性的设置器中,我们设置了 base.SelectionStartbase.SelectionLength 这是创建这些值的循环更新并阻止从基类正确更新这些值.更新的 MVVM TextEditor 类如下。

/// <summary>
/// Class that inherits from the AvalonEdit TextEditor control to 
/// enable MVVM interaction. 
/// </summary>
public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
{
    /// <summary>
    /// Default constructor to set up event handlers.
    /// </summary>
    public MvvmTextEditor()
    {
        TextArea.SelectionChanged += TextArea_SelectionChanged;
    }

    /// <summary>
    /// Event handler to update properties based upon the selection changed event.
    /// </summary>
    void TextArea_SelectionChanged(object sender, EventArgs e)
    {
        this.SelectionStart = SelectionStart;
        this.SelectionLength = SelectionLength;
    }

    #region Text.
    /// <summary>
    /// Dependancy property for the editor text property binding.
    /// </summary>
    public static readonly DependencyProperty TextProperty =
         DependencyProperty.Register("Text", typeof(string), typeof(MvvmTextEditor),
         new PropertyMetadata((obj, args) =>
         {
             MvvmTextEditor target = (MvvmTextEditor)obj;
             target.Text = (string)args.NewValue;
         }));

    /// <summary>
    /// Provide access to the Text.
    /// </summary>
    public new string Text
    {
        get { return base.Text; }
        set { base.Text = value; }
    }

    /// <summary>
    /// Return the current text length.
    /// </summary>
    public int Length
    {
        get { return base.Text.Length; }
    }

    /// <summary>
    /// Override of OnTextChanged event.
    /// </summary>
    protected override void OnTextChanged(EventArgs e)
    {
        RaisePropertyChanged("Length");
        base.OnTextChanged(e);
    }
    #endregion // Text.

    #region Caret Offset.
    /// <summary>
    /// DependencyProperty for the TextEditorCaretOffset binding. 
    /// </summary>
    public static DependencyProperty CaretOffsetProperty =
        DependencyProperty.Register("CaretOffset", typeof(int), typeof(MvvmTextEditor),
        new PropertyMetadata((obj, args) =>
        {
            MvvmTextEditor target = (MvvmTextEditor)obj;
            target.CaretOffset = (int)args.NewValue;
        }));

    /// <summary>
    /// Provide access to the CaretOffset.
    /// </summary>
    public new int CaretOffset
    {
        get { return base.CaretOffset; }
        set { base.CaretOffset = value; }
    }
    #endregion // Caret Offset.

    #region Selection.
    /// <summary>
    /// DependencyProperty for the TextEditor SelectionLength property. 
    /// </summary>
    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;
             }));

    /// <summary>
    /// Access to the SelectionLength property.
    /// </summary>
    public new int SelectionLength
    {
        get { return base.SelectionLength; }
        set { SetValue(SelectionLengthProperty, value); }
    }

    /// <summary>
    /// DependencyProperty for the TextEditor SelectionStart property. 
    /// </summary>
    public static readonly DependencyProperty SelectionStartProperty =
         DependencyProperty.Register("SelectionStart", typeof(int), typeof(MvvmTextEditor),
         new PropertyMetadata((obj, args) =>
             {
                 MvvmTextEditor target = (MvvmTextEditor)obj;
                 target.SelectionStart = (int)args.NewValue;
             }));

    /// <summary>
    /// Access to the SelectionStart property.
    /// </summary>
    public new int SelectionStart
    {
        get { return base.SelectionStart; }
        set { SetValue(SelectionStartProperty, value); }
    }
    #endregion // Selection.

    /// <summary>
    /// Implement the INotifyPropertyChanged event handler.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged([CallerMemberName] string caller = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
    }
}

我希望这对其他人有帮助。

【讨论】: