今天我们通过用户管理模块来系统的讲述关于Action,Trigger的使用。首先还是先上一张图,调一下胃口。 这个图中的Grid怎会变成如此摸样,是的,在这里我们用到了一点点3D的效果。首先我们先从前台代码入手。 controls:ChildWindowx:Class= MISInfoManage.UserManageView /*此处省略部分代码*/ xmlns:resource= clr-namespa
今天我们通过用户管理模块来系统的讲述关于Action,Trigger的使用。首先还是先上一张图,调一下胃口。
这个图中的Grid怎会变成如此摸样,是的,在这里我们用到了一点点3D的效果。首先我们先从前台代码入手。
<controls:ChildWindow x:Class="MISInfoManage.UserManageView"
/*此处省略部分代码*/ xmlns:resource="clr-namespace:MISInfoManage.Resources"
xmlns:trigger="clr-namespace:MISInfoManage.Trigger"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
/*此处省略部分代码*/
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="65"/>
<RowDefinition Height="297"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid Grid.Row="0" Grid.Column="0" Height="300" Width="540"
/*此处省略部分代码*/ >
<sdk:DataGrid.Projection>
<PlaneProjection RotationX="0" RotationY="10" RotationZ="0"></PlaneProjection>
</sdk:DataGrid.Projection>
<sdk:DataGrid.Effect>
<DropShadowEffect BlurRadius="5" Color="Black" ShadowDepth="8" Opacity="0.8"/>
</sdk:DataGrid.Effect>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<trigger:SelectChangeTargetTrigger TargetName="txtUserNo">
</trigger:SelectChangeTargetTrigger>
<trigger:SelectChangeTargetTrigger TargetName="cmbType">
</trigger:SelectChangeTargetTrigger>
<trigger:SelectChangeTargetTrigger TargetName="cmbState">
</trigger:SelectChangeTargetTrigger>
</i:EventTrigger>
</i:Interaction.Triggers>
<sdk:DataGrid.Columns>
/*此处省略部分代码*/ </sdk:DataGrid.Columns>
</sdk:DataGrid>
<sdk:DataPager x:Name="dataPager"
Grid.Row="1" BorderThickness="1"
BorderBrush="Black"
DisplayMode="FirstLastPreviousNextNumeric"
PageSize="10">
</sdk:DataPager>
<Border BorderThickness="1" Margin="0,5,0,5" Background="Silver" BorderBrush="Black" Grid.Row="2" Grid.Column="0">
/*此处省略部分代码*/ <Image Source="Images/Windows.png" Opacity="0.7" Grid.Row="0" Grid.Column="0"/>
<TextBox x:Name="txtUserNo" Foreground="Silver" BorderThickness="0" Grid.Row="0" Grid.Column="1" Text="{Binding UserNo,Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<trigger:FocusTrigger></trigger:FocusTrigger>
</i:EventTrigger>
<i:EventTrigger EventName="LostFocus">
<trigger:LostFocusTrigger>
</trigger:LostFocusTrigger>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</Grid>
</Border>
/*此处省略部分代码*/ <ComboBox x:Name="cmbType" Grid.Row="1" Grid.Column="1"
Background="AliceBlue"
HorizontalAlignment="Left" Margin="2,5,0,0" Width="200"
ItemsSource="{Binding UserTypeList,Mode=OneWay}"
SelectedItem="{Binding UserType}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding UserTypeImage}" Height="22" Stretch="UniformToFill"></Image>
<TextBlock Text="{Binding UserTypeName,Mode=TwoWay}" FontWeight="Bold" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
/*此处省略部分代码*/ </Grid>
</Border>
</Grid>
</controls:ChildWindow>
/*此处省略部分代码*/ xmlns:resource="clr-namespace:MISInfoManage.Resources"
xmlns:trigger="clr-namespace:MISInfoManage.Trigger"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
/*此处省略部分代码*/
<Grid x:Name="LayoutRoot" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="65"/>
<RowDefinition Height="297"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<sdk:DataGrid Grid.Row="0" Grid.Column="0" Height="300" Width="540"
/*此处省略部分代码*/ >
<sdk:DataGrid.Projection>
<PlaneProjection RotationX="0" RotationY="10" RotationZ="0"></PlaneProjection>
</sdk:DataGrid.Projection>
<sdk:DataGrid.Effect>
<DropShadowEffect BlurRadius="5" Color="Black" ShadowDepth="8" Opacity="0.8"/>
</sdk:DataGrid.Effect>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<trigger:SelectChangeTargetTrigger TargetName="txtUserNo">
</trigger:SelectChangeTargetTrigger>
<trigger:SelectChangeTargetTrigger TargetName="cmbType">
</trigger:SelectChangeTargetTrigger>
<trigger:SelectChangeTargetTrigger TargetName="cmbState">
</trigger:SelectChangeTargetTrigger>
</i:EventTrigger>
</i:Interaction.Triggers>
<sdk:DataGrid.Columns>
/*此处省略部分代码*/ </sdk:DataGrid.Columns>
</sdk:DataGrid>
<sdk:DataPager x:Name="dataPager"
Grid.Row="1" BorderThickness="1"
BorderBrush="Black"
DisplayMode="FirstLastPreviousNextNumeric"
PageSize="10">
</sdk:DataPager>
<Border BorderThickness="1" Margin="0,5,0,5" Background="Silver" BorderBrush="Black" Grid.Row="2" Grid.Column="0">
/*此处省略部分代码*/ <Image Source="Images/Windows.png" Opacity="0.7" Grid.Row="0" Grid.Column="0"/>
<TextBox x:Name="txtUserNo" Foreground="Silver" BorderThickness="0" Grid.Row="0" Grid.Column="1" Text="{Binding UserNo,Mode=OneWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<trigger:FocusTrigger></trigger:FocusTrigger>
</i:EventTrigger>
<i:EventTrigger EventName="LostFocus">
<trigger:LostFocusTrigger>
</trigger:LostFocusTrigger>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</Grid>
</Border>
/*此处省略部分代码*/ <ComboBox x:Name="cmbType" Grid.Row="1" Grid.Column="1"
Background="AliceBlue"
HorizontalAlignment="Left" Margin="2,5,0,0" Width="200"
ItemsSource="{Binding UserTypeList,Mode=OneWay}"
SelectedItem="{Binding UserType}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding UserTypeImage}" Height="22" Stretch="UniformToFill"></Image>
<TextBlock Text="{Binding UserTypeName,Mode=TwoWay}" FontWeight="Bold" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
/*此处省略部分代码*/ </Grid>
</Border>
</Grid>
</controls:ChildWindow>
在这里布局我们就不说了,相信看过前几篇的都明白。首先我们看有这么一段代码
- <sdk:DataGrid.Projection>
- <PlaneProjection RotationX="0" RotationY="10" RotationZ="0"></PlaneProjection>
- </sdk:DataGrid.Projection>
这段代码是设置DataGrid的3D效果的,在这里我们设置其x轴的旋转角为0,Y轴旋转角为10,Z轴是0。怎么理解呢?这就好比空间一个三维坐标,X轴转动你想象成手指头抓住X轴的正方向,用手指去撵的让它转动。同理,Y轴和Z轴也是同样的道理。我们在这里设置正角,是让它逆时针转动,如果是负角则是顺时针。OK,我们接着看下面这样一段代码
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="SelectionChanged">
- <trigger:SelectChangeTargetTrigger TargetName="txtUserNo">
- </trigger:SelectChangeTargetTrigger>
- <trigger:SelectChangeTargetTrigger TargetName="cmbType">
- </trigger:SelectChangeTargetTrigger>
- <trigger:SelectChangeTargetTrigger TargetName="cmbState">
- </trigger:SelectChangeTargetTrigger>
- </i:EventTrigger>
- </i:Interaction.Triggers>
这段涉及到我开始提到的Action,在这里我们使用到了TargetedTriggerAction,也就是在DataGrid SelectionChanged事件触发的时候,会对这三个TargetName指定的UIElement进行UI的变化。我们看看这个SelectChangeTargetTrigger 。
- namespace MISInfoManage.Trigger
- {
- public class SelectChangeTargetTrigger : TargetedTriggerAction<DependencyObject>
- {
- private DependencyObject element;
- public SelectChangeTargetTrigger()
- { }
- protected override void OnAttached()
- {
- base.OnAttached();
- if (Target != null)
- {
- element = Target;
- }
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- element = null;
- }
- protected override void OnTargetChanged(DependencyObject oldTarget, DependencyObject newTarget)
- {
- base.OnTargetChanged(oldTarget, newTarget);
- if (element == null)
- {
- element = newTarget;
- }
- }
- protected override void Invoke(object parameter)
- {
- if ((this.AssociatedObject as DataGrid).SelectedItem != null)
- {
- if (this.Target.GetType() == typeof(TextBox))
- {
- TextBox textBox = (Target as TextBox);
- textBox.IsReadOnly = true;
- if (textBox.Name == "txtUserNo")
- {
- textBox.Foreground = new SolidColorBrush(Colors.Black);
- }
- }
- if (this.GetType() == typeof(ComboBox))
- {
- (Target as ComboBox).IsEnabled = false;
- }
- }
- }
- }
- }
在这里需要注意的是要引用System.Windows.Interactivity命名空间。结合界面代码我们可以看出,当DataGrid的SelectionChanged事件触发以后,会调用Invoke方法,也就是遍历该TargetedTrigger下的目标元素,在这里是对文本框设置Readonly,对ComboBox设置IsEnabled。这样就有效的实现了UI与逻辑的分离,这个将放在ViewModel中做处理。再往下走我们发现这样一段代码
- <Border BorderThickness="1" BorderBrush="Black" Grid.Row="0" Grid.Column="1" Width="200">
- <Grid>
- <Grid.RowDefinitions>
- <RowDefinition Height="Auto"></RowDefinition>
- </Grid.RowDefinitions>
- <Grid.ColumnDefinitions>
- <ColumnDefinition Width="Auto"></ColumnDefinition>
- <ColumnDefinition Width="*"></ColumnDefinition>
- </Grid.ColumnDefinitions>
- <Image Source="Images/Windows.png" Opacity="0.7" Grid.Row="0" Grid.Column="0"/>
- <TextBox x:Name="txtUserNo" Foreground="Silver" BorderThickness="0" Grid.Row="0" Grid.Column="1" Text="{Binding UserNo,Mode=OneWay}">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="GotFocus">
- <trigger:FocusTrigger></trigger:FocusTrigger>
- </i:EventTrigger>
- <i:EventTrigger EventName="LostFocus">
- <trigger:LostFocusTrigger>
- </trigger:LostFocusTrigger>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </TextBox>
- </Grid>
- </Border>
这段代码是模拟了一个水印文本框效果,在这里使用到了Trigger,一个是FocusTrigger,一个是LostFocusTrigger,结合起来实现水印效果。我们看看这两个Trigger的代码
public class FocusTrigger:TriggerActionTextBox { protected override void OnAttached() { base .OnAttached(); } protected override void OnDetaching() { base .OnDetaching(); } protected override void In
- public class FocusTrigger:TriggerAction<TextBox>
- {
- protected override void OnAttached()
- {
- base.OnAttached();
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- }
- protected override void Invoke(object parameter)
- {
- if (this.AssociatedObject.Text.Trim() == "请输入用户名")
- {
- this.AssociatedObject.Text = string.Empty;
- this.AssociatedObject.Foreground = new SolidColorBrush(Colors.Black);
- }
- }
- }
这个是FocusTrigger,文本框获取焦点时,调用Invoke方法。这个时候当文本框的值是“请输入用户名”的时候,就清掉文本框,并且字体前景色设置为黑色。我们再看看LostFocusTrigger的代码
- public class LostFocusTrigger : TriggerAction<TextBox>
- {
- protected override void OnAttached()
- {
- base.OnAttached();
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- }
- protected override void Invoke(object parameter)
- {
- if (string.IsNullOrWhiteSpace(this.AssociatedObject.Text))
- {
- this.AssociatedObject.Foreground = new SolidColorBrush(Colors.Gray);
- this.AssociatedObject.Text = "请输入用户名";
- }
- }
- }
当文本框失去焦点的时候,如果文本框值为空或者空白,那么文本框的值为“请输入用户名”,前景色改为Gray。OK,我们继续往下看
- <ComboBox x:Name="cmbType" Grid.Row="1" Grid.Column="1"
- Background="AliceBlue"
- HorizontalAlignment="Left" Margin="2,5,0,0" Width="200"
- ItemsSource="{Binding UserTypeList,Mode=OneWay}"
- SelectedItem="{Binding UserType}">
- <ComboBox.ItemTemplate>
- <DataTemplate>
- <StackPanel Orientation="Horizontal">
- <Image Source="{Binding UserTypeImage}" Height="22" Stretch="UniformToFill"></Image>
- <TextBlock Text="{Binding UserTypeName,Mode=TwoWay}" FontWeight="Bold" Margin="2,0,0,0"/>
- </StackPanel>
- </DataTemplate>
- </ComboBox.ItemTemplate>
- </ComboBox>
在这里我们使用了ComboBox的项模板,在这里你可以自定义你想要的模版。在这里我放了一个图片和一个文本。如下图所示
怎么样,很不错吧。最后在这列我们重点看看分页。我这可是服务端分页。
- <sdk:DataPager x:Name="dataPager"
- Grid.Row="1" BorderThickness="1"
- BorderBrush="Black"
- DisplayMode="FirstLastPreviousNextNumeric"
- PageSize="10">
- </sdk:DataPager>
这个时候我就要把ViewModel的代码贴出来了,否则没法讲述清楚。
- namespace MISInfoManage.ViewModels
- {
- public class UserManageViewModel : INotifyPropertyChanged
- {
- private bool isFirstLoad;
- public UserManageView userManageView;
- DataPagerTrigger dataPagertrigger;
- List<int> list = new List<int>();
- public UserManageViewModel()
- {
- this.InitData(1, 10);
- }
- public UserManageViewModel(UserManageView userManageView)
- : this()
- {
- isFirstLoad = true;
- this.userManageView = userManageView;
- this.dataPagertrigger = new DataPagerTrigger(this);
- this.AttachTrigger();
- this.SetTitle();
- this.UserNo = "请输入用户名";
- }
- #region property
- /*此处省略部分代码*/ #endregion
- #region method
- private void AttachTrigger()
- {
- this.dataPagertrigger.Attach(this.userManageView.dataPager);
- }
- private void SetTitle()
- {
- Image image = new Image();
- image.Source = new BitmapImage(new Uri("../Images/drag.png", UriKind.Relative));
- image.Height = 30;
- image.Width = 20;
- TextBlock textBlock = new TextBlock();
- textBlock.Margin = new Thickness(5, 0, 0, 0);
- textBlock.Text = "用户管理";
- textBlock.FontSize = 14;
- textBlock.FontWeight = FontWeights.Bold;
- StackPanel stackPanel = new StackPanel();
- stackPanel.Orientation = Orientation.Horizontal;
- stackPanel.Background = new RadialGradientBrush(Colors.Gray, Colors.Brown);
- stackPanel.Width = this.userManageView.Width;
- stackPanel.Children.Add(image);
- stackPanel.Children.Add(textBlock);
- this.userManageView.Title = stackPanel;
- }
- private void GetUserList(UserRequest request, EventHandler<GetUserListCompletedEventArgs> callback)
- {
- UserManageService.UserManageServiceClient client = new UserManageServiceClient();
- client.GetUserListCompleted += callback;
- client.GetUserListAsync(request);
- }
- /*此处省略部分代码*/
- private void GetUserPagedCollection(int totalCount)
- {
- list.Clear();
- for (int i = 0; i < totalCount; i++)
- {
- list.Add(i);
- }
- PagedCollectionView pagedCollectionView = new PagedCollectionView(list);
- this.userManageView.dataPager.Source = pagedCollectionView;
- isFirstLoad = false;
- }
- public void GetDataByPage(int pageIndex, int pageSize)
- {
- UserRequest userRequest = new UserRequest()
- {
- PageIndex = pageIndex,
- PageSize = pageSize
- };
- this.GetUserList(userRequest, (obj, args) =>
- {
- if (args.Error == null)
- {
- this.UserResponse = args.Result;
- this.UserList = userResponse.UserList;
- if (isFirstLoad)
- {
- this.GetUserPagedCollection(this.UserResponse.RecordCount);
- }
- }
- });
- }
- private void InitData(int pageIndex, int pageSize)
- {
- this.GetDataByPage(pageIndex,pageSize);
- this.GetUserStateList();
- this.GetUserTypeList();
- }
- #endregion
- /*此处省略部分代码*/ }
- }
在ViewModel的代码中,我们发现了这样一段代码
- this.dataPagertrigger = new DataPagerTrigger(this);
- this.AttachTrigger();
- private void AttachTrigger()
- {
- this.dataPagertrigger.Attach(this.userManageView.dataPager);
- }
这段代码就是在初始化ViewModel的时候,将dataPager控件附加给dataPagertrigger。我们看看dataPagertrigger代码
- public class DataPagerTrigger : TriggerBase<UIElement>
- {
- public UserManageViewModel userManageViewModel;
- public DataPagerTrigger(UserManageViewModel userManageViewModel)
- {
- this.userManageViewModel = userManageViewModel;
- }
- protected override void OnAttached()
- {
- base.OnAttached();
- if (this.AssociatedObject is DataPager)
- {
- (this.AssociatedObject as DataPager).PageIndexChanged += this.PageIndexChanged;
- }
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- if (this.AssociatedObject is DataPager)
- {
- (this.AssociatedObject as DataPager).PageIndexChanged -= this.PageIndexChanged;
- }
- }
- private void PageIndexChanged(object sender, EventArgs e)
- {
- DataPager dataPager = sender as DataPager;
- this.userManageViewModel.GetDataByPage(dataPager.PageIndex, dataPager.PageSize);
- }
- }
这个类继承自TriggerBase,我们给其注册了一个PageIndexChanged 事件,当DataPager控件页码变化时,调用PageIndexChanged方法,在这个方法里调用userManageViewModel的GetDataByPage方法。在GetDataByPage方法中先获取用户列表,判断如果是第一次加载,就给 DataPager控件附一个PagedCollectionView类型的一个source,在这里通过模拟,可以有效的进行分页,因为目前我发现这个分页控件只能客户端分页。所以模拟一个List<int>,它的总条数==表的总记录。这样就实现了服务端分页,如果谁想要源码,可以加入群205217091。我们看看分页的效果
最后大家可能注意到弹出界面的Title和以前的不一样,这里你可以自定义,如ViewModel中的SetTitle方法。好了,时间不早了,洗洗睡!
本文来自BruceAndLee的博客,原文地址:http://leelei.blog.51cto.com/856755/950748
http://silverlightchina.net/html/tips/2012/0806/17928_2.html