A wpf windows toolkit for window operations, including a few behaviors, useful class for view model. Available on NuGet
- Support both .NET Core 3.0+ and .NET Framework 4.5+
- Handle Window opening, closing using behaviors
- Pass parameters and return value between windows
- Fully MVVM supported
- Other utility behaviors
Install-Package WpfWindowToolkit
dotnet add package WpfWindowToolkit
To use WpfWindowToolkit
in your application you need to add the following namespaces to your Xaml files.
xmlns:behaviors="http://wpfwindowtoolkit.org/behaviors"
xmlns:helpers="http://wpfwindowtoolkit.org/helpers"
To open a window, you can use attached properties of WindowHelper
like this:
<Button x:Name="btn1"
helpers:WindowHelper.OpenWindowType="{x:Type local:Window1}"
Content="Open window using Window Helper" />
or use OpenWindowAction
like this:
<Button x:Name="btn5"
Margin="0,5,0,0"
Content="Open window with parameter using action">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<behaviors:OpenWindowAction Parameter="WPF (action)" WindowType="{x:Type local:Window1}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
# approach 1 & 2
When opening a Window, you can pass a parameter to its viewmodel. Firstly set the parameter, like this:
helpers:WindowHelper.Parameter="WPF (attached property)"
or
<behaviors:OpenWindowAction Parameter="WPF (action)" WindowType="{x:Type local:Window1}" />
Then the view model of the window needed to be opened should inherit ViewModelBaseData<T>
, here the type parameter T
should be type of the parameter you want to passed to, for example:
public class Window1ViewModel : ViewModelBaseData<string>
{
protected override string InternalData { get; set; }
...
}
Of course, you can use data binding to bind a more business specific model or a more complex data type that needs passing to another window.
# approach 3
However, maybe you want to have more control concerning opening a window, not just using OpenWindowAction
and WindowHelper
. Here is what you can do:
Firstly, the view model of first window should inherit from ViewModelBaseEx
, then you can use its ShowWindow(OpenWindowInfo)
method to open another window, so your code would be like this:
public class MainViewModel : ViewModelBaseEx
{
...
public RelayCommand ComplexLogicForOpeningAWindowCommand
{
get
{
return new RelayCommand(() =>
{
// your logic here (before)
this.ShowWindow(new OpenWindowInfo { IsModal = true, Parameter = CurrentFriend, WindowType = typeof(Window2) });
// your logic here (after)
});
}
}
}
On the other hand, the view model of the second window which needs to be opened should be processed in the same way as discussed previously, that is to inherit from ViewModelBaseData<T>
.
When the opening window is closed, you may want to get a return value. To do this, firstly, the view model of the first window should inherit from ViewModelBaseEx<T>
, here the type parameter is the type of the return value, then you can use ShowWindow(OpenWindowInfo, Action<TReturnValue>)
method. Of course, your view model can also inherit from ViewModelBaseEx
and use its ShowWindow(OpenWindowInfo, Action<object>)
method. The second parameter indicates how to handle or process the return value by passing it to an Action
.
public class ReturnValueMainWindowViewModel : ViewModelBaseEx<Friend>
{
public void ShowFriendSelectionWindow()
{
this.ShowWindow(new OpenWindowInfo { WindowType = typeof(ReturnValueTestWindow) }, friend =>
{
if (friend != null)
{
MessageBox.Show($"You have selected this friend: {friend.Name}");
}
else
{
MessageBox.Show("No friend has been selected");
}
});
}
}
Then, the view model of the second window need implement the interface IWindowReturnValue
or IWindowReturnValue<T>
which only contain a property named ReturnValue
, at a proper place set the value to this property. Here is the code:
public class ReturnValueTestWindowViewModel : BindableBase, IWindowReturnValue<Friend>
{
...
public void SetReturnValue()
{
ReturnValue = SelectedFriend;
}
...
}
By using CloseWindowAction
, you can add the functionality to close the current Window for an element, like this:
<Button Content="Close the current window with confirmation">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<behaviors:CloseWindowAction ClosingCheckFunc="{Binding CheckBeforeCloseWindow}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
To handle window closing event, you can use ClosingWindowBehavior
, attach it to a Window like this:
<i:Interaction.Behaviors>
<behaviors:ClosingWindowBehavior ClosingCheckFunc="{Binding CheckBeforeCloseWindow}" />
</i:Interaction.Behaviors>
By binding ClosingCheckFunc
property to a function which type is Func<bool>
to indicate whether the window can be closed or not.
Sometimes, before closing the window, there is some logic in the view model needed to be executed, to do this, just follow the 2 steps:
Firstly, add the behavior EnableWindowCloseBehavior
to the window that needs to be closed from its view model like this:
<Window>
<i:Interaction.Behaviors>
<behavior:EnableWindowCloseBehavior />
</i:Interaction.Behaviors>
...
</Window>
Then, the corresponding view model should implement IClosable
which contains an Action
named CloseAction
:
public class CloseTestViewModel : BindableBase, IClosable
{
public Action CloseWindow { get; set; }
...
}
At a proper place, you could invoke CloseWindow
action to close the window:
CloseWindow?.Invoke(); // or just CloseWindow();
It's essential to get event parameter for some operations, drag and drop , for instance, we can get the dragging data by accessing the event parameter of DragOver
or Drop
event, which type is DragEventArgs
.
To get event parameter in view model, you can use InvokeCommandAction
or CallMethodAction
, both of their names are same as the corresponding action in Blend Behaviors. While, the difference is these two action can invoke a command or a method with the parameter of the event which invokes them by a EventTrigger
.
<Button Content="Get event argument(InvokeCommandAction)">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<behaviors:InvokeCommandAction Command="{Binding ShowEventArgumentCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Get event argument(CallMethodAction)">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<behaviors:CallMethodAction MethodName="ShowEventArgument" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
public ICommand ShowEventArgumentCommand => new RelayCommand<RoutedEventArgs>(ShowEventArgument);
public void ShowEventArgument(RoutedEventArgs e)
{
...
}
Tips:
For
InvokeCommandAction
, you can still set a value forCommandParameter
property for your purpose, without explicitly setting value for this property, event parameter will be used as the parameter in this action.
For
CallMethodAction
, the specified method should contain either no parameter or one parameter with the event parameter type.
For more info, you could dive into the demo project, here is a screenshot for the demo app: