Silverlight: VM、MVVM 的简单弹出窗口行为






4.97/5 (16投票s)
一个简单的弹出行为,关闭时运行一些 ICommands
由 Haruhiro Isowa 创建,Michael Washington 协助的 Behavior。
引言
本文将介绍如何使用我的弹出窗口行为,该行为将启动一个简单的是/否Popup
,并解释其工作原理,以便您可以根据需要进行调整。这个Popup
应该通过为您生成Popup
来简化 MVVM / VM 样式的Popup
创建,从而节省您为Popup
创建视图和视图模型的时间,并消除了其他解决方案中所示的复杂性(例如使用消息传递/服务等)。这还允许设计人员在等待程序员为他们创建视图和VM时对Popup
进行一些控制。
HisowaSimplePopUpBehavior 的作用
这是它生成的Popup
,但您可以通过修改代码或通过行为的属性来自定义。
这些是属性。(杂项下的所有属性都是可绑定的)。
Brush
用于设置Button
和PopUp
窗口的背景,渐变、图像等都可以。CustomParameter
:object
,您将其传递给行为,当PopUp
关闭时,它将返回给您。您可以传递选定的对象或 ID 等。PopUpMessage
:string
,设置PopUp
的消息PopUpNoMessage
:string
,设置否按钮上的文本PopUpYesMessage
:string
,设置是按钮上的文本ReturnDialogResult
:bool?
,公开PopUp
的结果(您可以使用双向绑定将结果传回。我用它来设置checkbox
上的IsChecked
)。ReturnICommand
:ICommand
,当PopUp
关闭时运行的命令。它将传递一个dataholder
类HisowaSimplePopUpBehaviorResult
,该类包含DialogResult (bool?), Popup
的DataContext
(在此 Popup Behavior 版本中可能不会使用),以及InputParameter
,即您在上面设置的CustomParameter
。
通过行为的属性,您大概可以猜到会发生什么。这个Behavior
将生成一个简单的Popup
,您可以设置文本、背景值;传入一个参数(可能用于放置相关对象);允许您绑定PopUp
的DialogResult
;最重要的是,当PopUp
关闭时运行一个ICommand
,并将DialogResult
、DataContext
以及您传入的参数传递给您,以便您的ICommand
可以消费它。
使用我的 HisowaSimplePopUpBehavior
首先打开一个新的 Silverlight 应用程序。将其命名为SimplePopUpBehaviorSample
。将我的HisowaSimplePopUpBehavior
类添加到您的解决方案中。添加对System.Windows.Interactivity
和System.Windows.Controls
的引用。System.Windows.Interactivty
在Blend SDK中。构建您的项目,然后在Expression Blend中打开您的MainPage.xaml。
在我们开始之前,我们需要有一个视图和视图模型。在Blend中删除MainPage.xaml。通过右键单击项目并选择添加新项来向项目添加一个新项。在对话框中,设置带视图模型的用户控件,将文件命名为MainPage.xaml。
打开MainPage.xaml。
将一个按钮拖到页面上。然后查找我的行为HisowaSimplePopUpBehavior
并将其拖到Button
上。
.
现在构建并在Blend中运行它。您现在应该会看到一个简单的Popup
。此时,它还没有做什么特别的事情。
ViewModel 代码
我们需要在ViewModel
中设置一些代码。但在我们开始之前,我们将使用 John Papa 的DelegateCommand.cs文件。从John Papa 的 Delegate Command获取。
将MainPageModel.cs代码替换为以下内容
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Input;
using HisowaPopUpBehaviors;
namespace SimplePopUpBehaviorSample
{
public class MainPageModel : INotifyPropertyChanged
{
public MainPageModel()
{
PopupSimpleResultCommand = new DelegateCommand
(PopupSimpleResult, CanPopupSimpleResult);
}
#region PopupSimpleResultCommand
public ICommand PopupSimpleResultCommand { get; set; }
public void PopupSimpleResult(object param)
{
//cast to my DataContainer
HisowaSimplePopUpBehaviorResult _result =
(HisowaSimplePopUpBehaviorResult)param;
//get the dialogResult
PopUpResult = _result.DialogResult;
//get the input if exists
if (_result.InputParameter != null)
{
Message = _result.InputParameter.ToString();
}
}
private bool CanPopupSimpleResult(object param)
{
return true;
}
#endregion
#region PopUpResult
private bool? _PopUpResult;
public bool? PopUpResult
{
get
{
return _PopUpResult;
}
set
{
_PopUpResult = value;
this.NotifyPropertyChanged("PopUpResult");
}
}
#endregion
#region Message
private string _Message;
public string Message
{
get { return _Message; }
private set
{
if (Message == value)
{
return;
}
_Message = value;
this.NotifyPropertyChanged("Message");
}
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
}
这是一个简单的VM,它有一个ICommand
,该命令将Popup
结果设置为属性(string Message, bool? PopupResult
)。它还实现了INotifyPropertyChanged
。
*注意:如果您使用的项目名称不同,您可能需要修复命名空间。
现在构建,然后回到Blend。
使用行为 part2,附带属性
现在我们已经完成了所有基础工作,我们可以开始充分利用HisowaSimplePopUpBehavior
了。
首先,我们需要为此演示创建一个类似的 UI。
使用元素属性绑定绑定这些文本值。
PopUpMessage
: 您的消息Textbox
的Text
属性PopUpYes
/NoMessage
: 您的Textbox
的Text
属性CustomParameter
: 您的输入Textbox
的Text
属性(允许对象,可以是任何内容)
将ReturnDialogResult
绑定到Checkbox
的IsChecked
属性,双向绑定。
构建并运行。现在您应该能够更改TextBox
中的文本为您在textbox
中输入的内容。如果popup
是由是按钮关闭的,则CheckBox
应该被选中。
应该看起来像这样。
现在我们要设置ReturnICommand
,这是Behavior
最重要的部分。为此,将ReturnIcommand
数据绑定到PopUpSimpleResultCommand
并构建。
要检查它是否正常工作,请回到Visual Studio并在您绑定的ICommand
中的某个地方设置一个断点。然后在Visual Studio中调试。
请注意,Icommand
正在接收HisowaPopUpBehaviors
类,该类包含DialogResult
、InputParameter
(CustomParameter
)和DataContext
。您可以在您的ICommand
中利用这些。
我的行为解释
在本节中,我将解释行为的代码。其中大部分是依赖项属性,允许databinding
,所以我会跳过它们。
这是Invoke
方法,当触发器被触发时调用。
#region Invoke
//Shows popup on event trigger you set in designer
protected override void Invoke(object parameter)
{
ChildWindow _childWindow = new ChildWindow();
_grid = new Grid();
_grid.Width = 300;
_grid.Height = 100;
//width 3 cols
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
//height 3 rows
_grid.RowDefinitions.Add(new RowDefinition()
{ Height = new GridLength(1, GridUnitType.Star) });
_grid.RowDefinitions.Add(new RowDefinition());
_grid.RowDefinitions.Add(new RowDefinition());
//add message
TextBlock tbkMessage = new TextBlock();
tbkMessage.Text = PopUpMessage;
tbkMessage.SetValue(Grid.RowProperty, 0);
tbkMessage.SetValue(Grid.ColumnProperty, 0);
tbkMessage.SetValue(Grid.ColumnSpanProperty, 3);
_grid.Children.Add(tbkMessage);
//add Yes button
Button btnYes = new Button();
btnYes.Content = PopUpYesMessage;
btnYes.SetValue(Grid.ColumnProperty, 0);
btnYes.SetValue(Grid.RowProperty, 1);
btnYes.Name = "btnYes";
btnYes.Click += new RoutedEventHandler(btnYes_Click);
_grid.Children.Add(btnYes);
//add No Button
Button btnNo = new Button();
btnNo.Content = PopUpNoMessage;
btnNo.SetValue(Grid.ColumnProperty, 2);
btnNo.SetValue(Grid.RowProperty, 1);
btnNo.Name = "btnNo";
btnNo.Click += new RoutedEventHandler(btnNo_Click);
_grid.Children.Add(btnNo);
//style
_grid.Background = BackGroundColorParameter;
btnYes.Background = YesColorParameter;
btnNo.Background = NoColorParameter;
//Make it a PopUp
_childWindow.Content = _grid;
PopUp = _childWindow;
PopUp.Closing += new EventHandler(PopUp_Closing);
PopUp.Show();
}
#endregion
基本上,它会创建UI,设置事件处理程序,并分配行为中的属性,例如背景和消息。这是您需要调整以更改 UI 的地方。
现在是是/否按钮的事件处理代码。
#region BtnClicks
void btnNo_Click(object sender, RoutedEventArgs e)
{
RemoveButtonHandlers();
PopUp.DialogResult = false;
}
void btnYes_Click(object sender, RoutedEventArgs e)
{
RemoveButtonHandlers();
PopUp.DialogResult = true;
}
private void RemoveButtonHandlers()
{
Button btnYes = (Button)_grid.FindName("btnYes");
btnYes.Click -= new RoutedEventHandler(btnYes_Click);
Button btnNo = (Button)_grid.FindName("btnNo");
btnNo.Click -= new RoutedEventHandler(btnNo_Click);
}
#endregion
这会设置DialogResults
,同时关闭PopUp
,并移除事件处理程序。
现在是PopUpClosing
事件的代码
#region ChildwindowClosing
//Runs an Icommand when the Popup is closing
//you can change what it passes to Icommand to whatever you like
//be sure that your method can consume them
void PopUp_Closing(object sender, CancelEventArgs e)
{
PopUp.Closing -= new EventHandler(PopUp_Closing);
ReturnDialogResultCommand = PopUp.DialogResult;
if (ReturnICommand != null)
{
HisowaSimplePopUpBehaviorResult _result =
new HisowaSimplePopUpBehaviorResult();
_result.DialogResult = PopUp.DialogResult;
_result.DataContext = PopUp.DataContext;
_result.InputParameter = CustomParameter;
ReturnICommand.Execute(_result);
}
}
#endregion
这一切只是移除处理程序,从依赖项属性获取Icommand
,创建我的数据容器HisowaSimplePopUpBehaviorResult
,然后运行Icommand
并传递HisowaSimplePopUpBehaviorResult
。
最后,DataContainer HisowaSimplePopUpBehaviorResult
类,它在同一个文件中。
public class HisowaSimplePopUpBehaviorResult
{
public bool? DialogResult { get; set; }
public object DataContext { get; set; }
public object InputParameter { get; set; }
}
这只是一个简单的数据持有者。
完整代码
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.ComponentModel;
using System.Reflection;
namespace HisowaPopUpBehaviors
{
[System.ComponentModel.Description("Launches a Simple Popup on Event Trigger")]
public class HisowaSimplePopUpBehavior : TargetedTriggerAction<frameworkelement />,
INotifyPropertyChanged
{
private ChildWindow PopUp;
Grid _grid = new Grid();
//constructor
public HisowaSimplePopUpBehavior()
{ }
#region CustomParameterProperty
public static readonly DependencyProperty CustomParameterProperty =
DependencyProperty.Register("CustomParameter", typeof(object),
typeof(HisowaSimplePopUpBehavior), null);
public object CustomParameter
{
get
{
return base.GetValue(CustomParameterProperty);
}
set
{
base.SetValue(CustomParameterProperty, value);
}
}
#endregion
#region ReturnDialogResultCommandProperty
public static readonly DependencyProperty ReturnDialogResultCommandProperty =
DependencyProperty.Register("ReturnDialogResultCommand", typeof(bool?),
typeof(HisowaSimplePopUpBehavior), null);
public bool? ReturnDialogResultCommand
{
get
{
return (bool?)base.GetValue(ReturnDialogResultCommandProperty);
}
set
{
base.SetValue(ReturnDialogResultCommandProperty, value);
this.NotifyPropertyChanged("ReturnDialogResultCommand");
}
}
#endregion
#region ReturnICommandProperty
public static readonly DependencyProperty ReturnICommandProperty =
DependencyProperty.Register("ReturnICommand", typeof(ICommand),
typeof(HisowaSimplePopUpBehavior), null);
public ICommand ReturnICommand
{
get
{
return (ICommand)base.GetValue(ReturnICommandProperty);
}
set
{
base.SetValue(ReturnICommandProperty, value);
}
}
#endregion
#region PopUpMessageProperty
public static readonly DependencyProperty PopUpMessageProperty =
DependencyProperty.Register("PopUpMessage", typeof(string),
typeof(HisowaSimplePopUpBehavior), null);
public string PopUpMessage
{
get
{
if (base.GetValue(PopUpMessageProperty) == null)
{
return "Are you sure?";
}
else
{
return (string)base.GetValue(PopUpMessageProperty);
}
}
set
{
base.SetValue(PopUpMessageProperty, value);
}
}
#endregion
#region PopUpYesMessageProperty
public static readonly DependencyProperty PopUpYesMessageProperty =
DependencyProperty.Register("PopUpYesMessage", typeof(string),
typeof(HisowaSimplePopUpBehavior), null);
public string PopUpYesMessage
{
get
{
if (base.GetValue(PopUpYesMessageProperty) == null)
{
return "Yes";
}
else
{
return (string)base.GetValue(PopUpYesMessageProperty);
}
}
set
{
base.SetValue(PopUpYesMessageProperty, value);
}
}
#endregion
#region PopUpNoMessageProperty
public static readonly DependencyProperty PopUpNoMessageProperty =
DependencyProperty.Register("PopUpNoMessage", typeof(string),
typeof(HisowaSimplePopUpBehavior), null);
public string PopUpNoMessage
{
get
{
if (base.GetValue(PopUpNoMessageProperty) == null)
{
return "No";
}
else
{
return (string)base.GetValue(PopUpNoMessageProperty);
}
}
set
{
base.SetValue(PopUpNoMessageProperty, value);
}
}
#endregion
#region BackgroundColor
public static readonly DependencyProperty BackGroundColorParameterProperty =
DependencyProperty.Register("BackGroundColorParameter",
typeof(System.Windows.Media.Brush), typeof(HisowaSimplePopUpBehavior), null);
public System.Windows.Media.Brush BackGroundColorParameter
{
get
{
return (System.Windows.Media.Brush)base.GetValue
(BackGroundColorParameterProperty);
}
set
{
base.SetValue(BackGroundColorParameterProperty, value);
}
}
#endregion
#region YesColor
public static readonly DependencyProperty YesColorParameterProperty =
DependencyProperty.Register("YesColorParameter",
typeof(System.Windows.Media.Brush), typeof(HisowaSimplePopUpBehavior), null);
public System.Windows.Media.Brush YesColorParameter
{
get
{
return (System.Windows.Media.Brush)base.GetValue
(YesColorParameterProperty);
}
set
{
base.SetValue(YesColorParameterProperty, value);
}
}
#endregion
#region NoColor
public static readonly DependencyProperty NoColorParameterProperty =
DependencyProperty.Register("NoColorParameter",
typeof(System.Windows.Media.Brush), typeof(HisowaSimplePopUpBehavior), null);
public System.Windows.Media.Brush NoColorParameter
{
get
{
return
(System.Windows.Media.Brush)base.GetValue(NoColorParameterProperty);
}
set
{
base.SetValue(NoColorParameterProperty, value);
}
}
#endregion
#region Invoke
//Shows popup on event trigger you set in designer
protected override void Invoke(object parameter)
{
ChildWindow _childWindow = new ChildWindow();
_grid = new Grid();
_grid.Width = 300;
_grid.Height = 100;
//width 3 cols
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition()
{ Width = new GridLength(1, GridUnitType.Star) });
//height 3 rows
_grid.RowDefinitions.Add(new RowDefinition()
{ Height = new GridLength(1, GridUnitType.Star) });
_grid.RowDefinitions.Add(new RowDefinition());
_grid.RowDefinitions.Add(new RowDefinition());
//add message
TextBlock tbkMessage = new TextBlock();
tbkMessage.Text = PopUpMessage;
tbkMessage.SetValue(Grid.RowProperty, 0);
tbkMessage.SetValue(Grid.ColumnProperty, 0);
tbkMessage.SetValue(Grid.ColumnSpanProperty, 3);
_grid.Children.Add(tbkMessage);
//add Yes button
Button btnYes = new Button();
btnYes.Content = PopUpYesMessage;
btnYes.SetValue(Grid.ColumnProperty, 0);
btnYes.SetValue(Grid.RowProperty, 1);
btnYes.Name = "btnYes";
btnYes.Click += new RoutedEventHandler(btnYes_Click);
_grid.Children.Add(btnYes);
//add No Button
Button btnNo = new Button();
btnNo.Content = PopUpNoMessage;
btnNo.SetValue(Grid.ColumnProperty, 2);
btnNo.SetValue(Grid.RowProperty, 1);
btnNo.Name = "btnNo";
btnNo.Click += new RoutedEventHandler(btnNo_Click);
_grid.Children.Add(btnNo);
//style
_grid.Background = BackGroundColorParameter;
btnYes.Background = YesColorParameter;
btnNo.Background = NoColorParameter;
//Make it a PopUp
_childWindow.Content = _grid;
PopUp = _childWindow;
PopUp.Closing += new EventHandler(PopUp_Closing);
PopUp.Show();
}
#endregion
#region BtnClicks
void btnNo_Click(object sender, RoutedEventArgs e)
{
RemoveButtonHandlers();
PopUp.DialogResult = false;
}
void btnYes_Click(object sender, RoutedEventArgs e)
{
RemoveButtonHandlers();
PopUp.DialogResult = true;
}
private void RemoveButtonHandlers()
{
Button btnYes = (Button)_grid.FindName("btnYes");
btnYes.Click -= new RoutedEventHandler(btnYes_Click);
Button btnNo = (Button)_grid.FindName("btnNo");
btnNo.Click -= new RoutedEventHandler(btnNo_Click);
}
#endregion
#region ChildwindowClosing
//Runs an Icommand when the Popup is closing
//you can change what it passes to Icommand to whatever you like
//be sure that your method can consume them
void PopUp_Closing(object sender, CancelEventArgs e)
{
PopUp.Closing -= new EventHandler(PopUp_Closing);
ReturnDialogResultCommand = PopUp.DialogResult;
if (ReturnICommand != null)
{
HisowaSimplePopUpBehaviorResult _result =
new HisowaSimplePopUpBehaviorResult();
_result.DialogResult = PopUp.DialogResult;
_result.DataContext = PopUp.DataContext;
_result.InputParameter = CustomParameter;
ReturnICommand.Execute(_result);
}
}
#endregion
// Utility
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
public class HisowaSimplePopUpBehaviorResult
{
public bool? DialogResult { get; set; }
public object DataContext { get; set; }
public object InputParameter { get; set; }
}
}
另请参阅: HisowaModPopUpBehavior
此变体允许您使用自己的视图和视图模型并将其注入popup
。