WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。

时间:2022-06-23 01:21:18

做过WPF开发的人,都知道做MVVM架构,最麻烦的是Event的绑定,因为Event是不能被绑定的,同时现有的条件下,命令是无法替代Event。而在开发过程中无法避免Event事件,这样MVVM的架构就不能完全实现了。

所以后来微软提供了一个折中的方案,使用Trigger触发器和System.Windows.Interactivity结合通过事件绑定事件,个人觉得这个方法也挺可以的。

还有Prism框架方法,我看过但是这个方法比较繁琐可用性不高。

后来通过搜索和自己研究知道了一个解决方案,可以绑定所有事件,这个是原始版本,我个人正在研究一个完全的版本,希望能完全支持Event属性。

对于编程学习喜欢的方式,我个人是这个看法,我做开发也有一年多了,WPF开发也有九个月了。从小白到现在入门略有深入。不再局限于实现效果,而是更喜欢实现功能的原汁原味的代码。很多效果也许我不能写出完整的代码,也没有那么多的时间,但是深入看了别人的设计代码,总是受益良多。

工作这么长时间,总算明白为什么Java那么受欢迎了。因为作为技术人员到了一定境界,更喜欢了解具体的实现过程,而对于直接使用SDK的兴趣淡了不少。打个比方微软提供了很多优秀的SDK,但是就像家庭一样,越是包办一切的好父母,往往不太招孩子们的喜欢,不是孩子们不懂得感恩,而是社会自然规律,你无法成长,你便很容易感觉到自己无能,无能的人哪有快乐而言。

正文:具体的类图 通过类图可以看到结构相当清晰,它们的依赖关系是单向的。是属于低耦合的关系

WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。

 
现在提供各个类的代码,从上到下,层次从左到右。注意实际上建立类的顺序是相反的,这里只是为了展示方便
 
Events.cs
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data; namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
/// <summary>
/// 这个类的作用是真正的绑定命令
/// 将命令绑定到对应的控件上
/// </summary>
public class Events
{
/// <summary>
/// 事件行为属性
/// 注册属性名
/// </summary>
private static readonly DependencyProperty EventBehaviorsProperty = DependencyProperty.RegisterAttached("EventBehaviors",typeof(EventBehaviorCollection),typeof(Control),null); private static readonly DependencyProperty InternalDataContextProperty = DependencyProperty.RegisterAttached("InternalDataContext", typeof(Object), typeof(Control), new PropertyMetadata(null, DataContextChanged)); public static readonly DependencyProperty CommandsProperty =
DependencyProperty.RegisterAttached("Commands", typeof(EventCommandCollection), typeof(Events), new PropertyMetadata(null, CommandsChanged)); private static void DataContextChanged(DependencyObject dependencyObject,DependencyPropertyChangedEventArgs e)
{
var target = dependencyObject as Control;
if (target == null) return;
foreach (var behavior in GetOrCreateBehavior(target))
{
behavior.Bind();
}
} public static EventCommandCollection GetCommands(DependencyObject dependencyObject)
{
return dependencyObject.GetValue(CommandsProperty) as EventCommandCollection;
} public static void SetCommands(DependencyObject dependencyObject,EventCommandCollection eventCommands)
{
dependencyObject.SetValue(CommandsProperty,eventCommands);
} private static void CommandsChanged(DependencyObject dependencyObject,DependencyPropertyChangedEventArgs e)
{
var target = dependencyObject as Control;
if (target == null) return;
var behaviors = GetOrCreateBehavior(target);
foreach (var eventCommand in e.NewValue as EventCommandCollection)
{
var behavior = new EventBehavior(target);
behavior.Bind(eventCommand);
behaviors.Add(behavior);
}
} private static EventBehaviorCollection GetOrCreateBehavior(FrameworkElement target)
{
var behavior = target.GetValue(EventBehaviorsProperty) as EventBehaviorCollection;
if (behavior == null)
{
behavior = new EventBehaviorCollection();
target.SetValue(EventBehaviorsProperty,behavior);
target.SetBinding(InternalDataContextProperty,new Binding());
}
return behavior;
} }
}

EventBehaviorCollection

