c# WPF中System.Windows.Interactivity的使用
背景
在我們進(jìn)行WPF開(kāi)發(fā)應(yīng)用程序的時(shí)候不可避免的要使用到事件,很多時(shí)候沒(méi)有嚴(yán)格按照MVVM模式進(jìn)行開(kāi)發(fā)的時(shí)候習(xí)慣直接在xaml中定義事件,然后再在對(duì)應(yīng)的.cs文件中直接寫事件的處理過(guò)程,這種處理方式寫起來(lái)非常簡(jiǎn)單而且不用過(guò)多地處理考慮代碼之間是否符合規(guī)范,但是我們?cè)趯懘a的時(shí)候如果完全按照WPF規(guī)范的MVVM模式進(jìn)行開(kāi)發(fā)的時(shí)候就應(yīng)該將相應(yīng)的事件處理寫在ViewModel層,這樣整個(gè)代碼才更加符合規(guī)范而且層次也更加清楚,更加符合MVVM規(guī)范。
常規(guī)用法
1 引入命名空間
通過(guò)在代碼中引入System.Windows.Interactivity.dll,引入了這個(gè)dll后我們就能夠使用這個(gè)里面的方法來(lái)將事件映射到ViewModel層了,我們來(lái)看看具體的使用步驟,第一步就是引入命名控件
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
另外還可以通過(guò)另外一種方式來(lái)引入命名空間,其實(shí)這兩者間都是對(duì)等的。
xmlns:i=http://schemas.microsoft.com/expression/2010/interactivity
2 添加事件對(duì)應(yīng)的Command
這里以TextBox的GetFocus和LostFocus為例來(lái)進(jìn)行說(shuō)明
<TextBox Text="CommandBinding">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding OnTextLostFocus}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
</i:EventTrigger>
<i:EventTrigger EventName="GotFocus">
<i:InvokeCommandAction Command="{Binding OnTextGotFocus}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
這個(gè)里面我們重點(diǎn)來(lái)看看這個(gè)InvokeCommandAction的代碼結(jié)構(gòu)
namespace System.Windows.Interactivity
{
public sealed class InvokeCommandAction : TriggerAction<DependencyObject>
{
public static readonly DependencyProperty CommandProperty;
public static readonly DependencyProperty CommandParameterProperty;
public InvokeCommandAction();
public string CommandName { get; set; }
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
protected override void Invoke(object parameter);
}
}
這里我們發(fā)現(xiàn)這里我們?nèi)绻覀兌x一個(gè)Command的話我們只能夠在Command中獲取到我們綁定的CommandParameter這個(gè)參數(shù),但是有時(shí)候我們需要獲取到觸發(fā)這個(gè)事件的RoutedEventArgs的時(shí)候,通過(guò)這種方式就很難獲取到了,這個(gè)時(shí)候我們就需要自己去擴(kuò)展一個(gè)InvokeCommandAction了,這個(gè)時(shí)候我們應(yīng)該怎么做呢?整個(gè)過(guò)程分成三步:
2.1 定義自己的CommandParameter
public class ExCommandParameter
{
/// <summary>
/// 事件觸發(fā)源
/// </summary>
public DependencyObject Sender { get; set; }
/// <summary>
/// 事件參數(shù)
/// </summary>
public EventArgs EventArgs { get; set; }
/// <summary>
/// 額外參數(shù)
/// </summary>
public object Parameter { get; set; }
}
這個(gè)對(duì)象除了封裝我們常規(guī)的參數(shù)外還封裝了我們需要的EventArgs屬性,有了這個(gè)我們就能將當(dāng)前的事件的EventArgs傳遞進(jìn)來(lái)了。
2.2 重寫自己的InvokeCommandAction
public class ExInvokeCommandAction : TriggerAction<DependencyObject>
{
private string commandName;
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExInvokeCommandAction), null);
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExInvokeCommandAction), null);
/// <summary>
/// 獲得或設(shè)置此操作應(yīng)調(diào)用的命令的名稱。
/// </summary>
/// <value>此操作應(yīng)調(diào)用的命令的名稱。</value>
/// <remarks>如果設(shè)置了此屬性和 Command 屬性,則此屬性將被后者所取代。</remarks>
public string CommandName
{
get
{
base.ReadPreamble();
return this.commandName;
}
set
{
if (this.CommandName != value)
{
base.WritePreamble();
this.commandName = value;
base.WritePostscript();
}
}
}
/// <summary>
/// 獲取或設(shè)置此操作應(yīng)調(diào)用的命令。這是依賴屬性。
/// </summary>
/// <value>要執(zhí)行的命令。</value>
/// <remarks>如果設(shè)置了此屬性和 CommandName 屬性,則此屬性將優(yōu)先于后者。</remarks>
public ICommand Command
{
get
{
return (ICommand)base.GetValue(ExInvokeCommandAction.CommandProperty);
}
set
{
base.SetValue(ExInvokeCommandAction.CommandProperty, value);
}
}
/// <summary>
/// 獲得或設(shè)置命令參數(shù)。這是依賴屬性。
/// </summary>
/// <value>命令參數(shù)。</value>
/// <remarks>這是傳遞給 ICommand.CanExecute 和 ICommand.Execute 的值。</remarks>
public object CommandParameter
{
get
{
return base.GetValue(ExInvokeCommandAction.CommandParameterProperty);
}
set
{
base.SetValue(ExInvokeCommandAction.CommandParameterProperty, value);
}
}
/// <summary>
/// 調(diào)用操作。
/// </summary>
/// <param name="parameter">操作的參數(shù)。如果操作不需要參數(shù),則可以將參數(shù)設(shè)置為空引用。</param>
protected override void Invoke(object parameter)
{
if (base.AssociatedObject != null)
{
ICommand command = this.ResolveCommand();
/*
* ★★★★★★★★★★★★★★★★★★★★★★★★
* 注意這里添加了事件觸發(fā)源和事件參數(shù)
* ★★★★★★★★★★★★★★★★★★★★★★★★
*/
ExCommandParameter exParameter = new ExCommandParameter
{
Sender = base.AssociatedObject,
Parameter = GetValue(CommandParameterProperty),
EventArgs = parameter as EventArgs
};
if (command != null && command.CanExecute(exParameter))
{
/*
* ★★★★★★★★★★★★★★★★★★★★★★★★
* 注意將擴(kuò)展的參數(shù)傳遞到Execute方法中
* ★★★★★★★★★★★★★★★★★★★★★★★★
*/
command.Execute(exParameter);
}
}
}
private ICommand ResolveCommand()
{
ICommand result = null;
if (this.Command != null)
{
result = this.Command;
}
else
{
if (base.AssociatedObject != null)
{
Type type = base.AssociatedObject.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] array = properties;
for (int i = 0; i < array.Length; i++)
{
PropertyInfo propertyInfo = array[i];
if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
{
result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
}
}
}
}
return result;
}
}
這個(gè)里面的重點(diǎn)是要重寫基類中的Invoke方法,將當(dāng)前命令通過(guò)反射的方式來(lái)獲取到,然后在執(zhí)行command.Execute方法的時(shí)候?qū)⑽覀冏远x的ExCommandParameter傳遞進(jìn)去,這樣我們就能夠在最終綁定的命令中獲取到特定的EventArgs對(duì)象了。
2.3 在代碼中應(yīng)用自定義InvokeCommandAction
<ListBox x:Name="lb_selecthistorymembers"
SnapsToDevicePixels="true"
ItemsSource="{Binding DataContext.SpecificHistoryMembers,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
HorizontalAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Background="#fff"
BorderThickness="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<interactive:ExInvokeCommandAction Command="{Binding DataContext.OnSelectHistoryMembersListBoxSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}">
</interactive:ExInvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
注意這里需要首先引入自定義的interactive的命名空間,這個(gè)在使用的時(shí)候需要注意,另外在最終的Command訂閱中EventArgs根據(jù)不同的事件有不同的表現(xiàn)形式,比如Loaded事件,那么最終獲取到的EventArgs就是RoutedEventArgs對(duì)象,如果是TableControl的SelectionChanged事件,那么最終獲取到的就是SelectionChangedEventArgs對(duì)象,這個(gè)在使用的時(shí)候需要加以區(qū)分。
3 使用當(dāng)前程序集增加Behavior擴(kuò)展
System.Windows.Interactivity.dll中一個(gè)重要的擴(kuò)展就是對(duì)Behavior的擴(kuò)展,這個(gè)Behavior到底該怎么用呢?我們來(lái)看下面的一個(gè)例子,我們需要給一個(gè)TextBlock和Button增加一個(gè)統(tǒng)一的DropShadowEffect,我們先來(lái)看看最終的效果,然后再就具體的代碼進(jìn)行分析。

