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.
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>
where i
is 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.