In this article, I decided to continue to improve the project from the previous articles (part 1 and part 2). Changes to be made are shown below.
- Allocate dialog service into separate class library;
- Instantiate dialog service declaratively (in XAML) via dependency property;
- Some code clean up.
Class library for dialog Service
Dialog Service will be responsible to display a dialog Window. The UI design will depend on a view model of a dialog, but some properties and functionality should be provided regardless of any particular design. Since the dialog window belongs to the Service, the reference to the parent Window should be passed from the calling side. Defined Service objects consists of the following
- dialog window (view) – A container to hold particular UI design of a dialog;
- dialog view model – Base view model to inherit by any dialog view model;
- dialog Service – A class which define a piece of code to popup a dialog window;
- dialog Service interface – An interface for the Service to implement, to separate calling view model and Service implementation.
Let's start.
Step
Open WpfApplication1 solution attached to the article (you may download WpfApplicationResult.zip from part 2).
Open WpfApplication1 solution attached to the article (you may download WpfApplicationResult.zip from part 2).
Step 1
Add new class library project to the solution and name it WpfDialogService. This solution is targeted to .NET Framework 4 (but you can decide).
Add new class library project to the solution and name it WpfDialogService. This solution is targeted to .NET Framework 4 (but you can decide).
Step 2
Delete an empty Class1 from WpfDialogService project.
Delete an empty Class1 from WpfDialogService project.
Step 3
Add new item -> User control (WPF) -> give a name DialogView, make sure you added reference to System.Xaml assembly to compile the project.
Step 4
Replace UserControl base class with Window in DialogView markup and the code at the backend file to compile the project.
Replace UserControl base class with Window in DialogView markup and the code at the backend file to compile the project.
Step 5
Set Window properties in markup.
- WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight"
Replace Grid container with
- <ContentPresenter x:Name="ContentPresenter" Content="{Binding}"></ContentPresenter>
Final view is given below.
- <Window x:Class="WpfDialogService.DialogView"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="clr-namespace:WpfDialogService"
- mc:Ignorable="d"
- d:DesignHeight="300" d:DesignWidth="300"
- WindowStartupLocation="CenterOwner" SizeToContent="WidthAndHeight">
- <ContentPresenter x:Name="ContentPresenter" Content="{Binding}"></ContentPresenter>
- </Window>
Add a new class and give it the name DialogViewModelBase. Further, any view model for dialog views should inherit the base view model. Add a public property Owner of type Window and CloseDialog method, as shown.
- using System.Windows;
- namespace WpfDialogService
- {
- public class DialogViewModelBase
- {
- public Window Owner { get; set; }
- public void CloseDialog(Window view)
- {
- if (view != null)
- view.DialogResult = true;
- }
- }
- }
Add new class, give it the name DialogService and extract its interface IDialogService, as shown below.
- namespace WpfDialogService
- {
- public interface IDialogService
- {
- void ShowDialogModal(DialogViewModelBase vm);
- }
- }
- namespace WpfDialogService
- {
- public class DialogService : IDialogService
- {
- public void ShowDialogModal(DialogViewModelBase vm)
- {
- DialogView v = new DialogView();
- v.Owner = vm.Owner;
- v.DataContext = vm;
- v.ShowDialog();
- v.Owner = null;
- }
- }
- }
Step 9
Delete DialogFacade and DialogService subfolders from Dialogs folder in WpfApplication1 project. Remove all the references of the deleted items to build the project (XAML and the code at the backend).
Delete DialogFacade and DialogService subfolders from Dialogs folder in WpfApplication1 project. Remove all the references of the deleted items to build the project (XAML and the code at the backend).
Step 10
Add reference to WpfDialogService library into WpfApplication1.
Add reference to WpfDialogService library into WpfApplication1.
Step 11
Add DialogService property to MainWindowViewModel:
Add DialogService property to MainWindowViewModel:
- public IDialogService DialogService { get; set; }
It is a bit tricky to get DialogService instance initialized in XAML of the main Window. Let’s try to use a dependency property for this purpose. Take a look at the very first line in MainWindow.xaml:
- <Window x:Class="WpfApplication1.Views.MainWindow" ...>
- using System.Windows;
- using WpfApplication1.ViewModels;
- using WpfDialogService;
- namespace WpfApplication1.Views
- {
- public class MainWindowBase : Window
- {
- public IDialogService DialogService
- {
- get { return (IDialogService)GetValue(DialogServiceProperty); }
- set { SetValue(DialogServiceProperty, value); }
- }
- public static readonly DependencyProperty DialogServiceProperty =
- DependencyProperty.Register("DialogService", typeof(IDialogService), typeof(MainWindowBase), new PropertyMetadata(null, PropertyChangedCallback));
- static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- Window v = d as Window;
- if (v == null)
- return;
- MainWindowViewModel vm = v.DataContext as MainWindowViewModel;
- if (vm == null)
- return;
- IDialogService ds = e.NewValue as IDialogService;
- if (ds == null)
- return;
- vm.DialogService = ds;
- }
- }
- }
Add a global resource for DialogService to App.xaml: add "ds" namespace and a resource with key DialogDervice.
- <Application x:Class="WpfApplication1.App"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:viewModels="clr-namespace:WpfApplication1.ViewModels"
- xmlns:dialogYesNo="clr-namespace:WpfApplication1.Dialogs.DialogYesNo"
- xmlns:ds="clr-namespace:WpfDialogService;assembly=WpfDialogService"
- StartupUri="Views/MainWindow.xaml">
- <Application.Resources>
- <viewModels:ViewModelLocator x:Key="ViewModelLocator" />
- <ds:DialogService x:Key="DialogService" />
- <DataTemplate DataType="{x:Type dialogYesNo:DialogYesNoViewModel}">
- <dialogYesNo:DialogYesNoView></dialogYesNo:DialogYesNoView>
- </DataTemplate>
- </Application.Resources>
- </Application>
Now, we can change XAML of MainWindow. Let’s change the code at the backend first. It should look, as shown below.
- namespace WpfApplication1.Views
- {
- /// <summary>
- /// Interaction logic for MainWindow.xaml
- /// </summary>
- public partial class MainWindow : MainWindowBase
- {
- public MainWindow()
- {
- InitializeComponent();
- }
- }
- }
- <views:MainWindowBase x:Class="WpfApplication1.Views.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:views="clr-namespace:WpfApplication1.Views"
- Title="MainWindow" Height="350" Width="525"
- DataContext="{Binding MainWindowViewModel, Source={StaticResource ResourceKey=ViewModelLocator}}"
- DialogService="{Binding ., Source={StaticResource ResourceKey=DialogService}}">
- <Grid>
- <Button Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"
- Command="{Binding OpenDialogCommand}"
- CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
- </Grid>
- </views:MainWindowBase>
Step15
Take a look at DialogService dependency property [Step 12]. The callback method PropertyChangedCallback is defined. It helps to set a value of DialogService property in MainWindowViewModel.
Step 16
Make DialogYesNoViewModel is inherited from DialogViewModelBase, modify OnYesClicked and OnNoClicked methods to call CloseDialog method of the base class is shown below.
- ...
- private void OnYesClicked(object parameter)
- {
- this.YesClicked(this, EventArgs.Empty);
- CloseDialog(parameter as Window);
- }
- private void OnNoClicked(object parameter)
- {
- this.NoClicked(this, EventArgs.Empty);
- CloseDialog(parameter as Window);
- }
- ...
Add Message property to DialogYesNoViewModel, as shown below.
- public string Message { get; private set; }
- public DialogYesNoViewModel(string message)
- {
- Message = message;
- this.yesCommand = new RelayCommand(OnYesClicked);
- this.noCommand = new RelayCommand(OnNoClicked);
- }
Modify OnOpenDialog method in MainWindowViewModel to use DialogService property to open a dialog,
- private void OnOpenDialog(object parameter)
- {
- var vm = new Dialogs.DialogYesNo.DialogYesNoViewModel("Question");
- vm.YesClicked += OptionYes;
- vm.NoClicked += OptionNo;
- vm.Owner = parameter as Window;
- if (DialogService != null)
- DialogService.ShowDialogModal(vm);
- }
Thiruppathi RPosted May 12, 2017, 7:51 AM
Nice Article.Thanks to sharing....
Rafnas T PPosted May 9, 2017, 12:24 AM
Nice share, keep sharing...