代碼分析
1 增加一個(gè)EffectBehavior
public class EffectBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
}
private void AssociatedObject_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
var element = sender as FrameworkElement;
element.Effect = new DropShadowEffect() { Color = Colors.Transparent, ShadowDepth = 2 }; ;
}
private void AssociatedObject_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
var element = sender as FrameworkElement;
element.Effect = new DropShadowEffect() { Color = Colors.Red, ShadowDepth = 2 };
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.MouseEnter -= AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave -= AssociatedObject_MouseLeave;
}
}
這里我們繼承自System.Windows.Interactivity中的Behavior<T>這個(gè)泛型類,這里我們的泛型參數(shù)使用FrameworkElement,因?yàn)榇蟛糠值目丶际抢^承自這個(gè)對(duì)象,我們方便為其統(tǒng)一添加效果,在集成這個(gè)基類后我們需要重寫基類的OnAttached和OnDetaching方法,這個(gè)里面AssociatedObject就是我們具體添加Effect的元素,在我們的示例中這個(gè)分別是TextBlock和Button對(duì)象。
2 在具體的控件中添加此效果
<Window x:Class="WpfBehavior.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfBehavior" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"> <TextBlock Text="測(cè)試文本" Margin="2" Height="30"> <i:Interaction.Behaviors> <local:EffectBehavior></local:EffectBehavior> </i:Interaction.Behaviors> </TextBlock> <Button Content="測(cè)試" Width="80" Height="30" Margin="2"> <i:Interaction.Behaviors> <local:EffectBehavior></local:EffectBehavior> </i:Interaction.Behaviors> </Button> </StackPanel> </Grid> </Window>
以上就是c# WPF中System.Windows.Interactivity的使用的詳細(xì)內(nèi)容,更多關(guān)于WPF中System.Windows.Interactivity的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 詳解Android封裝一個(gè)全局的BaseActivity
- 詳解Android Activity的啟動(dòng)流程
- Android利用startActivityForResult返回?cái)?shù)據(jù)到前一個(gè)Activity
- Android非異常情況下的Activity生命周期分析
- Android實(shí)現(xiàn)左滑退出Activity的完美封裝
- Android Activity的4種啟動(dòng)模式圖文介紹
- Android IPC機(jī)制ACtivity綁定Service通信代碼實(shí)例
- 通過(guò)實(shí)例解析android Activity啟動(dòng)過(guò)程
- Android用tabhost實(shí)現(xiàn) 界面切換,每個(gè)界面為一個(gè)獨(dú)立的activity操作
- springboot2.2 集成 activity6實(shí)現(xiàn)請(qǐng)假流程(示例詳解)
- 詳解Android Activity中的幾種監(jiān)聽(tīng)器和實(shí)現(xiàn)方式
相關(guān)文章
C#應(yīng)用XML作為數(shù)據(jù)庫(kù)的快速開(kāi)發(fā)框架實(shí)現(xiàn)方法
這篇文章主要介紹了C#應(yīng)用XML作為數(shù)據(jù)庫(kù)的快速開(kāi)發(fā)框架實(shí)現(xiàn)方法,詳細(xì)介紹了將XML作為數(shù)據(jù)庫(kù)的C#桌面應(yīng)用開(kāi)發(fā)技巧,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-12-12
客戶端實(shí)現(xiàn)藍(lán)牙接收(C#)知識(shí)總結(jié)
網(wǎng)上有關(guān)藍(lán)牙接收的資料很多,使用起來(lái)也很簡(jiǎn)單,但是我覺(jué)得還是有必要把這些知識(shí)總結(jié)下來(lái),藍(lán)牙開(kāi)發(fā)需要用到一個(gè)第三方的庫(kù)InTheHand.Net.Personal.dll,感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02
C#如何實(shí)現(xiàn)dataGridView動(dòng)態(tài)綁定數(shù)據(jù)
這篇文章主要介紹了C#如何實(shí)現(xiàn)dataGridView動(dòng)態(tài)綁定數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
C#實(shí)現(xiàn)從多列的DataTable里取需要的幾列
這篇文章主要介紹了C#實(shí)現(xiàn)從多列的DataTable里取需要的幾列,涉及C#針對(duì)DataTable操作的相關(guān)技巧,需要的朋友可以參考下2016-03-03
C#實(shí)現(xiàn)SMTP服務(wù)發(fā)送郵件的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用C#實(shí)現(xiàn)SMTP服務(wù)發(fā)送郵件的功能,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12

