Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Handling Windows in WPF Applications

0.00/5 (No votes)
28 Jun 2013 1  
Presents a library which allows to open and handle dialogs in MVVM manner.

Introduction

Hello! I'm a WPF/Prism developer. I found a need to open real windows in my WPF/Prism applications. The Prism provides a good way to open pop-up windows but not real ones. If the application follows MVVM pattern, this is bad practice to open a new window from view model code . Sometimes, I would like to open and handle the window from XAML. How can it be performed?

Background

The key for the solution are behaviors. The basic idea is to write (blend) behavior that will open windows upon a request and manage them. So windows will be defined in XAML. We will need some proxy object which allows us to access our behavior if we want to show a dialog from code or handle the window by a trigger:

Additionally, we need to define routed commands which allows us to perform windows operation from XAML:

Using the Code

Let me to present WOP library.

First of all, it contains window open behavior which allows to define windows in XAML. This code is located in main window:

<i:Interaction.Behaviors>
        <wop:OpenWindowsBehavior>
            <wop:WindowDefinition Type="{x:Type samples:Dialog1}" 
				  Key="Dialog1" 
				  DataContext="{StaticResource DialogViewModel}"/>
        </wop:OpenWindowsBehavior>
</i:Interaction.Behaviors>  

For the behavior following additional properties can be given:

  • WindowsOperationManager - This is our proxy object to communicate with triggers or view model. Normally, there is no need to specify the manager, WindowsOperationManager.Default manager will be used.
  • DefaultType - Sets default window type. If no type is specified in a window definition, this type will be used.

For each window, a WindowDefinition row should be added. Here we need to specify unique window key which will be used to identify the window. The DataContext can be specified or given in a view model code when window is open. Instead of giving the data context object, we can also specify DataContextType, then a new data context object will be instantiated automatically. Additionally, the following parameters can be provided:

  • Title - Sets the created window title. When set, overrides original window title.
  • WindowStartupLocation - Sets created window start up position. Default value is CenterOwner.
  • Top - Sets created window Top. This property is ignored if WindowStartupLocation isn't Manual.
  • Left - Sets created window Left. This property is ignored if WindowStartupLocation isn't Manual.
  • MultipleInstances - Sets the flag which determines this window definition can be used to open multiple window instances. In this case, window should not be used as modal dialog.
  • PropagateCommands - Sets the flag which determines if DialogCommands should be propagated to the created window. Default value is true.

Let's say we defined our window how now can they be opened?

First of all, we can open the window from the main window view model code in the following way:

/// <summary>
/// Handles OpenWindowCommand
/// </summary>
/// <param name="prm">Command parameter.</param>
private void OnOpenWindowCommand(object prm)
{
    // Create view model for the dialog (in case the data context is not given in xaml).
    DialogViewModel vm = new DialogViewModel();
    // Show the modal dialog. This line will block execution.
    // Define a static class with window key instead of hardcoded key.
    bool? result = WindowsOperationManager.Default.ShowDialog("Dialog1", vm);
    // At this point the dialog is closed.
}   

Accordingly to Show and ShowDialog methods of the Window class, the execution will proceed or be blocked until the window is closed. The dialog result will be returned. Notice that we need to know only the window key but not type. Mocking the WindowsOperationManager provides us the view model unit testing capabilities.

For more details, please examine SimpleDialog1 sample.

The manager implements IWindowOperationManager interface which provides a rich API to open, close and handle windows defined by the behavior.

SimpleDialog2 demonstrates how windows can be opened and handled from XAML. DialogCommands are handed by the behavior (actually command bindings are set to the associated object and propagated to each new window). In order to open a window, we can execute DialogCommand.Show or DialogCommand.ShowDialog command:

<!-- Notice the command parameter is window key -->
<Button Content="Show dialog" 
Command ="wop:DialogCommands.ShowDialog" 
CommandParameter="Dialog1"/> 

In order to close the window, we can use DialogCommand.Close command. DialogCommand.CloseTrue and DialogCommand.CloseFalse will set the dialog result as well:

<!--This is Dialog window code -->
<Button Content="Ok" Command="wop:DialogCommands.CloseTrue"/>
<Button Content="Cancel" 
Command="wop:DialogCommands.CloseFalse" IsDefault="True"/> 

How can the dialog result or a dialog data be handled in XAML?

We can use interactivity triggers to accomplish this. The wop library provides WindowClosedTrigger and SetFromWindowDataContextAction classes. WindowClosedTrigger is activated when window is closed. Desired window key and dialog result can be specified. Notice that internally the trigger uses WindowsOperationManager to access window objects. If required, the default manager can be replaced with custom one by setting WindowsOperationManager property. SetFromWindowDataContextAction provides ability to take a data from the window data context object and set it to the specified target's property:

<i:Interaction.Triggers>
    <wop:WindowClosedTrigger DialogResult="True" Key="Dialog1">
        <wop:WindowClosedTrigger.Actions>
            <!-- WindowDataContextProperty points on a 
            property of the window data context object.-->
            <!-- TargetName is the name of target control located in main window.-->
            <!-- PropertyName is the name of target control property to be set.-->
            <wop:SetFromWindowDataContextAction TargetName="txtDialogData" 
                                  PropertyName="Text"  
                                  WindowDataContextProperty="DialogData"/>
        </wop:WindowClosedTrigger.Actions>
    </wop:WindowClosedTrigger>
</i:Interaction.Triggers>  

SetFromWindowDataContextAction allows to specify TargetObject instead of TargetName. This can be useful when you need to take a data from dialog view model and set it to the main view model.

Consider SimpleDialog2 for more details.

The WOP library provides the ability to open and handle multiple instances of the same window. In this case, the window gets the same key but different index. MultiInstance sample demonstrates this.

NestedDialogs sample shows how one dialog can be opened from another.

Sometimes, we need to share a data given by one window to others in time when the window is still open.

WOP provides the following mechanism to implement this:

When a window wants to share part of its view model data, it can execute DialogCommand.Post command. This command will be handled by WindowPostTrigger provided by the library. The trigger is the same as WindowClosedTrigger and has the same properties. SetFromWindowDataContextAction.TargetName can be set to currently open window key. In this way, a data of post window view model can be set to other (currently open) window data context:

<i:Interaction.Triggers>
    <wop:WindowPostTrigger Key="{x:Static samples:WindowsList.WINDOW1}">
        <!-- When window 1 post data following actions performed.-->
        <!-- First the action will check if the Target Name is window key.
             If so the target object will be set as target window data context.-->
        <!-- Thus, in this case data from Window1 DataContext property Value 
             will be set to Window2 and WIndow 3 DataContext property Value.-->
        <wop:SetFromWindowDataContextAction 
        TargetName="{x:Static samples:WindowsList.WINDOW2}"
        PropertyName="Value"  WindowDataContextProperty="Value"/>
        <wop:SetFromWindowDataContextAction 
        TargetName="{x:Static samples:WindowsList.WINDOW3}"
        PropertyName="Value"  WindowDataContextProperty="Value"/>
    </wop:WindowPostTrigger>
</i:Interaction.Triggers> 

For additional information, review MessagePosting sample.

WOP library is well XML- documented and blendable. Have a good time playing with it.

All code is written in C#, framework 4.5 VS2012.

Points of Interest

Interaction triggers and blend behaviors provide a wide range of extension for WPF and abilities to solve technical problems which pops up when we try to implement MVVM using WPF. Additionally, they provide solid code block which can be reusable in many situations.

History

  • 28th June, 2013: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here