Windows Phone 开发笔记 (2) Command
2012-04-30 21:25 F-sea 阅读(1418) 评论(3) 编辑 收藏 举报本文同步发表于 Windows Phone论坛 WPDEVN :http://www.wpdevn.com/showtopic-138.aspx
在上一篇开发笔记中我么聊了ViewModel 这一次我们来说说Command。 在VM中我们一些方法需要提供给view 使用。其中最简单的方法是直接暴露一个public 的方法由view(在xmal.cs) 中直接调用。如果我们想把这个方法绑定到页面(xaml) 中的事件则需要使用Command. 当然在xmal.cs 中也可以直接使用Command。
View 想使用Command 那么首先必须在VM 实现一个公开的Command属性。
在Windows Phone 的SDK 中 Command的实现必须继承与System.Windows.Input.ICommand接口: 在ICommand 中包含CanExecute 和Execute 方法和一个CanExecuteChanged 事件。
如果Command 是被绑定到页面上的事件中。那么系统在执行Command的时候将先执行 CanExecute 方法
如果方法返回true则执行Execute方法的方式来执行Command。在调用这两个方法的时候Command的参数均会被传入这两个方法。
同时我们建议是自己调用Command使用的话也建议遵循这样的方式。
在使用Command使用必然会增加代码量,当然也带来好处:
1、
可以直接绑定
2、
可以控制状态
现在我们先来Command如何实现。一般来说我在代码中实现一个基础的通用Command,已代理的方式在Command 构建的时候传入Execute 方法和CanExecute的具体逻辑。
代码如下:
using System; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace WeiFan.Utility.Command { public class DelegateCommand<T> : ICommand where T : class { Action<T> m_excuteAction; Func<T, bool> m_canExcunteFunc; public DelegateCommand(Action<T> excuteAction) { m_excuteAction = excuteAction; } public DelegateCommand(Action<T> excuteAction, Func<T, bool> canExcuteAction) { m_canExcunteFunc=canExcuteAction; m_excuteAction = excuteAction; } private bool m_isCanExecute = true; public bool IsCanExecute { get { return m_isCanExecute; } set { if (value != m_isCanExecute) { m_isCanExecute = value; FireCanExecuteChanged(); } } } private void FireCanExecuteChanged() { if (CanExecuteChanged !=null) CanExecuteChanged(this, new EventArgs()); } #region ICommand Members public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) { if (m_canExcunteFunc == null) { return IsCanExecute; } else { T args; args = parameter as T; IsCanExecute = m_canExcunteFunc(args); } return IsCanExecute; } public void Execute(object parameter) { if (m_excuteAction != null) { T args; args = parameter as T; m_excuteAction(args); } } #endregion } }
其中泛型T为Command 参数类型。
那么 我们在VM 中使用该Command 如下:
private DelegateCommand<string> m_clickImageCommand; public ICommand ClickImageCommand { get { return m_clickImageCommand; } }
注意这个Command 是调用的时候接受一个 string 类型的参数。 这个Command的CanExecute 永远返回True 也就是说无论什么时候 该Command 都可以执行。
接下来我们来聊聊Command 的参数
首先Command在使用的是有允许传入一个 Object 类型的参数。 如果我们的Command 只接收一个参数到好说 在泛型里面限定类型就OK 了。。。 如果要接受多个参数的话就很蛋疼了。。
在这里我选择采用 一个集合类型将多个参数打包成一个集合传入Command。
考虑到为了让其使用起来友好 又能方便绑定我选择该集合采用简直对的方式 集合中的每个元素包含一个key 和一个value . 元素可以使用index 来索引 可以使用key 来索引。
代码如下:
CommandArg:
[ContentProperty("Value")] public class CommandArg : DependencyObject, IEquatable<CommandArg> { public CommandArg(string key, Object value) { this.Key = key; this.Value = value; } public CommandArg() { } private const string KEY_CAN_NOT_NULL = "绑定参数的key不能为空"; #region DependencyProperty private static readonly DependencyProperty KeyProperty = DependencyProperty.Register("Key", typeof(string), typeof(CommandArg), new PropertyMetadata(new Random().Next().ToString(), KeyPropertyChanged)); private static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(Object), typeof(CommandArg), new PropertyMetadata(null)); private static void KeyPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { if (string.IsNullOrWhiteSpace(Convert.ToString(e.NewValue)) && string.IsNullOrEmpty(Convert.ToString(e.NewValue))) throw new Exception(KEY_CAN_NOT_NULL); } bool m_isSetKey = false; public string Key { get { return Convert.ToString(GetValue(KeyProperty)); } set { if (string.IsNullOrWhiteSpace(Convert.ToString(value))) { throw new ArgumentException(KEY_CAN_NOT_NULL); } else { if (m_isSetKey == false) { SetValue(KeyProperty, value); m_isSetKey = true; } else { throw new InvalidOperationException("KEY_HAS_BEEN_SET"); } } } } public Object Value { get { return GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } #endregion public override string ToString() { return string.Format("({0},{1})", this.Key, this.Value); } #region IEquatable接口实现 public bool Equals(CommandArg other) { if (other == null) return false; return string.Equals(Key, other.Key, StringComparison.InvariantCultureIgnoreCase) && Value == other.Value; } #endregion }
这是集合中单个元素对象
CommandArgs:
using System; using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using WeiFan.Utility.Command; namespace WeiFan.Utility.Command { public class CommandArgs: DependencyObjectCollection<CommandArg> { public CommandArgs(): base() { base.Clear(); base.CollectionChanged += new NotifyCollectionChangedEventHandler(CommandArgs_CollectionChanged); } void CommandArgs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems!=null) { foreach (CommandArg arg in e.NewItems) { CommandArg containArg = GetItemByKey(arg.Key); if (containArg != null && !containArg.Equals(arg)) { throw new Exception("The collection contains this key"); } } } } public new void SetValue(DependencyProperty dp, object value) { //System.Diagnostics.Debug.WriteLine("SetValue"); base.SetValue(dp, value); } public bool Contains(string key) { if (GetItemByKey(key) == null) { return false; } else { return true; } } public new void Add(CommandArg item) { if (GetItemByKey(item.Key) != null) { throw new Exception("The collection contains this key"); } else { base.Add(item); } } private CommandArg GetItemByKey(string key) { if (!String.IsNullOrEmpty(key) && !String.IsNullOrWhiteSpace(key)) { for (int i = 0; i < base.Count; i++) { CommandArg arg = base[i]; if (arg.Key == key) { return arg; } } } return null; } /// <summary> /// 索引器 /// </summary> /// <param name="key">Key</param> /// <returns></returns> public CommandArg this[string key] { get { return GetItemByKey(key); } set { CommandArg arg = GetItemByKey(key); if (arg == null) { throw new NullReferenceException(); } else { arg = value; } } } } }
这是参数集合
好了。 接下来的事情我们就来看下 VM 中的Command 如何使用了
先看简单的吧 xaml.cs 中使用:
if (m_statusVM.LoadComand.CanExecute(null)) { m_statusVM.LoadComand.Execute(null); }
这里该是什么参数就在CanExecute 和 Execute 里面传什么参数
如果想在xaml 中绑定command 的话 需要使用Trigger 将Command和控件事件绑定起来:
代码如下:
<Image x:Name="imgMyImage" VerticalAlignment="Top" Width="75" Height="75" Source="{Binding Path=Status.User.ProfileImageUrl}" > <i:Interaction.Triggers> <i:EventTrigger EventName="Tap"> <i:InvokeCommandAction Command="{Binding Path=ClickUserCommand}"></i:InvokeCommandAction> </i:EventTrigger> </i:Interaction.Triggers> </Image>
在这里 当Image 的Tap 事件触发的时候 调用 ClickUserCommand 事件 这个Command 没有参数传入。
注意 i是:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
工程需要引用 Microsoft.Expression.Interactions
如果需要传入参数 那么代码如下:
<Image x:Name="imgMyImage" VerticalAlignment="Top" Width="75" Height="75" Source="{Binding Path=Status.User.ProfileImageUrl}" > <i:Interaction.Triggers> <i:EventTrigger EventName="Tap"> <i:InvokeCommandAction Command="{Binding Path=ClickUserCommand}"> <i:InvokeCommandAction.CommandParameter> <Command:CommandArgs> <Command:CommandArg Value="sdf" Key="1"></Command:CommandArg> <Command:CommandArg Value="sdf" Key="2"></Command:CommandArg> </Command:CommandArgs> </i:InvokeCommandAction.CommandParameter> </i:InvokeCommandAction> </i:EventTrigger> </i:Interaction.Triggers> </Image>
注意这里的Command: 的定义是
xmlns:Command="clr-namespace:WeiFan.Utility.Command;assembly=WeiFan.Utility" 该namespace是DelegateCommand 、CommandArg 和 ComandArgs 的定义所在位置
这里我们想ClickUserCommand 中 CommandArgs 集合对象 集合中有两个CommanArg元素 key 分别为1 和2 .
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步