using System;
using System.Collections.Generic; namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
/// <summary>
/// 事件名称集合
/// </summary>
public class EventCommandCollection : List<EventCommand>
{ }
}

EventBehavior

using System;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media; namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
/// <summary>
/// 事件行为
/// 这个类的作用是将,通过反射将事件名,实例化一个真正的事件对象
/// </summary>
public class EventBehavior : CommandBehaviorBase<Control>
{
private EventCommand _bindingInfo; public EventBehavior(Control control) : base(control) { } public void Bind(EventCommand bindingInfo)
{
ValidateBindingInfo(bindingInfo);
_bindingInfo = bindingInfo;
Bind();
} private void ValidateBindingInfo(EventCommand bindingInfo)
{
if (bindingInfo == null) throw new ArgumentException("bindingInfo is Null");
if (string.IsNullOrEmpty(bindingInfo.CommandName)) throw new ArgumentException("bindingInfo.CommandName is Null");
if (string.IsNullOrEmpty(bindingInfo.EventName)) throw new ArgumentException("bindingInfo.EventName is Null");
} public void Bind() {
ValidateBindingInfo(_bindingInfo);
HookPropertyChanged();
HookEvent();
SetCommand();
} public void HookPropertyChanged() {
var dataContext = this.TargetObject.DataContext as INotifyPropertyChanged;
if (dataContext == null) return;
dataContext.PropertyChanged -= this.DataContextPropertyChanged;
dataContext.PropertyChanged += this.DataContextPropertyChanged;
} private void DataContextPropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (e.PropertyName == _bindingInfo.CommandName)
this.SetCommand();
} private void SetCommand()
{
var dataContext = this.TargetObject.DataContext;
if (dataContext == null) return;
var propertyInfo = dataContext.GetType().GetProperty(_bindingInfo.CommandName);
if (propertyInfo == null) throw new ArgumentException("propertyInfo is null,commandName is error");
this.Command = propertyInfo.GetValue(dataContext,null) as ICommand;
}
/// <summary>
/// 钩事件
/// 有助于绑定AttachedProperty属性
/// </summary>
private void HookEvent()
{
var eventNames = _bindingInfo.EventName.Split('.');
int length = eventNames.Length;
//分开来,目的是为了提高效率
if (length == )
{
var eventInfo = this.TargetObject.GetType().GetEvent(_bindingInfo.EventName, BindingFlags.Public | BindingFlags.Instance);
if (eventInfo == null) throw new ArgumentException("eventInfo is Null,eventName is error");
eventInfo.RemoveEventHandler(this.TargetObject, this.GetEventMethod(eventInfo));
eventInfo.AddEventHandler(this.TargetObject, GetEventMethod(eventInfo));
}
else
{
//TargetObject控件加载完成后触发执行
this.TargetObject.Initialized += (sender, e) =>
{
var tempObject = SelectVisual(eventNames, this.TargetObject);
var tempEventInfo = tempObject.GetType().GetEvent(eventNames[length-], BindingFlags.Public | BindingFlags.Instance);
if (tempEventInfo == null) throw new ArgumentException("eventInfo is Null,eventName is error");
tempEventInfo.RemoveEventHandler(tempObject, this.GetEventMethod(tempEventInfo));
tempEventInfo.AddEventHandler(tempObject, GetEventMethod(tempEventInfo));
};
}
} #region 筛选内部控件 /// <summary>
/// 筛选Visual
/// </summary>
/// <param name="names"></param>
/// <param name="obj"></param>
/// <returns></returns>
private static Visual SelectVisual(string[] names, Control obj)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
int act = assemblies.Length;
for (int i = ; i < act; i++)
{
if (assemblies[i].FullName.Split(',')[] == "PresentationFramework")
{
return SelectVisual(assemblies[i], names, , obj);
}
}
return null;
} /// <summary>
/// 命名空间名称集合
/// </summary>
static string[] namespaceNames = {"System.Windows.Controls.Primitives","System.Windows.Controls" };
/// <summary>
/// 筛选Visual
/// </summary>
/// <param name="assembly"></param>
/// <param name="names"></param>
/// <param name="i"></param>
/// <param name="obj"></param>
/// <returns></returns>
private static Visual SelectVisual(Assembly assembly, string[] names,int i,Visual obj)
{
Type type=null;
int tempLength=namespaceNames.Length;
for (int j = ; j < tempLength; j++)
{
type = assembly.GetType(string.Format("{0}.{1}",namespaceNames[j], names[i]));
if(type!=null)
{
break;
}
}
if (type == null)
{
return null;
}
obj = GetDescendantByType(obj, type);
if (names.Length == (i+))//判断是否到了数组有效的位置,因为为了偷懒没有对string[]进行去尾操作,因为string[]最后一个数据是事件名称,在这里是 无效的
{
return obj;
}
return SelectVisual(assembly, names, i+, obj);
} /// <summary>
/// 通过Type获取包含的控件
/// </summary>
/// <param name="element"></param>
/// <param name="type"></param>
/// <returns></returns>
private static Visual GetDescendantByType(Visual element, Type type)
{
if (element == null) return null; if (element.GetType() == type||element.GetType().BaseType==type) return element;
Visual foundElement = null;
if (element is FrameworkElement)
(element as FrameworkElement).ApplyTemplate();
int cnt = VisualTreeHelper.GetChildrenCount(element);
for (int i = ; i < cnt; i++)
{
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type);
if (foundElement != null)
break;
}
return foundElement;
} #endregion private Delegate _method;
private Delegate GetEventMethod(EventInfo eventInfo)
{
if (eventInfo == null) throw new ArgumentException("eventInfo is null");
if (eventInfo.EventHandlerType == null) throw new ArgumentException("EventHandlerType is null");
if (this._method == null)
{
this._method = Delegate.CreateDelegate(
eventInfo.EventHandlerType,this,GetType().GetMethod("OnEventRaised",BindingFlags.NonPublic|BindingFlags.Instance));
}
return _method;
} private void OnEventRaised(object sender,EventArgs e)
{
this.ExecuteCommand();
} }
}

