EventToCommand works for some commands

May 4, 2012 at 3:06 PM
Edited May 4, 2012 at 3:08 PM

Hi,

 

I am using Extended WPF Toolkit (http://wpftoolkit.codeplex.com/wikipage?title=RichTextBox) and I need within MVVM framework to execute some command when a link in the RTB is clicked.

I've tried several ways to achieve this (e.g. http://stackoverflow.com/questions/10441725/flowdocument-richtextbox-with-hyperlinks-using-mvvm) without success.

Now I am trying to use EventToCommand trigger:

<tk:RichTextBox Text="{Binding Text}" IsDocumentEnabled="True" Width="300" 
                Margin="0" IsReadOnly="True" Hyperlink.Click="RichTextBox_Click" <--this works
HorizontalScrollBarVisibility="Auto"> <tk:RichTextBox.TextFormatter>      <l:MyXamlFormatter/>     </tk:RichTextBox.TextFormatter>     <i:Interaction.Triggers>
<!-- this does not fire -->
     <i:EventTrigger EventName="Hyperlink.Click">          <catel:EventToCommand Command="{Binding TestCommand}"/>         </i:EventTrigger>     </i:Interaction.Triggers> </tk:RichTextBox>

If I subscribe directly (with event handler in the code behind I do see the event fire.

In addition, I manage to subscribe to some other events (e.g. MouseEnter, GotMouseFocus) and not to others (MouseDown, MouseUp (these 2 don't fire at all).

Can I even do an event trigger on this Hyperlink?

Maybe there is other way to pass a Command from my VM to be executed when clicking an Hyperlink in the RichTextBox?

Thanks,

Tomer

May 4, 2012 at 3:44 PM

I quickly took a look at the RichTextBox, and it derives from RichTextBox (from MS itself). In the docs, I cannot find any documentation about Hyperlink.Click, so I think it is an attached event.

So, the issue is that the EventToCommand does not support the complex naming styles like [Control].[Event]. However, I see a situation where it would be good to have this trigger.

For now, it is best to handle the RichTextBox_Click, and then call the viewModel.MyClickCommand from your code-behind. It is possible to create a feature that allows handling attached events. If you want this, you can create a new issue here:

http://catel.codeplex.com/workitem/list/advanced

May 5, 2012 at 1:21 PM

Hi Geert,

As always, it is a pleasure to write in this discussion forum and have an answer very quickly afterwards.

The situation I have is a string property that is being handled by a Formatter. In the converter I am parsing the text and create the hyperlink with an internal command that raises an event (Catel's MessageMdeiator) that I subscribe to in the VM and thus, I don't have a direct dependency between the View and ViewModel - I think it is a bit closer to MVVM than directly handing this in Code-Behind.

I am still waiting for some answers of how to best handle the situation using an attach property or a dependency property that is set on a static resource.

(here is what I tried to do: http://stackoverflow.com/questions/10441725/flowdocument-richtextbox-with-hyperlinks-using-mvvm)

I will try to also open an issue - as it may be useful to others.

May 5, 2012 at 2:53 PM

Well, as you said there are 3 ways. I will state them in the order I think they match MVVM best (although all are good MVVM solutions):

1) EventToCommand handles attached events (will become available in the future)

2) Call the command from code-behind

3) Use the MessageMediator

 

The reason I put the message mediator on the 3rd place is because I try to avoid this as much as possible. When you introduce a message mediator, some stuff becomes "magic" and you have no idea where it is coming from. This way, you create an "unknown dependency" which the developer should be aware off. For example, there is no clear implementation that shows another developer how and when the message will be delivered by an "unknown dependency". Also, for unit testing you will need to send messages via the message mediator to test it, which is more work than simply calling a command on a view model.

I think handling an event in the code-behind, and passing it through to the VM is much more clear (because for the VM there is no difference, the command is called). Only in the view and code-behind, there is a change, but that is easy to find simply because the code is in the same file.

I know other frameworks such as MVVM light quickly grab to the messenger to solve such issues, but I think the message mediator should only be used for application-level communications, not as a replacement for when it becomes a bit harder to solve an issue.

May 6, 2012 at 2:01 PM

Hi Geert,

Good point - and working with this solution today show me what you mean.

As for point 2 - how should this be implemented - I know how to add a code behind but the view does not know the VM and thus, how can it access its command?

Should I add direct reference (dependency)? Should I cast the DataContext to the expected VM and then call it - this couple the V and VM... other option?

Thanks,

Tomer

May 14, 2012 at 7:59 PM

Sorry for my late reply. I am now going through all my mail of the last two weeks, must have missed this somehow. The best way is to cast a view to IUserControl or IDataWindow (in the next version, there will also be a IView that contains a view model which you can cast both to). Then you have a view model property which you can use. Casting the DataContext isn't always save because for example, the UserControl doesn't set the DataContext to the ViewModel (but has an internal data context with the view model).