Wpf MVVM button call method in view and view model also - wpf

In my MVVM application a have a button in a view.
When I press a button I want the run some method in the view, and also some method in the view model.
I connected come command to my button, so the command can run some method in the view model. But how can I run some method in view also?
I tried to connect a click event also, but it does not work.
What is the best way to run functions from view and viewmodel also.
Thanks,

You can do this entirely in XAML with interaction triggers:
<Button Content="Do Something" Click="OnClickHandler"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd ="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding Path=ViewModelCommand1}" />
<cmd:EventToCommand Command="{Binding Path=ViewModelCommand2}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
In this case both the Window handler and each of the two viewmodel commands get invoked. If the handler sets e.Handled to true the viewmodel commands don't get called. If you set PassEventArgsToCommand="True" in the cmd:EventToCommand then you can specify a handler that accepts the args; setting Handled to true in the first viewmodel handler won't stop the second one being called but you can still check the value in the second handler manually.
Now, that said I would strongly encourage you to re-evaluate your architecture. Calling code-behind is not MVVM, and in over 7 years of doing this on a daily basis I have yet to see a single case where it was actually needed.

on the Click event, Execute command.
private void btnClick(object sender, RoutedEventArgs e)
{
var btn = sender as Button;
btn.Command.Execute(btn.CommandParameter);
}

I would highly question your decision to "run both view and viewmodel method on button click". To me, it seems that what you want is that button runs a viewmodel command which in turn causes some change in view. What if for example it was possible to execute the method not through UI but from somewhere else in the application. Shouldn't the view change also?
For that I recommend creating an interface I[something]View, that the view will implement and which the view model have reference to. Then, the viewmodel can call the method on the interface which will do what you are expecting on the button click.

Related

Handle WPF button click and enable disable