EventCommandCollection

using System;
using System.Collections.Generic; namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
/// <summary>
/// 事件名称集合
/// </summary>
public class EventCommandCollection : List<EventCommand>
{ }
}

CommandBehaviorBase

using System;
using System.Windows.Controls;
using System.Windows.Input; namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
/// <summary>
/// 基于行为的处理连接的一个命令
/// 这个类可以用来提供新的行为,比如按钮的Clik事件行为
/// 鼠标进入控件的事件行为
/// </summary>
/// <typeparam name="T"></typeparam>
public class CommandBehaviorBase<T> where T : Control
{
/// <summary>
/// 定义的一个命令
/// </summary>
private ICommand command;
/// <summary>
/// 命令传值的内容
/// </summary>
private object commandParameter;
/// <summary>
/// 弱引用对象
/// 即对象被引用的情况下,允许垃圾回收来回收对象
/// </summary>
private readonly WeakReference targetObject;
/// <summary>
/// 命令中允许事件发生的事件的方法,
/// 即允许事件发生的条件
/// </summary>
private readonly EventHandler commandCanExecuteChangedHandler; #region 属性
/// <summary>
/// 相应的命令被执行和监控
/// </summary>
public ICommand Command
{
get { return this.command; }
set {
if (this.command != null)
{
this.command.CanExecuteChanged -= this.commandCanExecuteChangedHandler;
}
this.command = value;
if (this.command != null)
{
this.command.CanExecuteChanged += this.commandCanExecuteChangedHandler;
this.UpdateEnabledState();
}
}
}
/// <summary>
/// 命令中传值的内容
/// </summary>
public object CommandParameter
{
get { return this.commandParameter; }
set { if (this.commandParameter != value) {
this.commandParameter = value;
this.UpdateEnabledState();
} }
}
/// <summary>
/// 一个弱引用的对象
/// </summary>
protected T TargetObject
{
get {
return targetObject.Target as T;
}
} #endregion /// <summary>
/// 构造函数
/// </summary>
/// <param name="targetObject">行为上的目标对象</param>
public CommandBehaviorBase(T targetObject)
{
this.targetObject = new WeakReference(targetObject);
this.commandCanExecuteChangedHandler = new EventHandler(this.CommandCanExecuteChanged);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CommandCanExecuteChanged(object sender,EventArgs e)
{
this.UpdateEnabledState();
} /// <summary>
/// 基于属性的命令的执行,来更新的目标对象。
/// </summary>
protected virtual void UpdateEnabledState()
{
if (this.TargetObject == null)
{
this.Command = null;
this.CommandParameter = null;
}else if(this.Command!=null)
{
this.TargetObject.IsEnabled = this.Command.CanExecute(this.CommandParameter);
}
}
/// <summary>
/// 执行命令
/// </summary>
protected virtual void ExecuteCommand()
{
if (this.Command != null)
{
this.Command.Execute(this.CommandParameter);
}
} }
}

