Bind a Parent window's event to UserControl inner handlers with MVVM - wpf

I have a parent window that loads a usercontrol inside a ContentControl. The parent window has a ToolBar with some buttons (i.e. Save button). I'd like to assign those buttons commands that would be handled inside the usercontrol.
I need to manage my own ICommand commands from the usercontrol's ViewModel so, to summarize: The user clicks on the "Save" button (on the main window) so the button fires an event which the userControl handles to save the information within the control.
Is this possible?

There are two ways of doing it.
Using MVVM,
Since your window contains UserControl, you need to set up so that Window has reference to the UserControl ViewModel(assume it is called UserControlViewModel). If you have a command in the UserControlViewModel, you could bind to that command, by calling: UserControlViewModel.Command something like:
<Button x:Name="Save" Content="Save" Command="{Binding UserControlViewModel.SaveCommand}">
Use the event handler
Again, your window needs to have a reference to the class where the event handler is implemented. you could have the following in your Window XAML file:
<Button x:Name="Save" Content="Save" Clicked="SaveButtonClicked"/>
Then in your code behind,
private void SaveButtonClicked( .... sender, .... e)
{
UserControlClass.SaveData();
// or using command
UserControlClass.MyCommand.Execute()
}

OK, you are trying to bind a child element's command to a parent window. First give a name to the usercontrol (e.g. x:Name = MyUserControl) and write a public command in the usercontrol's datacontext/viewmodel (e.g. ICommand MyCommand). Now in the button do this
<Button x:Name="SaveButton" Command={Binding ElementName=MyUserControl, Path=DataContext.MyCommand} />
This will bind the save buttons command to the command inside the datacontext of child usercontrol :)
BTW, if you are looking for the other way around (i.e. binding child command to parent in mvvm) you will need to use FindAncestor. You can have a look on my codeproject article
regarding this :)

This sounds like a case where Routed Commands could work. For the routed command, set the Command of the tollbar button to "Save". Then in the user control, add a command binding that listens for the "save" command.
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.CommandBindings>
<CommandBinding
Command="Save"
CanExecute="SaveCommand_CanExecute"
Executed="SaveCommand_Executed"
/>
</UserControl.CommandBindings>
</UserControl>
In the code-behind's event handlers for the command binding, just locate the viewmodel and invoke the save command on it's viewmodel. If you want a more purist MVVM approach, you could try Josh Smith's approach to routed commands with MVVM.
Using this approach, as long as the user control has focus somewhere inside of it, then the save button's Save command will route to the user control's command binding and exec the save command.
I think this might accomplish your goal of decoupling the toolbar button from the dynamically loaded user control in the ContentPresenter.

Well after a while I come back to this question. I needed to communicate with my parent window so i decided to perform the view-viewmodel datacontext binding right from the parent window, mainly because I want the child viewmodel to attach to events fired from its parent and also I can fire events to the parent in order to show messages outside the child control.
I know I may not be using the MVVM pattern completly but I wanted to have more control over these features.

Related

Catching right mouse click on user control level

<UserControl>
<Grid>
<!-- multiple controls here -->
</Grid>
</UserControl>
As the above example says, there are multiple MS / 3rd party controls hosted on UserControl. I need to catch mouse right click over the UserControl (or any of its child controls). It seems that PreviewMouseRightButtonUp event is not fired when clicked on some of the 3rd party controls inside the UserControl. As per documentation, PreviewMouseRightButtonUp is not a tunneling event, but a direct event, so it is possible that some 3d party controls do not notify the subscribers about this event.
I have also tried to add handler to this event, but still no result
AddHandler(UserControl.PreviewMouseRightButtonDownEvent, new RoutedEventHandler(GetHandledToo), true);
AddHandler(UserControl.PreviewMouseRightButtonUpEvent, new RoutedEventHandler(GetHandledToo), true);
AddHandler(UserControl.MouseRightButtonDownEvent, new RoutedEventHandler(GetHandledToo), true);
AddHandler(UserControl.MouseRightButtonUpEvent, new RoutedEventHandler(GetHandledToo), true);
So, is there a way to always catch the right mouse button event on the user control level, no matter if it is being marked as handled or not?
Edit: I have found where the problem lies. The problem lies in the ContentControl which has not yet evaluated its content. Example:
<ContentControl x:Name="chart" Content="{Binding DiagramScreen}" />
ContentControl recolves its structure through DataTemplate. If DiagramScreen is NULL, the ContentControl's content is not yet created. This means that the space that this ContentControl occupies in UserControl is not responding to Mouse events. How Can I make ContentControl respond to mouse events, even if its content is NULL?
I think if it is handled you can't get it further for bubbling event. A workaround is to pass your host object to your user control. After that, you can call whatever method you want from your host object. It's ugly but I think it will work

mvvm Pass button click to parent

