【问题标题】:Making AvalonEdit MVVM compatible使 AvalonEdit MVVM 兼容
【发布时间】:2012-09-02 21:18:00
【问题描述】:

我正在尝试使 Avalon MVVM 在我的 WPF 应用程序中兼容。通过谷歌搜索,我发现AvalonEdit is not MVVM friendly 我需要通过创建一个派生自 TextEditor 的类然后添加必要的依赖属性来导出 AvalonEdit 的状态。恐怕我对 Herr Grunwald 的回答 here 很迷茫:

如果你真的需要使用 MVVM 导出编辑器的状态,那么我建议你创建一个派生自 TextEditor 的类,它添加必要的依赖属性并将它们与 AvalonEdit 中的实际属性同步。

有没有人有一个例子或有关于如何实现这一点的好建议?

【问题讨论】:

    标签: c# .net wpf mvvm avalonedit


    【解决方案1】:

    Herr Grunwald 正在讨论用dependency properties 包装TextEditor 属性,以便您可以绑定到它们。基本思路是这样的(例如使用 CaretOffset 属性):

    修改TextEditor类

    public class MvvmTextEditor : TextEditor, INotifyPropertyChanged
    {
        public static DependencyProperty CaretOffsetProperty = 
            DependencyProperty.Register("CaretOffset", typeof(int), typeof(MvvmTextEditor),
            // binding changed callback: set value of underlying property
            new PropertyMetadata((obj, args) =>
            {
                MvvmTextEditor target = (MvvmTextEditor)obj;
                target.CaretOffset = (int)args.NewValue;
            })
        );
    
        public new string Text
        {
            get { return base.Text; }
            set { base.Text = value; }
        }
    
        public new int CaretOffset
        {
            get { return base.CaretOffset; }
            set { base.CaretOffset = value; }
        }
    
        public int Length { get { return base.Text.Length; } }
    
        protected override void OnTextChanged(EventArgs e)
        {
            RaisePropertyChanged("Length");
            base.OnTextChanged(e);
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
    

    现在CaretOffset 已被包装在一个 DependencyProperty 中,您可以将它绑定到一个属性,例如您的视图模型中的Offset。为了说明,将Slider 控件的值绑定到相同的视图模型属性Offset,并看到当您移动滑块时,Avalon 编辑器的光标位置会更新:

    测试 XAML

    <Window x:Class="AvalonDemo.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit"
        xmlns:avalonExt="clr-namespace:WpfTest.AvalonExt"
        DataContext="{Binding RelativeSource={RelativeSource Self},Path=ViewModel}">
      <StackPanel>
        <avalonExt:MvvmTextEditor Text="Hello World" CaretOffset="{Binding Offset}" x:Name="editor" />
        <Slider Minimum="0" Maximum="{Binding ElementName=editor,Path=Length,Mode=OneWay}" 
            Value="{Binding Offset}" />
        <TextBlock Text="{Binding Path=Offset,StringFormat='Caret Position is {0}'}" />
        <TextBlock Text="{Binding Path=Length,ElementName=editor,StringFormat='Length is {0}'}" />
      </StackPanel>
    </Window>
    

    测试代码隐藏

    namespace AvalonDemo
    {
        public partial class TestWindow : Window
        {
            public AvalonTestModel ViewModel { get; set; }
    
            public TestWindow()
            {
                ViewModel = new AvalonTestModel();
                InitializeComponent();
            }
        }
    }
    

    测试视图模型

    public class AvalonTestModel : INotifyPropertyChanged
    {
        private int _offset;
    
        public int Offset 
        { 
            get { return _offset; } 
            set 
            { 
                _offset = value; 
                RaisePropertyChanged("Offset"); 
            } 
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
    }
    

    【讨论】:

    • 两个问题,一个 RaisePropertyChanged 是 AvalonTestModel 的方法,但我需要从派生的文本编辑器中调用该方法。我是否将方法设为静态?此外,您的 XAML 不会编译。 Slider 没有 MinValue 和 MaxValue 属性,并且绑定根本不起作用。我该如何解决这个问题?
    • @l46kok 1. 您可以在派生文本编辑器上以相同的方式实现INotifyPropertyChanged(不是静态的,只是一个常规事件)。 2. Slider 属性应该是MinimumMaximum。我的答案中的代码现在应该可以编译了...
    • 如果要导出TextArea、TextView等属性,需要做什么?我很难将 TextArea 设置为依赖属性,因为它是 TextEditor 中的只读属性
    • @l46kok 你什么时候需要设置TextArea?如果TextArea 中有需要绑定的属性,则可以使用base.TextArea.SomeProperty 以同样的方式为它们设置依赖属性。
    • 编写一个不支持 MVVM 的 WPF 控件似乎很幼稚。也许更好的方法是让每个人都告诉作者,并鼓励他编写更好的组件?
    【解决方案2】:

    您可以使用编辑器中的 Document 属性并将其绑定到 ViewModel 的属性。

    这是视图的代码:

    <Window x:Class="AvalonEditIntegration.UI.View"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:AvalonEdit="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit"
            Title="Window1"
            WindowStartupLocation="CenterScreen"
            Width="500"
            Height="500">
        <DockPanel>
            <Button Content="Show code"
                    Command="{Binding ShowCode}"
                    Height="50"
                    DockPanel.Dock="Bottom" />
            <AvalonEdit:TextEditor ShowLineNumbers="True"
                                   Document="{Binding Path=Document}"
                                   FontFamily="Consolas"
                                   FontSize="10pt" />
        </DockPanel>
    </Window>
    

    以及 ViewModel 的代码:

    namespace AvalonEditIntegration.UI
    {
        using System.Windows;
        using System.Windows.Input;
        using ICSharpCode.AvalonEdit.Document;
    
        public class ViewModel
        {
            public ViewModel()
            {
                ShowCode = new DelegatingCommand(Show);
                Document = new TextDocument();
            }
    
            public ICommand ShowCode { get; private set; }
            public TextDocument Document { get; set; }
    
            private void Show()
            {
                MessageBox.Show(Document.Text);
            }
        }
    }
    

    来源:blog nawrem.reverse

    【讨论】:

    • 感谢您的回答,但请阅读我在赏金中写的描述。我知道您可以绑定 Document 属性,但我需要 AvalonEdit 的整个状态(否则,我必须从 textview、textarea、document、visuallines 等开始绑定很多东西)
    【解决方案3】:

    不确定这是否符合您的需求,但我找到了一种方法来访问 ViewModel 上的 TextEditor 的所有“重要”组件,同时将其显示在查看,但仍在探索可能性。

    我所做的不是在视图上实例化 TextEditor,然后绑定我需要的许多属性,而是创建了一个内容控件并将其内容绑定到 TextEditor我在 ViewModel 中创建的 em> 实例。

    查看:

    <ContentControl Content="{Binding AvalonEditor}" />
    

    ViewModel:

    using ICSharpCode.AvalonEdit;
    using ICSharpCode.AvalonEdit.Document;
    using ICSharpCode.AvalonEdit.Highlighting;
    // ...
    private TextEditor m_AvalonEditor = new TextEditor();
    public TextEditor AvalonEditor => m_AvalonEditor;
    

    在 ViewModel 中测试代码(有效!)

    // tests with the main component
    m_AvalonEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("XML");
    m_AvalonEditor.ShowLineNumbers = true;
    m_AvalonEditor.Load(@"C:\testfile.xml");
    
    // test with Options
    m_AvalonEditor.Options.HighlightCurrentLine = true;
    
    // test with Text Area
    m_AvalonEditor.TextArea.Opacity = 0.5;
    
    // test with Document
    m_AvalonEditor.Document.Text += "bla";
    

    目前我仍在决定我需要我的应用程序来配置/使用 textEditor 做什么,但从这些测试来看,我似乎可以在保持 MVVM 方法的同时更改它的任何属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-28
      • 2021-08-21
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 2014-11-14
      • 1970-01-01
      相关资源
      最近更新 更多