Following steps in this order is not needed. It is created for understanding what functionality that we are achieving at the end and what it is replacing and what it is buying us.
You can directly go to step three where we implement using prism, mvvm, interactivity.
Step One (from code behind by handling click event, no prism no mvvm yet)
In the first step let us just open a message box from withing code behind by implementing button click event handler (if from from design view we double click on the button, it will provide the skeleton button click event handler method).
<Button Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="92,84,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75"
Click="button1_Click"
/>
// In the code behind
private void
button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello
World");
}
Well, the message box here is just for the sake of example, let's get rid of that and replace with a window. We then well open that window from code behind again.
Now let's create a new window called MessageWindow.xaml which we will bring up as dialog instead of message box.
<Window x:Class="WpfApplicationDialogExmaple.MessageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MessageWindow" mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="303" d:DesignWidth="423"
Width="480" Height="305">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Height="47" HorizontalAlignment="Center" Name="textBlock1" VerticalAlignment="Top" Width="290" >
Enter
something for goodness sake:
</TextBlock>
<TextBox Grid.Row="2" Height="32" HorizontalAlignment="Center" Name="textBox1" VerticalAlignment="Top" Width="289" />
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="OK" Height="23" HorizontalAlignment="Left" Name="btnOK" VerticalAlignment="Top" Width="75" Click="btnOK_Click"
/>
<Button Content="Cancel" Height="23" HorizontalAlignment="Center" Name="btnCancel" VerticalAlignment="Top" Width="75" Click="btnCancel_Click"
/>
</StackPanel>
</Grid>
</Window>
// and in code behind we process the button click event like before. Notice these are just to display dialog boxes in straight forward simple way and not yet Prism or MVVM way. In step two and three we will do that.
public partial class MessageWindow : Window
{
public
MessageWindow()
{
InitializeComponent();
}
public string Message { get;
set; }
private
void btnOK_Click(object
sender, RoutedEventArgs e)
{
Message = this.textBox1.Text;
this.Close();
}
private
void btnCancel_Click(object
sender, RoutedEventArgs e)
{
Message = string.Empty;
this.Close();
}
}
// and in the code behnind of the main window we simple bring up MessageWindow as dialog.
public partial class MainWindow : Window
{
public
MainWindow()
{
InitializeComponent();
}
private
void button1_Click(object
sender, RoutedEventArgs e)
{
//
MessageBox.Show("Hello World");
MessageWindow
mess = new MessageWindow();
mess.ShowDialog();
textBlock1.Text = mess.Message;
}
}
Second Step (from code behind using mvvm, however no prism yet)
Now let's use ViewModel. Just created a MessageViewModel as follows and plugged in this view model for data binding for our two windows (main window and dialog window) as their data context.
public class MessageViewModel
: INotifyPropertyChanged
{
private
string _message;
public event PropertyChangedEventHandler
PropertyChanged;
private
static MessageViewModel
_instance = new MessageViewModel();
private
MessageViewModel()
{
}
public static MessageViewModel
Instance
{
get
{ return _instance; }
}
public string Message {
get
{ return _message; }
set
{
if
(_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
private
void OnPropertyChanged(string
property)
{
if
(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
After this in code behind of main window and message window we added data context.
public MainWindow()
{
InitializeComponent();
this.DataContext
= MessageViewModel.Instance;
}
And also for message window
public MessageWindow()
{
InitializeComponent();
this.DataContext
= MessageViewModel.Instance;
}
In main window XAML, defined path for binding to Message property of the view model.
<TextBlock Height="138" HorizontalAlignment="Left" Margin="98,132,0,0" Name="textBlock1" VerticalAlignment="Top" Width="321" Text="{Binding Path=Message}" />
Up to this it is still using code behind, however at this point (in step 2) it is using view model.In step three we'll start using PRISM.
Step Three - A (using prism and mvvm)
First of all assuming we have prism librraies downloaded. We need to add reference to prism dll.
We'll use "command" from prism instead of handing click event from code behind. For this purpose besides adding the prism library reference, we'll add prism namespace in xaml and hook up to a command named DisplayDialogCommand which we'll define in out view model. Therefore the xaml of the main window will look like the following now.
<Window x:Class="WpfApplicationDialogExmaple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prismCmd="clr-namespace:Microsoft.Practices.Prism.Commands;assembly=Microsoft.Practices.Prism"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="92,84,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75" prismCmd:Click.Command="{Binding DisplayDialogCommand}" />
<TextBlock Height="138" HorizontalAlignment="Left" Margin="98,132,0,0" Name="textBlock1" VerticalAlignment="Top" Width="321" Text="{Binding Path=Message}" />
</Grid>
</Window>
So the view model code has been updated to add this new command definition (DisplayDialogCommand) as follows. We are taking advantage of DelegateCommand type which is part of prism library.
public class MessageViewModel : INotifyPropertyChanged
{
private string
_message;
public event PropertyChangedEventHandler PropertyChanged;
private static MessageViewModel _instance = new MessageViewModel();
private MessageViewModel()
{
DisplayDialogCommand = new
DelegateCommand<string>(OnDisplayDialogCommand);
}
public static MessageViewModel Instance
{
get { return
_instance; }
}
public string Message
{
get { return
_message; }
set
{
if (_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
public ICommand
DisplayDialogCommand { get; set; }
private void
OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
private void
OnDisplayDialogCommand(string args)
{
MessageBox.Show("Hello
Again, not intented but in progress ....");
// TO BE REPLACED in STEP THREE B
// TO BE REPLACED in STEP THREE B
// almost there, quite not there yet, because we
do not want to do this, i.e
// open ui/view from within view-model.. so
follow next steps ..
}
}
Step Three - B (continuation of Step Three)
In this step we'll replace the implementation of OnDisplaDialogCommand method above so that it creates and open up dialog window through prism's interactivity request, so that view model directly does not create any view and fully decoupled from creating view.
In order to implement this we'll use interactivity library from prism. Therefore add reference to prism interactivity dll as below.
Now let us add the following namespaces in main window xaml and add skeleton for interactivity triggers.
xmlns:prismInter="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
and in the body of xaml
<i:Interaction.Triggers>
</i:Interaction.Triggers>
</i:Interaction.Triggers>
[Little gripe session : sadly PopupChildWindowAction is only in Silverlight!!]
Therefore, we can create a "custom trigger action". TriggerAction is an action that is executed upon trigger being fired.
http://msdn.microsoft.com/en-us/library/ff725477%28v=expression.40%29.aspx
Here is how to create TriggerAction and following this skeleton, we'll create our trigger action. We'll like to keep as simple as possible because seeing the steps and how everything is working together is the objective here.
public class PopupAction : TriggerAction<DependencyObject> { public PopupAction() { // Insert code required for object creation below this point. } protected override void Invoke(object o) { // Insert code that defines what the Action will do when it is triggered or invoked. } }
Following were done.
Created a PopupWindow.xaml where PopupWindow is inherited from Window class and will host any user control ( as well it will clear this upon close).
Created a sample PopupUserControl.xaml so that we can display this onto PopupWindow.
Finally implemented the "Invoke" method that is mentioned above (the method to override from TriggerAction class).
public class PopupAction
: TriggerAction<DependencyObject>
{
public
PopupAction() { }
public UserControl PopupUserControl { get; set; }
protected
override void
Invoke(object parameter)
{
var
args = parameter as InteractionRequestedEventArgs;
if(args
!= null)
{
Action
callback = args.Callback;
Notification
notification = args.Context;
if
(notification != null)
{
PopupWindow
popupView = new PopupWindow
{ Title = notification.Title };
popupView.DataContext = notification;
popupView.DataContext = notification;
popupView.Height =
PopupUserControl.Height;
popupView.Width =
PopupUserControl.Width;
popupView.Content =
PopupUserControl;
popupView.ShowDialog();
EventHandler
handler = null;
handler = (o, e) =>
{
popupView.Closed -=
handler;
callback();
};
popupView.Closed +=
handler;
}
}
}
}
The above PopupAction will basically execute upon trigger event that would be raised from view model to notify the view that a popup is needed.
<Window x:Class="WpfApplicationDialogExmaple.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prismCmd="clr-namespace:Microsoft.Practices.Prism.Commands;assembly=Microsoft.Practices.Prism"
xmlns:prismInter="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApplicationDialogExmaple"
Title="MainWindow" Height="350" Width="525">
<Grid>
<i:Interaction.Triggers>
<prismInter:InteractionRequestTrigger SourceObject="{Binding PromptUserRequest}">
<local:PopupAction>
<local:PopupAction.PopupUserControl>
<local:PopupUserControl></local:PopupUserControl>
</local:PopupAction.PopupUserControl>
</local:PopupAction>
</prismInter:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Button Content="Button"
Height="23"
HorizontalAlignment="Left"
Margin="92,84,0,0"
Name="button1"
VerticalAlignment="Top"
Width="75"
prismCmd:Click.Command="{Binding
DisplayDialogCommand}">
</Button>
<TextBlock Height="138" HorizontalAlignment="Left" Margin="98,132,0,0" Name="textBlock1" VerticalAlignment="Top" Width="321" Text="{Binding Path=Message}" />
</Grid>
</Window>
And of course the PopupUserControl xaml will have now the TextBlock Text property bound to notification's Content.
<TextBlock Grid.Row="1" Height="47" HorizontalAlignment="Center" Name="textBlock1" VerticalAlignment="Top" Width="290" Text="{Binding Path=Content}">
And the view model looks like this now.
public class MessageViewModel
: INotifyPropertyChanged
{
private
string _message;
public event PropertyChangedEventHandler
PropertyChanged;
private
static MessageViewModel
_instance = new MessageViewModel();
private
MessageViewModel()
{
DisplayDialogCommand = new DelegateCommand<string>(OnDisplayDialogCommand);
PromptUserRequest = new InteractionRequest<Confirmation>();
}
public static MessageViewModel
Instance
{
get
{ return _instance; }
}
public string Message {
get
{ return _message; }
set
{
if
(_message != value)
{
_message = value;
OnPropertyChanged("Message");
}
}
}
public ICommand DisplayDialogCommand { get; set; }
public InteractionRequest<Confirmation>
PromptUserRequest { get; private set; }
private
void OnPropertyChanged(string
property)
{
if
(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
{
this.PromptUserRequest.Raise(new Confirmation {
Content = "Hello Wrold from prism based
popup", Title = "Popup from
prism" }, null);
}
}
Do you have a source code for the MVVM dialog part? I've tried implementing the code here but I get errors.
ReplyDeleteThanks.
Got it to work. Now how do you handle when OK or Cancel button is clicked?
ReplyDeleteJust use WPF command.
Delete