c# - Haciendo compatible AvalonEdit MVVM
.net wpf (3)
No estoy seguro de si esto se ajusta a sus necesidades, pero encontré una manera de acceder a todos los componentes "importantes" del TextEditor en un ViewModel mientras se muestra en una vista, aunque aún exploro las posibilidades.
Lo que hice fue en lugar de crear una instancia de TextEditor en la Vista y luego vincular las muchas propiedades que necesitaré, creé un Control de contenido y vinculé su contenido a una instancia de TextEditor que creo en ViewModel .
Ver:
<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;
Código de prueba en el ViewModel (funciona!)
// 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";
En este momento todavía estoy decidiendo exactamente qué necesito para que mi aplicación configure / haga con el editor de texto, pero a partir de estas pruebas parece que puedo cambiar cualquier propiedad mientras mantengo un enfoque de MVVM.
Estoy tratando de hacer que Avalon MVVM sea compatible con mi aplicación WPF. De Google, descubrí que AvalonEdit no es compatible con MVVM y necesito exportar el estado de AvalonEdit haciendo una clase derivada de TextEditor y luego agregando las propiedades de dependencia necesarias. Me temo que estoy bastante perdido en la respuesta de Herr Grunwald aquí :
Si realmente necesita exportar el estado del editor utilizando MVVM, le sugiero que cree una clase derivada de TextEditor que agregue las propiedades de dependencia necesarias y las sincronice con las propiedades reales en AvalonEdit.
¿Alguien tiene un ejemplo o tiene buenas sugerencias sobre cómo lograr esto?
Puede usar la propiedad de documento desde el editor y vincularla a una propiedad de su ViewModel.
Aquí está el código para la vista:
<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>
Y el código para el 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);
}
}
}
fuente: blog nawrem.reverse
Herr Grunwald está hablando de envolver las propiedades de TextEditor
con propiedades de dependencia , para que pueda enlazarlas. La idea básica es así (usando la propiedad CaretOffset , por ejemplo):
Clase modificada 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));
}
}
}
Ahora que CaretOffset
ha CaretOffset
en una propiedad de dependencia, puede vincularlo a una propiedad, por ejemplo, Offset
en su modelo de vista. Para ilustrar, vincule el valor de un control Slider
a la misma propiedad View Model Offset
, y vea que cuando mueve el control deslizante, la posición del cursor del editor de Avalon se actualiza:
Prueba de 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>
Código de prueba por detrás
namespace AvalonDemo
{
public partial class TestWindow : Window
{
public AvalonTestModel ViewModel { get; set; }
public TestWindow()
{
ViewModel = new AvalonTestModel();
InitializeComponent();
}
}
}
Modelo de vista de prueba
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));
}
}
}