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?
Related
I need to connect methods in View (WPF window) to events in ViewModel. Is it violation of MVVM pattern to DirectCast Object DataContext in view to concrete VM type and connect its events? If yes, is there better way to do it?
First look at what the methods in the view do. If they manipulate the view, consider adding properties to the viewmodel that you change in the events in the viewmodel and bind the view to. This way, by binding the view to properties you eliminate the need for code in the view.
If the methods contain other logic consider moving that logic to the viewmodel.
In other cases casting a DataContext to a viewmodel or interface can be a valid option and is not a violation of the MVVM pattern.
When adding code to a view, do consider testing. Automated/unit testing a view is harder than testing a viewmodel.
It is not a violation of the MVVM pattern, but the more abstract the better, of course (not because of MVVM but as a general good practice).
If you're setting your DataContext on XAML, you may be able to keep it abstract by using Interactivity EventTrigger and CallMethodAction... Maybe. But if you're setting it on code-behind (via injection or whatever), you're left with either casting the DataContext to a known type, or using Reflection (I wouldn't >_>).
Generally, creating an interface for your ViewModel, so you keep a decent level of abstraction and only expose what the view needs to know instead of its whole implementation, is good enough for most scenarios.
public interface IMyViewModel
{
event EventHandler MyEvent;
}
public class MyViewModel : IMyViewModel
{
public event EventHandler MyEvent;
// More viewmodel related stuff
protected virtual void OnMyEvent(EventArgs e)
{
if (MyEvent != null)
MyEvent(this, e);
}
}
public class MyWindow : Window
{
public MyWindow(IMyViewModel viewModel)
{
this.DataContext = viewModel;
InitializeComponent();
(this.DataContext as IViewModel).MyEvent += MyEventHandler;
}
private void MyEventHandler(object sender, EventArgs e)
{
// Do view related stuff
}
}
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 to set the DataContext value later in a WPF application?. Here in the below code I am setting it at the beginning (Startup). Later I would like to set another DataContext value to the view. What is the best way to do it?
public partial class App : Application
{
private void OnStartup(object sender, StartupEventArgs e)
{
MainWindow view = new MainWindow();
MainViewModel mainViewModel = new MainViewModel();
view.DataContext = mainViewModel;
view.Show();
}
}
Simply telling, there is a ObservableCollection inside the MainViewModel and it is assigned when the user do some button press action only. I need that data to be updated in the view. Please let me know if you have any questions
EDIT
::::
I have an additional question too. Do I need to reassign the entire DataContext or Can I do something through INotifyCollectionChanged event? Please clarify
Your mainViewModel is tightly coupled to your class MainViewModel. Try to use interface. you can create IViewModel and implement it to your MainViewModel. This site will help you
dofactory
you can also use some dependency injection like ninject.
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
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. :)