wpf prism (CAL) - Closing Window in Popup Region when the ViewModel knows nothing of the View - wpf

We have a Region in the Window tag of our shell, adding things to this region pops out another Window.
<Window x:Class="GTS.GRS.N3.Shell.Shell1"
--removed namespace references for clarity
cal:RegionManager.RegionName="{x:Static Constants:RegionNames.WindowRegion}">
We're adding ViewModels to the Region Manager and then the View is attached via a data context so that the ViewModel knows nothing about the View i.e.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<DataTemplate DataType="{x:Type Model:CommunicationViewModel}">
<v:CommunicationView />
</DataTemplate>
</ResourceDictionary>
My question is how do I close the Pop up Window, I tried removing the ViewModel from the RegionManager - but this exceptions ... the View is a UserControl, but I need to close its Owner which is a new Window opened by the Region. I don't really want to have to hack it via the DataContext of the ViewModel.
Can anyone assist please?

Andy,
It took me quite awhile to figure this one out myself.
The cleanest way to accomplish this is by using the DelegateCommand (or RelayCommand) and adding an event handler in the code that creates the window with window.Show().
// Define the View
Shell window = Container.Resolve<Shell>();
// Define the ViewModel
ShellViewModel windowVM = Container.Resolve<ShellViewModel>();
// When the ViewModel asks to be closed, close the View.
EventHandler handler = null;
handler = (sender, e) =>
{
windowVM.RequestClose -= handler;
window.Close();
};
windowVM.RequestClose += handler;
// Set the ViewModel as the DataContext of the View
window.DataContext = windowVM;
// Display the View
window.Show();
I then use a Composite Event to notify the window's ViewModel (not the UserControl's) that it has a request to close. The assigned subscription handler for the composite event then calls this.OnRequestClose().
In the Constructor for the ViewModel:
//subscribe to composite events
_eventAggregator.GetEvent<WindowCloseEvent>().Subscribe(WindowClose);
In the body of the ViewModel:
/// <summary>
/// Private Event handler for WindowCloseEvent.
/// </summary>
private void WindowClose(bool value)
{
// Close the View
this.OnRequestClose();
}
See Josh Smith's excellent article on MSDN about using the M-V-VM pattern with WPF at http://msdn.microsoft.com/en-us/magazine/dd419663.aspx for more information.

_regionManager.Regions[RegionNames.PopupRegion].Deactivate(_regionManager.Regions[RegionNames.PopupRegion].ActiveViews.FirstOrDefault());

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.

Screenshot of usercontrol immediately prior to being unloaded

I'm building a pos system that has a main ContentControl to display different screens of the application. I use DataTemplates to map my viewmodels to views. To navigate between the different views displayed in the ContentControl I'd like to store a screenshot of the UserControl in the viewmodel when the UserControl is unloaded (or the ContentControl changes).
I posted a related question here WPF Binding FrameworkElement event to command in which I attempted to bind a command to FrameworkElement.Unloaded but that doesn't work (see answer at that link)
Is this possible without breaking the MVVM pattern?
here is a nice link to how to do a screenshot in wpf.
here is what i would do:
my mainviewmodel which handle the navigation should expose an event and raise this event before you set the new contentviewmodel. the old contentviewmodel should be in the eventargs. in your mainwindow codebehind you subscribe to the event(not breaking mvvm here). when ever this event is raise you can call the screenshot method and put the result to the oldviewmodel.
edit:
mainwindow codebehind
void NavigationChangingEvent(object sender, NavChangingArgs args)
{
var oldvm = args.ChangingViewmodel;
oldvm.Screenshoot = this.mycontentcontrolwheremyviewmodelareshown.GetJpgImage(1, 90);
}

Multiple viewmodel interacting with each other

