Обеспечение совместимости AvalonEdit MVVM

Я пытаюсь сделать Avalon MVVM совместимым в своем приложении WPF. Погуглив, я узнал, что AvalonEdit не поддерживает MVVM и мне нужно экспортировать состояние AvalonEdit, создав класс, производный от TextEditor, а затем добавив необходимые свойства зависимостей. Боюсь, я совсем запутался в ответе герра Грюнвальда здесь:

Если вам действительно нужно экспортировать состояние редактора с помощью MVVM, то я предлагаю вам создать класс, производный от TextEditor, который добавляет необходимые свойства зависимостей и синхронизирует их с фактическими свойствами в AvalonEdit.

У кого-нибудь есть пример или есть хорошие предложения о том, как этого добиться?


person l46kok    schedule 10.09.2012    source источник


Ответы (3)


Г-н Грюнвальд говорит об обертывании свойств 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 к тому же свойству View Model 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));
        }
    }
}
person McGarnagle    schedule 13.09.2012
comment
два вопроса, один RaisePropertyChanged — это метод AvalonTestModel, но мне нужно вызвать этот метод из моего производного текстового редактора. Сделать метод статическим? Кроме того, ваш XAML не компилируется. Slider не имеет свойств MinValue и MaxValue, и привязка вообще не работает. Как я могу это исправить? - person l46kok; 20.09.2012
comment
@ l46kok 1. Вы можете реализовать INotifyPropertyChanged в производном текстовом редакторе таким же образом (не статично, просто обычное событие). 2. Свойства Slider должны были быть Minimum и Maximum. Код в моем ответе должен скомпилироваться сейчас... - person McGarnagle; 20.09.2012
comment
Если я хочу экспортировать такие свойства, как TextArea и TextView, что нужно сделать? Мне трудно установить TextArea в качестве свойства зависимости, поскольку это свойство доступно только для чтения в TextEditor. - person l46kok; 20.09.2012
comment
@ l46kok Когда вам нужно будет установить TextArea? Если внутри TextArea есть свойства, к которым вам нужно привязаться, вы можете настроить свойства зависимостей для них таким же образом, используя base.TextArea.SomeProperty. - person McGarnagle; 20.09.2012
comment
Мне это нужно для установки/удаления нового менеджера фолдинга для сворачивания кода FoldingManager.Install(TextArea); - person l46kok; 21.09.2012
comment
Я использовал вашу идею в своем приложении, но у меня возникла проблема с двусторонней привязкой. Буду признателен, если вы взглянете на мой вопрос. - person Naser Asadi; 13.02.2013
comment
Я реализовал ваше предложение в своем приложении MVVM WPF, однако, когда я пытаюсь прочитать из моей ViewModel смещение, оно всегда равно 0. Когда я устанавливаю Offset = 2000, TextEditor Caret перемещается на 2000. Я не уверен, почему я только возможность установить позицию из ViewModel и не читать позицию из ViewModel. Я пробовал Mode="TwoWay", а также UpdateSourceTrigger="PropertyChanged" без каких-либо изменений в функциональности. - person Matthew; 22.01.2014
comment
Кажется очень наивным писать элемент управления WPF, который не поддерживает MVVM. Возможно, было бы лучше, если бы все сказали об этом автору и призвали его писать более качественные компоненты? - person user3690202; 10.07.2015

Вы можете использовать свойство 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);
        }
    }
}

источник: блог nawrem.reverse

person Frederic    schedule 12.09.2012
comment
Я ценю усилия по ответу, но, пожалуйста, прочитайте описание, которое я написал в щедрости. Я знаю, что вы можете привязать свойство Document, но мне нужно все состояние AvalonEdit (в противном случае мне пришлось бы привязывать множество вещей, начиная с textview, textarea, document, visuallines и т. д.) - person l46kok; 13.09.2012

Не уверен, что это соответствует вашим потребностям, но я нашел способ получить доступ ко всем «важным» компонентам TextEditor в ViewModel, отображая его в представлении, но однако исследуя возможности.

Вместо создания экземпляра TextEditor в представлении, а затем привязки многих свойств, которые мне понадобятся, я создал элемент управления содержимым и привязал его содержимое к экземпляру TextEditor. который я создаю в ViewModel.

Просмотр:

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

Модель представления:

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.

person Luis Ferreira    schedule 05.01.2017