Routed event from user control in MVVM application - wpf

I am stuck on integrating a user control into a mvvm application.
The user control is a custom calendar and was not wrtitten using MVVM principles (I don't want to re-write it in MVVM).
I have a mouse down event that is fired in the user control (Which is combination of three different user controls).
The event fired code looks like this:
public static readonly RoutedEvent DateEvent =
EventManager.RegisterRoutedEvent("dateEvent", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(UserControl2));
public event RoutedEventHandler dateEvent
{
add{AddHandler(DateEvent, value);}
remove{ RemoveHandler(DateEvent, value);}
}
private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
{
RaiseEvent(new RoutedEventArgs(UserControl2.DateEvent, this));
}
How do I subscribe to this routed event in my main app viewModel? I know it is not very MVVM but as I said I cant be doing with re-writing my user control.
I know that this event will bubble up the tree until it is marked as handled. I know to add a public void method to deal with the event - I'm just not sure how to implement the interception of the event in the first place.

I think you should read the RelayCommand section of this.
WPF MVVM Apps

Related

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

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

How to subscribe to an event in a child usercontrol in Silverlight?

I've got a usercontrol (MyUC) that is programatically added to a page (MainPage) several times.
In MyUC I set the DataContext to a view model like this:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataContext = new MyUCViewModel();
}
In my view model (MyUCViewModel) I have a collection of data items:
public MyDataItemCollection MyDataItems { get; private set; }
And in the constructor I have:
public MyUCViewModel()
{
this.MyDataItems = new MyDataItemCollection();
this.MyDataItems.ChosenItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ChosenItemsChanged);
this.MyDataItems.Add(new DataItem());
}
From the above MyDataItems has another collection in it for ChosenItems and I added a NotifyCollectionChangedEventHandler to it.
Other parts of my code add and remove from the ChosenItems collection and that part seems to work ok.
Next I have the event handler method in the view model:
private void ChosenItemsChanged(object sender, EventArgs e)
{
MessageBox.Show("Chosen Items Changed");
}
This also works and I get a messagebox everytime the user makes a change in the UI that affects the ChosenItems collection.
The part I'm trying to figure out now is how do I set it up so that my MainPage does something when the ChosenItemsChanged event fires in my user controls. What I want to do is have the MainPage loop through the generated MyUC controls on the page and make each usercontrol call a method.
You can add more event listeners in the MainPage like this:
MyUCViewModel viewModel = myUC.DataContext;
viewModel.MyDataItems.ChosenItems.CollectionChanged
+= new NotifyCollectionChangedEventHandler(MainPage_ChosenItemsChanged);
This is based on the comment as the question was a little misleading:
While not strictly MVVM, as your question appears to be, your should write your User Controls as if it was a third-party control and simply expose a custom event on it. User Controls should always be a black-box with a public interface. For a reusable control that is self-contained (as many are) MVVM is overkill.
e.g.
in your User Control add:
public event EventHandler<MyEventArgs> MyEvent
Create a MyEventArgs class deriving from EventArgs and get it to hold useful parameters (like the selected item).
In your main page add a handler to MyEvent on each User Control you dynamically add.
I actually think the MVVM model is flawed and all this sort of controlling logic and event handlers belong in a Controller class (MVCVM!), but that's another story. :)

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.

What is the best way to handel click-events in MVVM?

What is the best way to handel click-events in MVVM? Are there a best way?
I have found two solutions:
with a relaycommand:
RelayCommand buttonAddCategory_Click;
public ICommand ButtonAddCategory_Click
{
get
{
return buttonAddCategory_Click ?? (buttonAddCategory_Click = new RelayCommand(param => this.AddCategory(),
param => true));
}
}
pro: ?; contra: need workaround with events if i would change ui elements like focus
with attached behaviour:
public static bool GetIsResetMouseLeftButtonDown(TreeView treeView)
{
return (bool)treeView.GetValue(IsResetMouseLeftButtonDownProperty);
}
public static void SetIsResetMouseLeftButtonDown(TreeView treeViewItem, bool value)
{
treeViewItem.SetValue(IsResetMouseLeftButtonDownProperty, value);
}
public static readonly DependencyProperty IsResetMouseLeftButtonDownProperty =
DependencyProperty.RegisterAttached("PreviewMouseLeftButtonDown", typeof(bool), typeof(TreeViewBehavior),
new UIPropertyMetadata(false, OnIsMouseLeftButtonDownChanged));
pro: you have RoutedEventArgs for changes on the ui; contra: access to other controls?
Right now i use both solutions. The RellayCommand in Buttons (with events for ui updates) and the attached behaviour for a treeview to deselect the treeviewitem if a user clicks.
To me there is no simple answer to this question.
That's the way I see it:
if you have a defined state-change on the VM, expose a RelayCommand which then can be bound to something the triggers it. In 99,9% percent of the cases this is a button/menu-entry. Something where it can be easily used. The cases that are left -> well some workaround might be needed, like calling a method from the view.
So a RelayCommand should imho be used if you are really targeting the VM.
Focus-changes on the other hand are view-related functionality. Imho this has nothing todo with the WM. That means for me it should be implemented in the view. So to me I'd even go for a straight-forward eventhandler that does the job.
hth,
Martin
I like this idea:
UI logic, such as opening new windows, showing/hiding elements, etc. You keep that on the code-behind.
When this 'click' should do something with the model, invoke the action.
So, a button that closes the window and saves something would be defined like this:
<Button Name="SaveBtnr" VerticalAlignment="Bottom"
Command="{Binding Save}" Click="OnSaveClick"
CommandParameter="{Binding}">Save</Button>
And the handler would be:
private void OnSaveClick(object sender, RoutedEventArgs e)
{
//Do UI Stuff
}
And then your command:
public void SaveCommand(object parameter)
{
//SaveStuff
}

Expose a Click event of a button inside a UserControl in Silverlight

I have a button inside my UserControl. I have three instances of this UserControl on the same page.
How can I expose the click event of the button inside such that I can assign different events for each instance of my UserControl.
I think this is similar to concept behind exposing DependencyProperty but I don't understand how to do it for events.
Thanks.
I normally add an event of the same name (and same parameters) to the user control and subscribe to the child control's original event, so I can pass the event on:
public partial class ClickEventControl : UserControl
{
public event EventHandler<RoutedEventArgs> Click;
public ClickEventControl()
{
InitializeComponent();
}
private void aButton_Click(object sender, RoutedEventArgs e)
{
if (Click != null)
{
Click(sender, e);
}
}
}
I would also be interested if there is a more general way of doing it.

Resources