I'm working on a Surface WPF project where we try to implement the MVVM pattern. Within this project we are building a few custom controls which we bind to different viewmodels.
For example we have a settings control which has a settings viewmodel and we have a mainviewmodel which is the "overall" viewmodel.
In our surfacewindows.xaml page we are setting the datacontext to the main viewmodel by using the viewmodel locator in mvvm-light. Also on our surfacewindow.xaml we have added our settings control and on the control we have set the datacontext to the settings viewmodel.
Now we need both viewmodels to interact with each other: The current case is that we need to set the visibility of the settings control. We have a property on the main viewmodel that is a boolean (IsSettingsControlVisible) which is bound to the controls Visibility property by using a converter to convert the boolean to a visibility object.
The problem arises now when we need to set the visibility to not visible by clicking on a close button on the settings control. Because we have set the datacontext on the control to the settings viewmodel, we cannot access the mainviewmodel.
What we have thought of until now is adding the settings viewmodel as a property to the mainviewmodel and remove the datacontext from the settings control. In the settingscontrol we will than use the binding as SettingsProperty.Property. Than we can access the mainviewmodel too from the setttings control. Does that make sense? Are there better ways of doing these kind of interactions?
I really like to hear your ideas about how to make these interactions happen.
I tend to work with graphs of view models that are constructed using Castle Windsor. The top level view model uses constructor injection to receive the next level of view models that it requires. And in the views I bind content presenters to properties on the view models to create the corresponding view graph.
Doing this, it's quite easy for parent child view models to communicate, but a bit harder for sibling or more distant view models to communicate.
In these instances, I tend to use an event aggregator, or Messenger to allow the view models to communicate.
As you are already using MVVMLight, I'd suggest using the MVVM Light toolkits Messenger system. It's intended for message exchange between ViewModels.
The concept behind is the Mediator pattern where different objects exchange information without knowing each other.
Here's an example:
In the SettingsViewModel register to an event that tells to show the settings dialog
public SettingsViewModel()
{
Messenger.Default.Register<ShowSettingsMessage>(this, ShowSettingsDialog);
}
private void ShowSettingsDialog(ShowSettingsMessage showSettingsMessage)
{
// Set the visibility:
this.IsVisible = showSettingsMessage.Content;
}
In your MainViewModel you send the notification, wrapped in a Message:
// make the settings visible, e.g. the button click command:
Messenger.Default.Send(new ShowSettingsMessage(true));
And here's the message:
// the message:
public class ShowSettingsMessage : GenericMessage<bool>
{
public ShowSettingsMessage(bool isVisible)
: base(isVisible)
{ }
}
I wouldn't recommend making the SettingsViewModel a property of the Mainviewmodel as you lose the possibility to use the SettingsViewModel in a different context or even remove/exchange it.
Try to create a Dependency Property on the Settings control called IsSettingControlVisible and bind it with the parent viewModel.
EDIT:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public int MyProperty
{
get { return (int)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.Register("MyProperty", typeof(int), typeof(UserControl1), new UIPropertyMetadata(0));
}
and use it like this...
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 MyProperty="{Binding Path=ParentViewModelProperty, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" />
</Grid>
</Window>

adding custom routed event of user control to xaml of a window in wpf

I am little new to Command binding so this might be a trivial question to many. I know that we can add Command bindings in xaml of a window and give its correspondng property in viewmodel. This viewmodel will be given to the DataContext of the window. Something like the following
--app.xaml.cs
mainWindow.DataContext = viewModel;
-- xaml
lt;Button Grid.Row="1" HorizontalAlignment="Right" Margin="0,3,18,3" Name="button1" Width="110"
Command="{Binding LoadCommand}">_Load</Button>
-- viewmodel
/// <summary>
/// Gets the load command.
/// </summary>
/// <value>The load command.</value>
public ICommand LoadCommand
{
get
{
if (m_LoadCommand == null)
{
m_LoadCommand = new RelayCommand(param => CanLoad(), param => Load());
}
return m_LoadCommand;
}
}
Here the relaycommand is a class which implements ICommand interface. CanLoad() and Load() are the methods which will get executed for canexecute and execute action of the relaycommand respectively. This is the click event of the button which is handled.
I have a user control which has a custom routedevent registered in it and the user control is then used on a window. I am currently adding the event handler explicitly in code.
//hook up event listeners on the actual UserControl instance
this.ucCustomEvent1.CustomClick += new RoutedEventHandler(ucCustomEvent_CustomClick);
//hook up event listeners on the main window (Window1)
this.AddHandler(UserControlThatCreatesEvent.CustomClickEvent, new RoutedEventHandler(ucCustomEvent_CustomClick));
I dont want to hook up the routedevent explicitly in code but in the xaml in the similar way as in the button example. I have uploaded the working sample code here for your perusal.
I'm not sure I fully understand your question but I hope one of my answers below helps you out.
To attach a "direct" event handler in XAML, just do the following:
<c:MyUserControl x:Name="uc1" CustomClick="uc1_CustomClickHandler"/>
To hook up a handler for the (routed) event of one element (e.g. the CustomClick event in your example) to another element (e.g. the parent window):
<Window c:MyUserControl.CustomClick="ucCustomEvent_CustomClick"/>
Now, if you want to tie up an event in your UI to a Command in your ViewModel, you will need attached behaviors to do that. There are lots of frameworks around featuring different implementations of this. Here's one you can try out: http://sachabarber.net/?p=514. It will allow you to do something like the following in your code:
<c:MyUserControl local:CommandBehavior.RoutedEventName="MyCustomClick"
local:CommandBehavior.TheCommandToRun="{Binding MyViewModelCommand}"/>
Hope this helps.

What goes in the Main method for WPF / MVVM?

Doing my first MVVM WPF application. I expected to see a Main() method in the App.xaml (I'm used to Silverlight) but it isn't there. I added my own Main method. In Silverlight I then created a View linked to a ViewModel and set it as the RootVisual. How do I correctly open my first View Window in WPF?
There are many ways, but I think the WPF equivalent of setting a Silverlight RootVisual is to call Application.Run
App.Run(new MainWindow())
In general, there is no right or wrong way here nor is there an accepted convention. Some people make this call in the Startup event. Other people don't use the event and override OnStartup instead. Still others use StartupUri in App.xaml.
When I created my first (and to date, only) WPF project, to display the appliation's main window (called MainWindow), I overrode the App class's OnStartup method as below:
/// <summary>
/// Raises the System.Windows.Application.Startup event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.StartupEventArgs" /> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// I did some app-specific stuff here...
MainWindow view = new MainWindow();
// Allow all controls in the window to bind to the ViewModel by setting the
// DataContext, which propagates down the element tree.
MainWindowViewModel viewModel = new MainWindowViewModel();
// and I did some more app-specific stuff here...
view.DataContext = viewModel;
view.Show();
}
I believe this was the recommended way for MVVM applications (was a while back though); this code was taken from a .NET 3.5 application.

Resources