2018年01月11日 23:01
原创作品,转载时请务必以超链接形式标明文章原始出处,否则将追究法律责任。

这个行业里,山外有山,人外有人,忽然觉得自己是个菜鸟。


今天我们通过用户管理模块来系统的讲述关于Action,Trigger的使用。首先还是先上一张图,调一下胃口。

225708129.jpg

这个图中的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>

在这里布局我们就不说了,相信看过前几篇的都明白。首先我们看有这么一段代码

<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: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的项模板,在这里你可以自定义你想要的模版。在这里我放了一个图片和一个文本。如下图所示

233301292.jpg

怎么样,很不错吧。最后在这列我们重点看看分页。我这可是服务端分页。

<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。我们看看分页的效果

235645894.jpg

最后大家可能注意到弹出界面的Title和以前的不一样,这里你可以自定义,如ViewModel中的SetTitle方法。好了,时间不早了,洗洗睡!

发表评论
匿名  
用户评论
暂无评论