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

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? :)

Related

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 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

Binding Button.IsEnabled to position of current in CollectionView

I am trying to bind the IsEnabled property of a button to properties of the window's CollectionViewSource. I am doing this to implement First/Previous/Next/Last buttons and want the First and Previous to be disabled when the view is on the first item etc.
I have the collection view source set up, UI controls binding to it correctly, with access to its view in code so the click event handlers work fine in navigating through the view.
<CollectionViewSource x:Key="cvMain" />
The DockPanel is the root element of the window
<DockPanel DataContext="{StaticResource cvMain}">
FoJobs is an observable collection, cvJobs is a CollectionView that I use in the button's click handler
private void Window_Loaded(object sender, RoutedEventArgs e) {
((CollectionViewSource)Resources["cvMain"]).Source = FoJobs;
cvJobs = (CollectionView)((CollectionViewSource)Resources["cvMain"]).View;
}
I have tried this but get a binding error "BindingExpression path error: '' property not found on 'object' ''ListCollectionView'"
<Button Name="cbFirst" Click="cbMove_Click" IsEnabled="{Binding Source={StaticResource cvMain}, Converter={StaticResource CurrPos2BoolConverter}}" />
I am trying to do with a converter first but figure a style with triggers would be more efficient, but cant get access to the collection view. Even though the underlying datacontext is set to a collection view source, the binding is passed to the converter as the view's source (if I dont explicity set the binding's Source, as above), which has no currency properties (CurrentPosition, Count etc).
Any help would be greatly appreciated.
Why don't you use a RoutedCommand for this(even if you don't use MVVM that is)?
say something like:
<Button x:Name="nextButton"
Command="{x:Static local:MainWindow.nextButtonCommand}"
Content="Next Button" />
and in your code-behind:
public static RoutedCommand nextButtonCommand = new RoutedCommand();
public MainWindow() {
InitializeComponent();
CommandBinding customCommandBinding = new CommandBinding(
nextButtonCommand, ExecuteNextButton, CanExecuteNextButton);
nextButton.CommandBindings.Add(customCommandBinding); // You can attach it to a top level element if you wish say the window itself
}
private void CanExecuteNextButton(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = /* Set to true or false based on if you want button enabled or not */
}
private void ExecuteNextButton(object sender, ExecutedRoutedEventArgs e) {
/* Move code from your next button click handler in here */
}
You can also apply one of the suggestions from Explicitly raise CanExecuteChanged() to manually re-evaluate Button.isEnabled state.
This way your encapsulating logic relating to the button in one area.

Keyboard shortcuts in WPF MVVM?

I have WPF application that follow MVVM pattern. I need to implement keyboard shortcuts. These shortcut have to contol WebBrowser control behaviour. I defined first custom command and added to view's inputbindings. There will be much more commands and they would have to invoke scripts on browser:
MainWindow.xaml.cs:
...
CommandBinding cb = new CommandBinding(RemoteControlCommands.TestCommand, MyCommandExecuted, MyCommandCanExecute);
this.CommandBindings.Add(cb);
KeyGesture kg = new KeyGesture(Key.Q, ModifierKeys.Control);
InputBinding ib = new InputBinding(RemoteControlCommands.TestCommand, kg);
this.InputBindings.Add(ib);
}
private void MyCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
webBrowser.InvokeScript("foo", "Hello World!");
}
private void MyCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
My question is how to fit this into MVVM patern? MVVM is a new concept to me but I understand how to bind view's command to view model and there execute methods or change properties.
However what I need in this case is to execute a method on a control in the view. What is the best place to shortcut handling in this scenario?
<Window.InputBindings>
<KeyBinding Command="{Binding MyCommand, Source=viewModel...}"
CommandParameter="{Binding,ElementName=browserControl,Mode=Self}"
Gesture="CTRL+R" />
</Window.InputBindings>
You can bind command property to View Model's command.

Need help handling events of a DataTemplate in the Application.xaml file

I have in my application a data template that has a few buttons.
I want those buttons' even handler to be fired in the current page (I am using this template in many pages) rather than in the Application.xaml.vb/cs file, since I want different actions on each page.
I hope I am clear.
You can use commanding to achieve this. Have the Buttons in the DataTemplate execute specific Commands:
<Button Command="{x:Static MyCommands.SomeCommand}"/>
Then have each view that uses that DataTemplate handle the Command:
<UserControl>
<UserCommand.CommandBindings>
<CommandBinding Command="{x:Static MyCommands.SomeCommand}"
Executed="_someHandler"/>
</UserCommand.CommandBindings>
</UserControl>
EDIT after comments: Once you have created a code-behind for your ResourceDictionary as per these instructions, you can simply connect events in the usual fashion:
In MyResources.xaml:
<ListBox x:Key="myListBoxResource" ItemSelected="_listBox_ItemSelected"/>
Then in MyResources.xaml.cs:
private void _listBox_ItemSelected(object sender, EventArgs e)
{
...
}
If you use events and not commands, then in your Click event handler just write
private void Button_Click(object sender, RoutedEventArgs e)
{
var dataItem = (FrameworkElement)sender).DataContext;
// process dataItem
}

Resources