I have a UI with a few controls.
Initially when form loads, search button will be disabled, once all
search criteria are given, search button will be enabled
automatically.
On click on search button, I want to call the method
using MVVM pattern and bind the result in grid
XAML:
<Button Name="btnGetDetails" Content="Get Details" Grid.Row="2" Command="{Binding SearchCommand}"/>
What code is required in model, view model and XAML?
Command will be executed only when the button will be clicked on. If you need to do something to the button then you should do it to the Window that contains the button (assuming your button is in a Window). Now if you want to stick with the MVVM pattern then you should not use the Window.OnLoaded, because that would put code in your code behind. One option is to use System.Windows.Interactivity, which you have download seperately. Here is what it will look like:
<Window x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr
-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ...}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Window>
As for what your Model, View and ViewModel should be, I think you should check out some tutorials on the web. There are some very good explanations on how to implement the MVVM pattern. I found this youtube video pretty informative myself:
http://www.youtube.com/watch?v=EpGvqVtSYjs
So some MVVM basics here. You're on the right track, just missing a step. Your Command implementation in your view model should (potentially) accept two inputs: An Action representing the executing code, and a Predicate that returns true/false on whether you can execute the code in the Action block. So, in your view model, define your command along the lines of (note: this is a sample from one of my projects):
this.executeCommand = new RelayCommand(this.OnExecuteClicked, this.OnCanExecuteChanged);
The OnCanExecuteChanged method will return a bool based on whatever criteria you set up. So, if you want the submit button enabled when property A and property B have been properly set up, then return true, else return false. The internal workings of your command implementation will take care of the rest. Do a search for a RelayCommand implementation (if you don't have it already) or a DelegateCommand for further samples.

How can i get "Click" event work with my WPF button when I use EventTrigger from the Expression Blend Interactivity assembly?

I'm attempting to use a Command, defined in my view model with EventTriggers as defined in xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
I have changed the control from a Microsoft.Surface.Presentation.Controls.SurfaceSlider-derived control to a regular WPF Button. With the Button, the same ConfirmOrderCommand gets fired if I use the EventName="MouseEnter", but if I use EventName="Click" nothing happens. The XAML for the Button is here.
<Button Name="ConfirmButton"
IsEnabled="{Binding IsEnabled}"
Style="{StaticResource ConfirmButtonStyle}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ConfirmOrderCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
I want the user to be able to click the button to fire the command. Can I get the Click event to work for a Button, or do I need to look for another event? I've also failed to get MouseLeftButtonUp to work.
Try EventName="Button.Click". Anyways, you can just set the Command Property of the Button itself, and remove the Interaction.Triggers part.
The actual reason the Button.Click would not work, was determined after much pain and finally asking someone on the project after he woke up in the morning.
Our application was stealing almost all of the Button events with FrameworkElement Preview delegates.
public static void RegisterEvents(FrameworkElement parent)
{
parent.PreviewMouseDown += MouseDown;
parent.PreviewMouseUp += MouseUp;
// even more of these
}
The delegates would then use System.Window.Input.TouchDevice to ReportDown() in the case of MouseDown etc. All this time I just thought I didn't know how the standard WPF button usually works. I guess the moral of this story is events don't have to start at the child control and then propagate up to their containers until Handled = true. Preview allows the container to capture the events first.

MVVM restricts Code Behind?

Does using MVVM model in WPF restricts the programmer from writing code behind? Avoiding Code behind results in lot of complications but on the other hand coupling rises as an issue. So what is better?
The MVVM model does NOT restrict you from writing code behind.
What it does promote is that the View should only be dependent on the ViewModel (and the ViewModel on the Model)
So if you write code behind that is actually implementing the ViewModel you are creating a dependency from the ViewModel to the View.
Code behind that ONLY does View related things is OK
The thing you are losing when using code behind is the easy (unit)testing of that code.
EDIT
A common expression in MVVM-world is "XAML only". As much as I like a short, snappy statement it tends to divert from the actual problem MVVM is trying to solve and how it tries to solve it.
As long as you stick to making the View dependent on the ViewModel and the ViewModel on the Model AND strive for (unit)testability you are on the right track.
EDIT 2
In the View handling an event should only do two things: change the View itself or notify the ViewModel using a binding that something has changed. Notifying the VIEW of a change in the ViewModel should be done by implementing INotifyPropertyChanged on the ViewModel.
In a similar way the ViewModel can respond to events in the View by binding ViewModel Commands to the View.
A WPF Button has a Command property that can be used. It is executed when the button is clicked.
If a control has no Command property or you want to execute a command when a different event is raised all you have to do is turn the event into the execution of an ICommand.
Microsoft already provided an implementation of that in the Blend SDK. From this article:
... xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity..."
<Slider
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<i:InvokeCommandAction Command="{Binding MyCommand}"
CommandParameter="{Binding Text, ElementName=textBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Slider>
And a discussion about commands versus event triggers

WPF - View model updating property via background worker, but view is not updating some elements until focused

View model is loading data asynchronously using background worker thread in model. All properties in the model and view model raise the property changed events, and all properties are being updated in the view as expected, except 2 buttons whose IsEnabled state depends on the outcome of some properties that are loaded.
The frustrating part is that as soon as I focus on any part of the view, or set a breakpoint after the properties are updated (create a delay), then the buttons IsEnabled state is updated as expected. So it appears to be a timing issue.
Any clues as to how to the best way to solve this? I'm using mvvm-light framework, but that shouldn't matter.
I've tried binding IsEnabled to the button instead of just relying on the Command property, but that made no difference. I've confirmed via logging that view model properties are set and the PropertyChanged event is being raised for the properties associated with the buttons.
Considering sending a message using mvvm-light messenger from the view model to the view on the async completed event and then somehow? triggering a view refresh, but that seems like a kludge.
Update
Thanks to blindmeis' answer, I tested the button behaviour without the Command binding set, i.e. just binding IsEnabled property, and it works as expected!
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}" />
Obviously it's not great because I can no longer execute the command :) but as soon as I add the command back, it stops behaving:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
Command="{Binding LoadProjectsCommand}" />
Leaving IsEnabled binding doesn't solve the problem, but that seems like a good clue.
The view model command code:
public ICommand LoadProjectsCommand
{
get
{
if (_loadProjectsCommand == null)
{
_loadProjectsCommand = new RelayCommand(loadProjects, () => CanLoadProjects);
}
return _loadProjectsCommand;
}
}
Workaround
Wire up the Click event and avoid Command. Would be nice to solve it from the view model, but this works:
<Button
Grid.Column="2" Content="{Binding LoadProjectsLabel}"
VerticalAlignment="Top" HorizontalAlignment="Right"
IsEnabled="{Binding CanLoadProjects}"
Click="loadProjects_Click"/>
Code behind:
void loadProjects_Click(object sender, RoutedEventArgs e)
{
SettingsViewModel vm = (SettingsViewModel)DataContext;
vm.LoadProjectsCommand.Execute(null);
}
Answer from other thread:
When your BackgroundWorker completes, call CommandManager.InvalidateRequerySuggested();
By default, Commands are only requeried occasionally by WPF. Otherwise, there would be a huge amount of overhead in constantly calling "CanExecute" on every ICommand implementation. Calling the above method forces the CommandManager to update immediately.
This will force the Commands to re-enable/disable appropriately.
EDIT:
i use a simpler but not so beautiful workaround. i simply call OnPropertyChanged("MyICommand") for my commands in my BackgroundWorker Completed Event.
EDIT:
here is another nice solution.
You should bind command parameter property to any updatable property on viewmodel and can execute must use this command parameter to enable button. If command parameter's target is updated, binding will enable/disable based on return value of can excute.

MVVM- Trigger Storyboard in the View Model in Silverlight

I have a couple of Storyboards in my view that I would like to trigger from the ViewModel if possible. Is there a simple way or elegant way of doing this. Here is what I am trying to do.
Person Clicks on a Button-->RelayCommand (In the ViewModel), the Relay Command should then play the storyboard. Also one more thing, I would like to also trigger the storyboard animation by itself in the ViewModel without any interaction.
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding ButtonPress}" CommandParameterValue="RedButtonLight">
</cmd:EventToCommand>
</i:EventTrigger>
</i:Interaction.Triggers>
I know it's a long time ago. But I've written a detailed blog post about Triggering Storyboards and MVVM.
http://mark.mymonster.nl/2010/12/14/trigger-a-storyboard-on-viewmodel-changes/
If the button click is purely to power a view-related thing and isn't doing any actual application logic, then I would argue that you can do all this in the code-behind of the view class.
If this isn't the case then I would use a property on the Presentation (ViewModel) to signal that the Presentation is in a state, and have the view react to the PropertyChanged event and start the storyboard. This is assuming you are implimenting INotifyPropertyChanged on your Presentation class.
Have a look at the expression samples. There is a trigger for events from the datacontext. DataEventTrigger
You could use that to trigger a ControlStoryboardAction to start the story board whenever your viewmodel raises a particular event.
Your viewmodel could then raise the event as part of the command as well as at other times.
Heres how you can do it in blend without touching a line of xaml or code :
http://www.basarat.com/2011/05/expression-blend-starting-storyboard.html

Resources