I am currently working on a project where users will be entering C# scripts that get dynamically compiled and executed. I will skip giving more details for now as I will probably have questions about other aspects of the project later.
Due to some higher level decisions that are out of my control, the user interface will be developed using WPF and the Quantum Whale Editor.NET control will be used as the editor. Sadly, it seems that the WPF version of the QWhale Editor.NET is still not fully mature and so it is lacking documentations and, worst, it does not seem to be binding friendly.
While I am still new to WPF, I am somewhat familiar with MVVM and so I would love to apply that. However, I was faced with the first challenge when I was testing the evaluation version and tried binding the editor's text to a property of a model and received an exception that it is not possible:
A 'Binding' cannot be set on the 'Text' property of type 'TextEditor'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
Having tried AvalonEdit while checking the alternatives, I remembered this other Stack Overflow question: Making AvalonEdit MVVM Compatible. And so I followed the same concept.
I have defined a class that inherits from the editor, added a dependency property and at first tried combining that with hiding the original Text
property of the editor using new
. But obviously that was a long shot and my property was not used, the base property was being called directly.
Once that failed, I defined a brand new property called DocumentText
. I had it wrap base.Text
, defined the binding using it and accordingly got one direction of binding working. That being from the model to the control. But from what I found, the best way to get binding working in the other direction is to override
the OnTextChanged
event (or an equivalent) to have it raise the property changed notification. The problem is that the control does not have such an event, as weird as that sounds.
Now I could probably override a bunch of other events (e.g. OnKeyUp, OnMouseClick, etc.) so that I could handle all possible actions that could modify the text (typing, drag & dropping, pasting, etc.) but that does not seem too practical and might not be repeatable to other properties that I might be interested in binding later on. And after a couple of days of searching online when I find the time, I am still no closer to finding other ideas. So, are there any proper solutions to my issue short of diving into the code of the control itself? (Supposedly, the license will give me access to the source code, but I would rather avoid direct modifications to that).
I have avoided specifying the QWhale editor in the question title and tags as I have felt that what I am looking for does not depend on this specific control. Please correct me if I am wrong.
I cannot provide my testing code at the moment due to being on a different computer, but if you think it is necessary just leave me a note and I will add it.
Update: Here is the code, because I am not sure that I managed to clearly describe my issue.
class ExtenEdit : TextEditor
{
public static DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ExtenEdit),
new PropertyMetadata((obj, args) =>
{
TextEditor target = (TextEditor)obj;
target.DocumentText = (string)args.NewValue;
})
);
public new string Text
{
get { return base.Text; }
set
{
if (base.Text != value)
{
base.Text = value;
}
}
}
}
That works when I modify the value in the ViewModel
, but when typing in the editor my property gets bypassed and the base property is called directly. It works if I add something like this:
protected override void OnKeyUp(System.Windows.Input.KeyEventArgs e)
{
SetCurrentValue(TextProperty, base.Text);
base.OnKeyUp(e);
}
But as I said above, I can not consider that as a valid "clean" solution.
And here are the related parts in my XAML (you can notice that I am also playing with AvalonDock):
Namespaces:
<Window x:Class="AvalonDockQWhale.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:avalonDock="http://avalondock.codeplex.com"
xmlns:converter="clr-namespace:AvalonDockQWhale.Converter"
xmlns:pane="clr-namespace:AvalonDockQWhale.View.Pane"
xmlns:editor="clr-namespace:QWhale.Editor.Wpf;assembly=QWhale.Editor.Wpf"
xmlns:control="clr-namespace:AvalonDockQWhale.Control"
xmlns:controlHelper="clr-namespace:AvalonDockQWhale.ControlHelper"
x:Name="mainWindow"
Title="MainWindow" Height="600" Width="800">
And the binding for my original attempt:
<control:ExtenEdit Text="{Binding Path=ScriptText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
The bindings that I have tried for DHN's solution:
<editor:TextEditor controlHelper:AttachedProperties.Text="{Binding ScriptText}" />
and
<editor:TextEditor controlHelper:AttachedProperties.Text="{Binding Path=ScriptText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Final details: I have accepted the answer given by DHN, even though it does not work in my specific case, as it seems to be a decent solution for similar issues.