How to handle a custom RoutedEvent fired from a UserControl from inside the ViewModel? - wpf

Currently I have a usercontrol that fires a registered RoutedEvent like this:
protected virtual void OnScrollEvent(object oldValue, object newValue)
{
AssociatedObject.RaiseEvent(new DateTimeEventArgs(OnVisualChartRangeChangedEvent, minDate, maxDate));
}
The mainwindow container currently handles this event by calling a method from the view like this.
<Grid>
<historicChart:HistoricChartControl behaviours:ChartBehavior.OnVisualChartRangeChanged="RoutedEventHandler"/>
</Grid>
and the code behind...
private void RoutedEventHandler(object sender, DateTimeEventArgs dateTimeEventArgs)
{
//do stuff here...
}
But what I would like is for this to conform to the MVVM model, so that my handler should be implemented the viewmodel and not in the view.
How can I do this? Could someone post me a brief example of how I could go about this?
Thanks in advnce

You can use the CallMethodAction to invoke your ViewModel handler.
See this on how to create an EventTrigger for your custom RoutedEvent :
Custom RoutedEvent as EventTrigger

Related

Bind static method from another class as handler for WPF control event

I have a window, say MainWindow with some NumericUpDowns. I have another window MyCalculatorWindow like this:
public class MyCalculatorWindow : Window {
public static void LongUpDown_TouchUp(object sender, TouchEventArgs e)
{
// Show calculator and set numeric value when OK button is pressed.
}
// The rest of the MyCalculator functionality...
}
Is there a way to bind that static method to the TouchUp event handler property of LongUpDowns in xaml (ideally to all of them at once)? Something like this:
<xctk:LongUpDown TouchUp="{Binding Something??? MyCalculator.LongUpDown_TouchUp}" />
Is there a way to bind that static method to the TouchUp event handler property of LongUpDowns in xaml (ideally to all of them at once)? Something like this:
No, there isn't. The XAML compiler can only find event handlers in the same class as the element itself.
You could define a TouchUp event handler in the code-behind file for the view where the <xctk:LongUpDown /> element is and call the static method from there. It's a one-liner:
private void Window65_TouchUp(object sender, TouchEventArgs e) => MyCalculatorWindow.LongUpDown_TouchUp(sender, e);

WPF. Change DataContext on event binding to access code-behind on a MVVM project

i'm developing a WPF application with MVVM.
At the XAML code i have a Grid with its DataContext pointing to a ViewModel, and i need to know if it is possible to change the DataContext at runtime to access an event at its code-behind.
Code-behind for the view:
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = new MainViewModel();
InitializeComponent();
}
private void ValidationEvent(object sender, ValidationErrorEventArgs e)
{
//Something useful
}
}
Here is the code that i tried in XAML:
<Grid Validation.Error={Binding Path=ValidationEvent RelativeSource={RelativeSource Self}}/>
The XAML code throws an XamlParseException telling that it is not possible to do the Binding on an "AddErrorHandler", that it is only possible for a DependencyProperty on a DependencyObject.
I don't want to change the DataContext of the Grid because inside it there are elements that access the MainViewModel properties, so i just want to change the DataContext for the Validation.Error event binding... If it is possible...
Thanks.
Validation.Error is an event, not a property. You can't set Bindings to events.
You can use things like MVVM Light's EventToCommand, or Microsoft's own Interactivity EventTrigger to associate Commands to Events.
But there really isn't anything wrong with just adding a regular event handler in code-behind and calling some viewmodel code from there... Contrary to what many people seem to think, MVVM doesn't forbid the use of code-behind and what you'd be doing is not very different from what an EventToCommand or an EventTrigger are doing under the hood.
First of all, just set the event handler name for the Validation.Error event.
<Grid Validation.Error="ValidationEvent" />
And then in your code-behind do whatever you want.
private void ValidationEvent(object sender, ValidationErrorEventArgs e)
{
// Something useful
// Some call to VM code
(this.DataContext as MainViewModel).SomeMethod();
}
This works independently of your DataContext (as long as you cast this.DataContext to the correct type, of course).
Event handlers don't depend on your DataContext, only Bindings do.

How does the WPF event system know about the event route?