Note: this is a different question to the one here: Pass dependency property to child view
I am currently creating a usercontrol in wpf which consists of several 'screens' that the user will click through.
For each 'screen' I have created a view with it's own viewmodel (e.g. View1.xaml, View2.xaml). The main usercontrol can then access these views:
<UserControl.Resources>
<local:ModuleBaseViewModel x:Key="ViewModelDataSource" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewModelDataSource}}">
<local:View1 Visibility="Visible"/>
<local:View2 Visibility="Hidden"/>
</Grid>
I would like to pass a button click from a view back down to the parent usercontrol so that I can hide that view and show the next one. Can someone explain to me how to do this?
Thanks!
If you are using MVVM and WPF, chances are you are using an IoC container. Almost every IoC container contains a method of publishing an event and then subscribing to that event. In Prism, the code looks something like this:
EventAggregator.GetEvent<SomeEvent>().Publish(ParametersHere)
I would suggest you look at your options for publishing events and reacting to them. Then your parent view model can respond to the children's events.

Best way to close Window with MEFedMVVM

In my Application i have a MainWindow that import over MEFedMVVM the ViewModel:
xmlns:mefed="clr-namespace:MEFedMVVM.ViewModelLocator;assembly=MEFedMVVM.WPF"
mefed:ViewModelLocator.ViewModel="MainViewModel"
And now i have my ViewModel too that realize the ViewModel:
[ExportViewModel("MainViewModel")]
public class MainViewModel: ViewModelBase
In my ViewModel i have a ICommand property for closing the window. The event for closing can comes from anywhere. And with help from the Cinch Framework 2.0 i realize a Simplecommand with Execute methode.
Question
How can i Close the Window from my execute methode? Over the dependency injection i haven't a constructor i can't register an event or give the view as parameter to the viewmodel.
Edit
However, a possibility which I think is not nice:
Call this in the methode
Application.Current.MainWindow.Close()
You can achieve this by writing an ICommand that passes the Window instance in as a parameter.
A good example is available here: How can I assign the 'Close on Escape-key press' behavior to all WPF windows within a project?
In that post the ICommand is eventually bound to a KeyBinding (so that the Escape key can be used to close the window) but you would be able to bind the command to any button or invoke it from anywhere within the view. The important part is to use a RelativeSource on your command parameter binding to reference the Window that you want to close
Edit in response to comments
The command is a singleton, but there is no requirement for it to be - it is only a singleton because it is stateless and it makes the binding easier. It acquires a reference to the Window through binding, so for a UserControl you can just use:
<Button Command="{x:Static mynamespace:CloseWindowCommand.Instance}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" Content="Close My Parent Window" />
To be able to call it from view model code is slightly more complicated and needs a different approach; a good example can be found here: http://gallery.expression.microsoft.com/WindowCloseBehavior

Initiating UserControl via MVVM in WPF / focus issue

I have a few usercontrols loaded into a tabcontrol via MVVM in WPF.
Within the XAML for the usercontrol I am setting focus to a textbox using the FocusManager, however this appears to only work when the first instance of the usercontrol is created.
Just to test I added a loaded event handler to the usercontrol - this is only called on the first instance.
I'm using data templates for the user controls as follows:
<DataTemplate DataType="{x:Type local:UserTypeViewModel}">
<local:UserTypeView />
</DataTemplate>
The textbox is focused as follows:
FocusManager.FocusedElement="{Binding ElementName=txtName}"
Additionally I'm using a global event handler (for the textbox GotFocus event) which selects all the text using a dispatcher.
If anyone has any tips on how to achieve focus with every usercontrol I'd be very grateful.
Thanks, Ben.
Remember that a visual element can only receive focus, if:
It is visible (in a TabControl only one tabitem can be visible at a time
IsFocusable must be set to true (is default false for UserControls)
It has finished loading (as you write - do it in the Loaded event))
I think the first reason is why it only works for the first element.
As for how to achieve it for all controls - you can use a style with an EventSetter for the Loaded event. You need to make a style per type of control though to avoid having to set it for each control.

How do I bind a command to e.g. the right mouse button in a ControlTemplate in WPF?

I have a custom Control derived class and a view model to go with. The user can do several actions with this control, and I'm thinking it's best to implement these as RoutedCommand objects or ICommand derived objects in the view model so the ControlTemplates can bind to them. Binding a command to a button in one ControlTemplate should be straightforward, but how can I bind the command to e.g. the right mouse button in another ControlTemplate? Probably a MouseGesture is involved, but I'm having a hard time fitting the pieces together.
A MouseGesture is involved, but you don't have to explicitly construct it. You can use a MouseBinding which will construct a MouseGesture for you under the hood.
You need a UIElement to attach your binding to. Here is how you would do it with a separate decorator.
<ControlTemplate ...>
<Decorator>
<Decorator.InputBindings>
<MouseBinding MouseAction="RightClick" Command="..." />
</Decorator.InputBindings>
... content here ...
</Decorator>
</ControlTemplate>
More likely your ControlTemplate uses a Panel such as a DockPanel or Grid for layout, in which case you could attach the binding to that instead of adding a Decorator.

Resources