EventCommand

using System;

namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
public class EventCommand
{
/// <summary>
/// 命令名称
/// 这个可以自定义,比如myCommandClick等
/// </summary>
public string CommandName { get; set; }
/// <summary>
/// 特定的事件名称
/// 首先要确认绑定的控件支持这个事件
/// 比如Click MouseOver 等
/// </summary>
public string EventName { get; set; } }
}

现在完整的框架已经搭建好了,现在就是做一个Demo就可以了。

用命令绑定事件,就要考虑到类似TextBoxBase.TextChanged命令的问题,理解这个的前提是了解Attached Property,Attached Property是DependencyObject的一种特殊形式,它利用DependencyProperty.RegisterAttached方法注册的。可以被有效的添加到任何继承自DependencyObject的对象中。同时要明白绑定控件中包含有TextBox这个控件,找到TextBox控件然后绑定它的事件问题就解决了。

说完思路现在提供以上绑事件命令类库如何使用:

  <ComboBox x:Name="cboBox" Margin="115,0,10,0" IsEditable="True" ItemsSource="{Binding ListCollection}" Text="{Binding UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"   FontSize="" Height="" HorizontalAlignment="Left"  VerticalAlignment="Center" Width="">
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<!--<Behaviors:EventCommand CommandName="cboTextBoxChangedCommand" EventName="TextBox.TextChanged" />-->
<Behaviors:EventCommand CommandName="cboTextBoxBaseChangedCommand" EventName="TextBoxBase.TextChanged" />
<Behaviors:EventCommand CommandName="cboSelectionChangedCommand" EventName="SelectionChanged" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</ComboBox>

示例代码

运用示例代码来自于这个xaml文件中

MainWindow.xaml