I am trying to understand how RoutedEvents work.
Well - I walked through some tutorials and understood why RoutedEvents are useful and how they work.
But there is one thing, that I don't get:
Let's say I wrote a class (e.g. "MyClass") , which has a RoutedEvent property, sth. like this:
public class MyClass
{
public static readonly RoutedEvent myEvent;
...
}
Well - just giving a property is not enough - so I have to register the RoutedEvent with the help of EventManager:
...
myEvent = EventManager.RegisterRoutedEvent("MyEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyClass));
...
Okay - now the WPF event system knows about THIS event.
If I do it that way, each class I write will have it's own RoutedEvent. But that makes no sense to me.
What I want, is that other classes listen to the same event - without being a type of MyClass.
For example:
I have a stackpanel and within the stackpanel is a button. Clicking the stackpanel will raise the onClick event. Clicking the button will raise the onClick event of the button - and then the onClick event on the stackpanel.
But how?
Sorry - it's hard for me to describe the problem - I am just too confused :)
Thx a lot.
CodeCannibal
What I want, is that other classes listen to the same event - without being a type of MyClass.
You expect the right from this and this is what it delivers. I mean by registering a RoutedEvent you are not strongly binding it to the type; instead you are bridging it using the string "MyEvent" EventManager.RegisterRoutedEvent("MyEvent", ...
RoutedEvent traverse through the logical tree and stops traversing when handled (exceptions are there).
So, StackPanel need not to be derived from MyClass. You just need to register the RoutedEvent at StackPanel by specifying the action/handler. Whenever the RoutedEvent traverse through StackPanel it will call the corresponding action.
For example:
UserControl1.cs
//Routed Event
public static readonly RoutedEvent ThisIsEvent = EventManager.RegisterRoutedEvent("ThisIs", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl1));
// .NET wrapper
public event RoutedEventHandler ThisIs
{
add { AddHandler(ThisIsEvent, value); }
remove { RemoveHandler(ThisIsEvent, value); }
}
//local handler where RaiseEvent is called
private void button1_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ThisIsEvent));
}
And below is how you subscribe to that event in you XAML. You can also do this in your code file...
<StackPanel Orientation="Vertical" **local:UserControl1.ThisIs="StackPanel_ThisIs"** >
<local:UserControl1></local:UserControl1>
</StackPanel>
I hope this clear your doubts.

How to bind an item command in user conrol to viewmodel command?

I have an UserControl. In my UserControl i have a button that I want bind its command to my ViewModel command. Can I do this?
Yes, you could add a routed event to your user control which gets invoked when the button is pressed.
You can then use various techniques to invoke the view model verb when the user control event fires.
E.g. you could use an attached property, or I would recommend using an MVVM framework such as Caliburn.Micro which has Actions that makes it even more straightforward.
I found it...I can define a DependensyProperty typof RelayCommand in my usercontrol and bind my DependensyProperty to my ViewModel Command
I'm not really sure what you mean but I take a shot.
In your code behind, define a RoutedCommand:
public partial class MyUserControl : UserControl
{
public static RoutedCommand Click =
new RoutedCommand("Click", typeof(UserControl));
}
Then it the xaml, set up a command binding:
<UserControl.CommandBindings>
<CommandBinding
Command="{x:Static MyNameSpace:MyUserControl.Click}"
CanExecute="ClickCanExecute"
Executed="ClickExecuted"/>
</UserControl.CommandBindings>
Then add the handlers in the code behind:
private void ClickCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void ClickExecuted(object sender, ExecutedRoutedEventArgs e)
{
// TODO execution logic goes here
}
Was I close? :)

hang on an handler to an event handler in View

i want to add an event to my Event handler. The Problem is my EventHandler is in my ViewModel and my Event is in View. How can I access my ViewModel from View to hang on this event?
thanks for your help
Since the view model is set as DataContext of the view you can subscribe to the DataContextChanged of the view and in the handler cast the DataContext to the view model's type and attach the desired event handler. Something like this:
public class MyView : UserControl
{
public MyView()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
}
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
MyViewModel viewModel = DataContext as MyViewModel;
if (viewModel != null)
{
viewModel.MyEvent += OnMyEvent;
}
}
}
Usually in MVVM, when the View wants to defer some operation to the ViewModel it does so by binding to an ICommand. This is essentially the same usage model as with the event, as the View "pushes" the notification to the ViewModel.
Depending on the nature of the event I would suggest you look into doing it with an ICommand, since:
The whole idea behind MVVM is for the ViewModel to not assume that the View is "real" UI
Typically, however, the View is real UI (so the event you mention probably has to do with something happening in the UI)
Which leads me to think that you are searching for a way to have the ViewModel subscribe to a UI event from the View, something that would come contrary to the premise of MVVM.
Is invoking a command on the ViewModel problematic in your case?

Resources