Two Way Binding to AvalonEdit Document Text using MVVM
avalonedit, binding, c#, mvvm, wpf
Solution
Create a Behavior class that will attach the TextChanged event and will hook up the dependency property that is bound to the ViewModel.
AvalonTextBehavior.cs
public sealed class AvalonEditBehaviour : Behavior<TextEditor>
{
public static readonly DependencyProperty GiveMeTheTextProperty =
DependencyProperty.Register("GiveMeTheText", typeof(string), typeof(AvalonEditBehaviour),
new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, PropertyChangedCallback));
public string GiveMeTheText
{
get { return (string)GetValue(GiveMeTheTextProperty); }
set { SetValue(GiveMeTheTextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
if (AssociatedObject != null)
AssociatedObject.TextChanged += AssociatedObjectOnTextChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
if (AssociatedObject != null)
AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged;
}
private void AssociatedObjectOnTextChanged(object sender, EventArgs eventArgs)
{
var textEditor = sender as TextEditor;
if (textEditor != null)
{
if (textEditor.Document != null)
GiveMeTheText = textEditor.Document.Text;
}
}
private static void PropertyChangedCallback(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var behavior = dependencyObject as AvalonEditBehaviour;
if (behavior.AssociatedObject!= null)
{
var editor = behavior.AssociatedObject as TextEditor;
if (editor.Document != null)
{
var caretOffset = editor.CaretOffset;
editor.Document.Text = dependencyPropertyChangedEventArgs.NewValue.ToString();
editor.CaretOffset = caretOffset;
}
}
}
}
View.xaml
<avalonedit:TextEditor
WordWrap="True"
ShowLineNumbers="True"
LineNumbersForeground="Magenta"
x:Name="textEditor"
FontFamily="Consolas"
SyntaxHighlighting="XML"
FontSize="10pt">
<i:Interaction.Behaviors>
<controls:AvalonEditBehaviour GiveMeTheText="{Binding Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors>
</avalonedit:TextEditor>
`i` must be defined as
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
ViewModel.cs
private string _test;
public string Test
{
get { return _test; }
set { _test = value; }
}
That should give you the Text and push it back to the ViewModel.
Problem
I want to include an AvalonEdit `TextEditor` control into my MVVM application. The first thing I require is to be able to bind to the `TextEditor.Text` property so that I can display text. To do this I have followed and example that was given in Making AvalonEdit MVVM compatible. Now, I have implemented the following class using the accepted answer as a template ``` public sealed class MvvmTextEditor : TextEditor, INotifyPropertyChanged { 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; }) ); public new string Text { get { return base.Text; } set { base.Text = value; } } protected override void OnTextChanged(EventArgs e) { RaisePropertyChanged("Text"); base.OnTextChanged(e); } public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string info) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info)); } } ``` Where the XAML is ``` <Controls:MvvmTextEditor HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontFamily="Consolas" FontSize="9pt" Margin="2,2" Text="{Binding Text, NotifyOnSourceUpdated=True, Mode=TwoWay}"/> ``` Firstly, this does not work. The Binding is not shown in Snoop at all (not red, not anything, in fact I cannot even see the `Text` dependency property). I have seen this question which is exactly the same as mine Two-way binding in AvalonEdit doesn't work but the accepted answer does not work (at least for me). So my question is: How can I perform two way binding using the above method and what is the correct implementation of my `MvvmTextEditor` class? Thanks for your time. Note: I have my `Text` property in my ViewModel and it implements the required `INotifyPropertyChanged` interface.