<Window x:Class="EventCommandDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:EventCommandDemo"
xmlns:Behaviors="clr-namespace:ManMonthMyth.Practices.Composite.Presentation.Behaviors;assembly=ManMonthMyth.Practices.Composite.Presentation.Behaviors"
Title="MainWindow" Width="" Height="" WindowStyle="None" ResizeMode="NoResize" Background="LightPink" WindowStartupLocation="CenterScreen">
<Window.Triggers>
<EventTrigger SourceName="chkRememberPwd" RoutedEvent="CheckBox.Unchecked">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="chkLanding" Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.01" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger SourceName="chkLanding" RoutedEvent="CheckBox.Checked">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="chkRememberPwd" Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.01" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
<Viewbox>
<Grid Width="" Height="">
<Grid.RowDefinitions>
<RowDefinition Height="" />
<RowDefinition Height="" />
<RowDefinition Height="" />
<RowDefinition Height="" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Grid.Row="" Orientation="Horizontal">
<ComboBox x:Name="cboBox" Margin="115,0,10,0" IsEditable="True" ItemsSource="{Binding ListCollection}" Text="{Binding UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="" Height="" HorizontalAlignment="Left" VerticalAlignment="Center" Width="">
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<!--<Behaviors:EventCommand CommandName="cboTextBoxChangedCommand" EventName="TextBox.TextChanged" />-->
<Behaviors:EventCommand CommandName="cboTextBoxBaseChangedCommand" EventName="TextBoxBase.TextChanged" />
<Behaviors:EventCommand CommandName="cboSelectionChangedCommand" EventName="SelectionChanged" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</ComboBox>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Text="注册账号" >
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<Behaviors:EventCommand CommandName="TxTMouseEnterCommand" EventName="MouseEnter" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</TextBlock>
</StackPanel>
<StackPanel Grid.Row="" Orientation="Horizontal">
<PasswordBox Margin="115,0,10,0" local:PasswordBoxBindingHelper.IsPasswordBindingEnabled="True" local:PasswordBoxBindingHelper.BindedPassword="{Binding Path=UserPassword,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="" Height="" HorizontalAlignment="Left" VerticalAlignment="Center" Width="" >
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<Behaviors:EventCommand CommandName="pwdGotFocusCommand" EventName="GotFocus" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</PasswordBox>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Name="textBlock2" Text="忘记密码" Style="{Binding ElementName=textBlock1,Path=Style}" />
</StackPanel>
<StackPanel Grid.Row="" Orientation="Horizontal">
<CheckBox x:Name="chkRememberPwd" Content="记住密码" IsThreeState="False" HorizontalAlignment="Left" Margin="115,0,0,0" VerticalAlignment="Top" >
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<Behaviors:EventCommand CommandName="chkUncheckedCommand" EventName="Unchecked"/>
<Behaviors:EventCommand CommandName="chkCheckedCommand" EventName="Checked" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</CheckBox>
<CheckBox x:Name="chkLanding" Content="自动登陆" IsThreeState="False" HorizontalAlignment="Left" Margin="15,0,0,0" VerticalAlignment="Top" />
</StackPanel>
<StackPanel Grid.Row="" Orientation="Horizontal">
<Button Margin="120,0,0,0" Height="" Width="" Name="btnLogin" VerticalAlignment="Top">
<TextBlock FontSize="" FontFamily="Consolas;Microsoft YaHei" Foreground="Blue" Text="登 录" />
<Behaviors:Events.Commands>
<Behaviors:EventCommandCollection>
<!--<Behaviors:EventCommand CommandName="btnMouseDoubleClickCommand" EventName="MouseDoubleClick" />-->
<Behaviors:EventCommand CommandName="btnClickCommand" EventName="Click" />
</Behaviors:EventCommandCollection>
</Behaviors:Events.Commands>
</Button>
</StackPanel>
</Grid>
</Viewbox>
</Window>

MainWindow.xaml

运行后的界面

WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。

MainWindow.xaml 后台代码 MainWindow.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace EventCommandDemo
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); #region 绑定数据和命令
MainViewModel _viewmodel = null;
this.Loaded += (sender, e) =>
{
if (_viewmodel == null)
{
_viewmodel = new MainViewModel();
this.DataContext = _viewmodel;
}
};
#endregion
} }
}

MainWindow.cs

Mainwindow.xaml辅助类 PasswordBoxBindingHelper.cs,提供PasswordBox绑定属性

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows; namespace EventCommandDemo
{
public static class PasswordBoxBindingHelper
{
public static bool GetIsPasswordBindingEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsPasswordBindingEnabledProperty);
} public static void SetIsPasswordBindingEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsPasswordBindingEnabledProperty, value);
} public static readonly DependencyProperty IsPasswordBindingEnabledProperty = DependencyProperty.RegisterAttached("IsPasswordBindingEnabled", typeof(bool),
typeof(PasswordBoxBindingHelper),new UIPropertyMetadata(false,OnIsPasswordBindingEnabledChanged)); private static void OnIsPasswordBindingEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var passwordBox = obj as System.Windows.Controls.PasswordBox;
if (passwordBox != null)
{
passwordBox.PasswordChanged -= PasswordBoxPasswordChanged;
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordBoxPasswordChanged;
}
}
} static void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
{
var passwordBox = (System.Windows.Controls.PasswordBox)sender;
if (!String.Equals(GetBindedPassword(passwordBox), passwordBox.Password))
{
SetBindedPassword(passwordBox, passwordBox.Password);
//目的是让打字的光标显示在最后
int length = passwordBox.Password.Length;
SetPasswordBoxSelection(passwordBox, length, length);
}
} public static string GetBindedPassword(DependencyObject obj)
{
return (string)obj.GetValue(BindedPasswordProperty);
} public static void SetBindedPassword(DependencyObject obj, string value)
{
obj.SetValue(BindedPasswordProperty, value);
} public static readonly DependencyProperty BindedPasswordProperty = DependencyProperty.RegisterAttached("BindedPassword", typeof(string),
typeof(PasswordBoxBindingHelper),new UIPropertyMetadata(string.Empty, OnBindedPasswordChanged)); private static void OnBindedPasswordChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var passwordBox = obj as System.Windows.Controls.PasswordBox;
if (passwordBox != null)
{
passwordBox.Password = e.NewValue == null ? string.Empty : e.NewValue.ToString();
}
} /// <summary>
/// 在更改了密码框的密码后, 需要手动更新密码框插入符(CaretIndex)的位置, 可惜的是,
/// 密码框并没有给我们提供这样的属性或方法(TextBox有, PasswordBox没有),
/// 可以采用下面的方法来设置
/// </summary>
/// <param name="passwordBox"></param>
/// <param name="start"></param>
/// <param name="length"></param>
private static void SetPasswordBoxSelection(System.Windows.Controls.PasswordBox passwordBox, int start, int length)
{
var select = passwordBox.GetType().GetMethod("Select",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); select.Invoke(passwordBox, new object[] { start, length });
} }
}

