WPF Event 在 Command 中的應用初級篇,支持全部Event 展現鬆耦合設計的所有代碼 - 解決TextBoxBase.TextChanged或者TextBox.TextChanged等

作過WPF開發的人,都知道作MVVM架構,最麻煩的是Event的綁定,由於Event是不能被綁定的,同時現有的條件下,命令是沒法替代Event。而在開發過程當中沒法避免Event事件,這樣MVVM的架構就不能徹底實現了。web

因此後來微軟提供了一個折中的方案,使用Trigger觸發器和System.Windows.Interactivity結合經過事件綁定事件,我的以爲這個方法也挺能夠的。編程

還有Prism框架方法,我看過可是這個方法比較繁瑣可用性不高。數組

後來經過搜索和本身研究知道了一個解決方案,能夠綁定全部事件,這個是原始版本,我我的正在研究一個徹底的版本,但願能徹底支持Event屬性。架構

對於編程學習喜歡的方式,我我的是這個見解,我作開發也有一年多了,WPF開發也有九個月了。從小白到如今入門略有深刻。再也不侷限於實現效果,而是更喜歡實現功能的原汁原味的代碼。不少效果也許我不能寫出完整的代碼,也沒有那麼多的時間,可是深刻看了別人的設計代碼,老是受益良多。框架

工做這麼長時間,總算明白爲何Java那麼受歡迎了。由於做爲技術人員到了必定境界,更喜歡瞭解具體的實現過程,而對於直接使用SDK的興趣淡了很多。打個比方微軟提供了不少優秀的SDK,可是就像家庭同樣,越是包辦一切的好父母,每每不太招孩子們的喜歡,不是孩子們不懂得感恩,而是社會天然規律,你沒法成長,你便很容易感受到本身無能,無能的人哪有快樂而言。less

正文:具體的類圖 經過類圖能夠看到結構至關清晰,它們的依賴關係是單向的。是屬於低耦合的關係ide

 

 
如今提供各個類的代碼,從上到下,層次從左到右。注意實際上創建類的順序是相反的,這裏只是爲了展現方便
 
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;
        }

    }
}
View Code


EventBehaviorCollection 函數

using System;
using System.Collections.Generic;

namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
    /// <summary>
    /// 事件名稱集合
    /// </summary>
    public class EventCommandCollection : List<EventCommand>
    {

    }
}
View Code


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 == 1)
                {
                    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-1], 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 = 0; i < act; i++)
            {
                if (assemblies[i].FullName.Split(',')[0] == "PresentationFramework")
                {
                    return SelectVisual(assemblies[i], names, 0, 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 = 0; 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+2))//判斷是否到了數組有效的位置,由於爲了偷懶沒有對string[]進行去尾操做,由於string[]最後一個數據是事件名稱,在這裏是 無效的
            {
                return obj;
            }
           return  SelectVisual(assembly, names, i+1, 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 = 0; 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();
        }

    }
}
View Code


EventCommandCollection this

using System;
using System.Collections.Generic;

namespace ManMonthMyth.Practices.Composite.Presentation.Behaviors
{
    /// <summary>
    /// 事件名稱集合
    /// </summary>
    public class EventCommandCollection : List<EventCommand>
    {

    }
}
View Code


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);
            }
        }

    }
}
View Code


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; }

    }
}
View Code

 

如今完整的框架已經搭建好了,如今就是作一個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="15" Height="25" HorizontalAlignment="Left"  VerticalAlignment="Center" Width="179">
                    <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="400" Height="300" 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="400" Height="300">
        <Grid.RowDefinitions>
            <RowDefinition Height="120" />
            <RowDefinition Height="40" />
            <RowDefinition Height="40" />
            <RowDefinition Height="30" />
            <RowDefinition />
        </Grid.RowDefinitions>  
        <StackPanel Grid.Row="1" Orientation="Horizontal">
                <ComboBox x:Name="cboBox" Margin="115,0,10,0" IsEditable="True" ItemsSource="{Binding ListCollection}" Text="{Binding UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"   FontSize="15" Height="25" HorizontalAlignment="Left"  VerticalAlignment="Center" Width="179">
                    <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="2" Orientation="Horizontal">
                <PasswordBox Margin="115,0,10,0" local:PasswordBoxBindingHelper.IsPasswordBindingEnabled="True"  local:PasswordBoxBindingHelper.BindedPassword="{Binding Path=UserPassword,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" FontSize="15" Height="25" HorizontalAlignment="Left" VerticalAlignment="Center" Width="179" >
                    <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="3" 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="4" Orientation="Horizontal">
                <Button Margin="120,0,0,0" Height="33" Width="181" Name="btnLogin" VerticalAlignment="Top">
                    <TextBlock FontSize="18" 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

運行後的界面

 

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

 

事件被綁定後觸發相關事件界面:

 

這裏分享源碼EventCommandDemo.zip文件下載地址,但願你們在技術的殿堂裏都能快速成長。

 

源碼下載:

點擊下載EventCommandDemo.zip

                                                                                                                       本文做者夜神,首發博客園,轉載請註明。

相關文章
相關標籤/搜索