Обеспечение совместимости AvalonEdit MVVM
Я пытаюсь сделать Avalon MVVM совместимым в своем приложении WPF. В результате поиска в Google я обнаружил, что AvalonEdit не является дружественным к MVVM, и мне нужно экспортировать состояние AvalonEdit, создав класс, производный от TextEditor, а затем добавив необходимые свойства зависимостей. Я боюсь, что совершенно заблудился в ответе господина Грюнвальда:
Если вам действительно необходимо экспортировать состояние редактора с помощью MVVM, я предлагаю вам создать класс, производный от TextEditor, который добавляет необходимые свойства зависимостей и синхронизирует их с фактическими свойствами в AvalonEdit.
У кого-нибудь есть пример или есть хорошие предложения о том, как этого добиться?
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));
}
}
}
Вы можете использовать свойство 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
Не уверен, что это соответствует вашим потребностям, но я нашел способ получить доступ ко всем "важным" компонентам TextEditor в ViewModel, пока он отображается в View, но все еще исследует возможности.
Вместо того чтобы создать экземпляр TextEditor в представлении, а затем связать многие свойства, которые мне понадобятся, я создал элемент управления содержимым и связал его содержимое с экземпляром TextEditor, который я создал в ViewModel.
Посмотреть:
<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.