PasswordBoxBindingHelper.cs

MainWindow的ViewModel文件MainViewModel.cs 提供绑定数据和命令及命令实现方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows; namespace EventCommandDemo
{
public class MainViewModel : ViewModelBase
{ public MainViewModel()
{
cboMouseEnterCommand = new ViewModelCommand((Object parameter) => { cboMouseEnterEvent(); });
cboTextBoxChangedCommand = new ViewModelCommand((Object parameter) => { cboTextBoxChangedEvent(); });
cboTextBoxBaseChangedCommand = new ViewModelCommand((Object parameter) => { cboTextBoxBaseChangedEvent(); });
cboSelectionChangedCommand = new ViewModelCommand((Object parameter) => { cboSelectionChangedEvent(); });
btnClickCommand = new ViewModelCommand((Object parameter) => { btnClickEvent(); });
btnMouseDoubleClickCommand = new ViewModelCommand((Object paramenter) => { btnMouseDoubleClickEvent(); });
pwdGotFocusCommand = new ViewModelCommand((Object paramenter) => { pwdGotFocusEvent(); });
chkCheckedCommand = new ViewModelCommand((Object paramenter) => { chkCheckedEvent(); });
chkUncheckedCommand = new ViewModelCommand((Object paramenter) => { chkUncheckedEvent(); }); var tempList =new string[]{"一个","两个","三个"};
ListCollection = tempList;
} #region 事件命令
/// <summary>
/// 下拉框 鼠标进入事件命令
/// </summary>
public ICommand cboMouseEnterCommand { get; private set; }
/// <summary>
/// 下拉框文本改变事件命令,通过TextBox.TextChanged
/// </summary>
public ICommand cboTextBoxChangedCommand { get;private set; }
/// <summary>
/// 下拉框文本改变事件命令,通过TextBoxBase.TextChanged
/// </summary>
public ICommand cboTextBoxBaseChangedCommand { get; private set; } /// <summary>
/// 下拉框,选中事件
/// </summary>
public ICommand cboSelectionChangedCommand { get; private set; } /// <summary>
/// 登陆按钮单机事件命令
/// </summary>
public ICommand btnClickCommand { get; private set; } /// <summary>
/// 登陆按钮双击事件命令
/// </summary>
public ICommand btnMouseDoubleClickCommand { get; private set; }
/// <summary>
/// 密码框获取焦点事件命令
/// </summary>
public ICommand pwdGotFocusCommand { get; private set; } /// <summary>
/// 多选框非选中事件命令
/// </summary>
public ICommand chkUncheckedCommand { get;private set; }
/// <summary>
/// 多选框选中事件命令
/// </summary>
public ICommand chkCheckedCommand { get;private set; } #endregion #region 事件执行方法
/// <summary>
/// 下拉框 鼠标进入事件,执行方法。
/// </summary>
private void cboMouseEnterEvent()
{
MessageBox.Show("下拉框,鼠标进入事件被触发了");
} /// <summary>
/// 下拉框文本改变事件,执行方法
/// </summary>
private void cboTextBoxBaseChangedEvent()
{
MessageBox.Show("TextBoxBase - 用户名输入,下拉框文本改变事件被触发了");
} private void cboTextBoxChangedEvent()
{
MessageBox.Show("TextBox - 用户名输入,下拉框文本改变事件被触发了");
} /// <summary>
/// 下拉框选中事件,执行方法
/// </summary>
private void cboSelectionChangedEvent()
{
MessageBox.Show("下拉框,下拉框选中一个Item事件被触发了");
}
/// <summary>
/// 登陆按钮单机事件,执行方法
/// </summary>
private void btnClickEvent()
{
MessageBox.Show("登陆按钮,鼠标单机(Click)事件被触发了");
} /// <summary>
/// 登陆按钮双击事件,执行方法
/// </summary>
private void btnMouseDoubleClickEvent()
{
MessageBox.Show("登陆按钮,鼠标双击事件被触发了");
} /// <summary>
/// 密码框获取焦点事件,执行方法
/// </summary>
private void pwdGotFocusEvent()
{
MessageBox.Show("密码框获取了焦点,事件被触发了");
} /// <summary>
/// 多选框,非选中状态事件,执行方法
/// </summary>
private void chkUncheckedEvent()
{
MessageBox.Show("多选框没选中,事件被触发了");
}
/// <summary>
/// 多选框,选中状态事件,执行方法
/// </summary>
private void chkCheckedEvent()
{
MessageBox.Show("多选框选中,事件被触发了");
}
#endregion #region 成员
private string[] listCollection;
#endregion #region 属性
public string[] ListCollection { get { return listCollection; } set { this.SetProperty(ref this.listCollection,value);} }
#endregion }
}

MainViewModel.cs

ViewModel辅助类有ViewModelBase.cs,ViewModelCommand.cs 分别提供属性更改通知和Command命令接口ICommand实现类

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text; namespace EventCommandDemo
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged; #region 支持.NET4.5
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) { return false; }
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion #region 支持.NET4.0 //private static string GetProperyName(string methodName)
//{
// if (methodName.StartsWith("get_") || methodName.StartsWith("set_") ||
// methodName.StartsWith("put_"))
// {
// return methodName.Substring("get_".Length);
// }
// throw new Exception(methodName + " not a method of Property");
//} //protected bool SetProperty<T>(ref T storage, T value)
//{
// if (object.Equals(storage, value)) { return false; }
// storage = value;
// string propertyName = GetProperyName(new System.Diagnostics.StackTrace(true).GetFrame(1).GetMethod().Name);
// this.OnPropertyChanged(propertyName);
// return true;
//} //private void OnPropertyChanged(string propertyName)
//{
// var eventHandler = this.PropertyChanged;
// if (eventHandler != null)
// {
// eventHandler(this, new PropertyChangedEventArgs(propertyName));
// }
//} #endregion } }

ViewModelBase.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace EventCommandDemo
{
public class ViewModelCommand : System.Windows.Input.ICommand
{
private Action<Object> action; public Action<Object> ViewModelAction
{
get { return action; }
set { action = value; }
} public ViewModelCommand(Action<Object> act)
{
action = act;
} public bool CanExecute(Object parameter)
{
return true;
} public void Execute(Object parameter)
{
this.ViewModelAction(parameter);
} public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
}
}

ViewModelCommand.cs

事件被绑定后触发相关事件界面:

WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。

这里分享源码EventCommandDemo.zip文件下载地址,希望大家在技术的殿堂里都能快速成长。

源码下载:

点击下载EventCommandDemo.zip

                      本文作者夜神,首发博客园,转载请注明。

WPF Event 在 Command 中的应用初级篇,支持所有Event 展示松耦合设计的全部代码 - 解决TextBoxBase.TextChanged或者TextBox.TextChanged等类似事件绑定问题。的更多相关文章

  1. WPF编程,C&num;中对话框自动关闭的一种方法。

    原文:WPF编程,C#中对话框自动关闭的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details/8 ...

  2. WPF中如何将ListViewItem双击事件绑定到Command

    今天的博客将介绍如何实现ListViewItem双击事件绑定到ViewModel中的Command.实现方法借助了Style中的EventSetter,请看下面的详细代码: <ListView ...

  3. WPF MVVM 从Prism中学习设计模式之Event Aggregator 模式

    Prism简介 Prism是由微软Patterns & Practices团队开发的项目,目的在于帮助开发人员构建松散耦合的.更灵活.更易于维护并且更易于测试的WPF应用或是Silverlig ...

  4. WPF自学入门(十一)WPF MVVM模式Command命令

    在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式.正如上一篇文章中在开始说的,MVVM的目的是为了最大限度地降低了 ...

  5. WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍

    WPF自学入门(十一)WPF MVVM模式Command命令   在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式 ...

  6. WPF EventSetter Handler Command

    最近做一个工具,突然发现ListBox和ListView等列表控件的MouseDoubleClick事件有时候是获取不到当前双击的行对象数据的,比如这样写: <ListBox Grid.Row= ...

  7. WPF中MVVM模式下控件自有的事件绑定

    1.原因 在WPF中单纯的命令绑定往往不能满足覆盖所有的事件,例如ComboBox的SelectionChanged事件,DataGrid的SelectionChanged事件等等,这时就可以用事件绑 ...

  8. 在WPF的MVVM框架中获取下拉选择列表中的选中项

    文章概述: 本演示介绍怎样在WPF的MVVM框架中.通过数据绑定的方式获取下拉列表中的选中项.程序执行后的效果例如以下图所看到的: 相关下载(代码.屏幕录像):http://pan.baidu.com ...

  9. &lbrack;转&rsqb;WPF命令集 Command

    在我们日常的应用程序操作中,经常要处理各种各样的命令和进行相关的事件处理,比如需要复制.粘贴文本框中的内容;上网查看网页时,可能需要返回上一网页查看相应内容;而当我们播放视频和多媒体时,我们可能要调节 ...

随机推荐

  1. 操作系统 页面置换算法LRU和FIFO

    LRU(Least Recently Used)最少使用页面置换算法,顾名思义,就是替换掉最少使用的页面. FIFO(first in first out,先进先出)页面置换算法,这是的最早出现的置换 ...

  2. 51nod1158 全是1的最大子矩阵

    跟最大子矩阵差不多O(n3)扫一下.有更优写法?挖坑! #include<cstdio> #include<cstring> #include<cctype> #i ...

  3. &lbrack;搜片神器&rsqb;DHT后台管理程序数据库流程设计优化学习交流

    谢谢园子朋友的支持,已经找到个VPS进行测试,国外的服务器: sosobt.com 大家可以给提点意见... 服务器在抓取和处理同时进行,所以访问速度慢是有些的,特别是搜索速度通过SQL的like来查 ...

  4. 数据库不能用delete---index空间不足

    只有0702表是这样的情况,0101表可以使用like和between and

  5. Astyle:代码格式化工具简明指南

    astyle是一个我自己常用的开放源码工具.它可以方便的将程序代码格式化成自己想要的样式而不必人工修改.本来嘛,作为高等生物应该优先去做一些智慧的事情,而不是把时间消耗在机器可以完美完成的事情上. 想 ...

  6. JQuery中html&lpar;&rpar;方法的注意事项

    .html方法当不传参数时用来获取元素的html内容, return this[0] && this[0].nodeType === 1 ? this[0].innerHTML.rep ...

  7. java中遍历MAP&comma;嵌套map的几种方法

    java中遍历MAP的几种方法 Map<String,String> map=new HashMap<String,String>();    map.put("us ...

  8. CodeForces 670C Cinema

    简单题. 统计一下懂每种语言的人分别有几个,然后$O(n)$扫一遍电影就可以得到答案了. #pragma comment(linker, "/STACK:1024000000,1024000 ...

  9. 【ASP&period;NET Core快速入门】(八)Middleware管道介绍、自己动手构建RequestDelegate管道

    中间件是汇集到以处理请求和响应的一个应用程序管道的软件. 每个组件: 可以选择是否要将请求传递到管道中的下一个组件. 之前和之后调用管道中的下一个组件,可以执行工作. 使用请求委托来生成请求管道. 请 ...

  10. 解决bootstrap-table多次请求只触发一次的问题

    在初始化table之前,要将table销毁,否则会保留上次加载的内容 1 $("#table").bootstrapTable('destroy'); 2 $("#tab ...