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

扯面工程师,配菜工程师,门迎工程师,当今世界工程师可真多啊。

今天我们把系统参数管理模块翻译成Silverlight项目。首先请看两张图

224430272.png

再看看翻译好的图

224454491.png

其实我们看到了第二张图用到了treeView,Popup,这节主要讲的还是MVVM,不过和以往有所不同。先看看前台代码

<controls:ChildWindow x:Class="MISInfoManage.CodeManageView" 
    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:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
    xmlns:resource="clr-namespace:MISInfoManage.Resources" 
    xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" 
    xmlns:Primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows" 
    mc:Ignorable="d" Width="700" Height="400" FontSize="13" 
    Title="系统参数管理"> 
    <controls:ChildWindow.Resources> 
        <resource:CodeManageResource x:Key="CodeManageResource"/> 
        <Style x:Key="ColumnHeaderStyle" TargetType="sdk:DataGridColumnHeader"> 
            <Setter Property="Height" Value="25"></Setter> 
        </Style> 
    </controls:ChildWindow.Resources> 
    <Grid x:Name="LayoutRoot" Background="White"> 
        <Grid.RowDefinitions> 
            <RowDefinition Height="*"></RowDefinition> 
        </Grid.RowDefinitions> 
        <Grid.ColumnDefinitions> 
            <ColumnDefinition Width="Auto"></ColumnDefinition> 
            <ColumnDefinition Width="*"></ColumnDefinition> 
            <ColumnDefinition Width="0"></ColumnDefinition> 
        </Grid.ColumnDefinitions> 
        <Border BorderBrush="AliceBlue" Width="150" Grid.Row="0" Grid.Column="0" BorderThickness="1" CornerRadius="2" Margin="0,0,10,0"> 
            <toolkit:TreeView x:Name="treeViewCode"></toolkit:TreeView> 
        </Border> 
        <sdk:DataGrid Grid.Row="0" Grid.Column="1" 
                          BorderBrush="Black" BorderThickness="1" 
                          IsReadOnly="True" x:Name="dgCodeList" 
                          AutoGenerateColumns="False" 
                          AlternatingRowBackground="Gray" 
                          CanUserReorderColumns="True" 
                          VerticalScrollBarVisibility="Auto" 
                          HorizontalScrollBarVisibility="Auto"   
                          SelectionMode="Single" 
                          SelectedItem="{Binding CodeEntity,Mode=TwoWay}" 
                          ItemsSource="{Binding CodeList,Mode=OneWay}" 
                          CanUserSortColumns="True"> 
            <sdk:DataGrid.Columns> 
                <sdk:DataGridTemplateColumn Header="选择"> 
                    <sdk:DataGridTemplateColumn.CellTemplate> 
                        <DataTemplate> 
                            <CheckBox HorizontalAlignment="Center"></CheckBox> 
                        </DataTemplate> 
                    </sdk:DataGridTemplateColumn.CellTemplate> 
                </sdk:DataGridTemplateColumn> 
                <sdk:DataGridTextColumn Binding="{Binding data,Mode=OneWay}" HeaderStyle="{StaticResource ColumnHeaderStyle}" Header="数据值"/> 
                <sdk:DataGridTextColumn Binding="{Binding ename,Mode=OneWay}" HeaderStyle="{StaticResource ColumnHeaderStyle}" Header="英文代码"/> 
                <sdk:DataGridTextColumn Binding="{Binding cname,Mode=OneWay}" HeaderStyle="{StaticResource ColumnHeaderStyle}" Header="中文代码"/> 
                <sdk:DataGridTextColumn Binding="{Binding display_content,Mode=OneWay}" HeaderStyle="{StaticResource ColumnHeaderStyle}" Header="显示值"/> 
                <sdk:DataGridTemplateColumn Header="操作"> 
                    <sdk:DataGridTemplateColumn.CellTemplate> 
                        <DataTemplate> 
                            <StackPanel> 
                                <StackPanel.Resources> 
                                    <Style x:Key="buttonStyle" TargetType="Button"> 
                                        <Setter Property="Template"> 
                                            <Setter.Value> 
                                                <ControlTemplate TargetType="Button"> 
                                                    <Border CornerRadius="2" BorderThickness="1" BorderBrush="Red"> 
                                                        <StackPanel Orientation="Horizontal" Background="Turquoise"> 
                                                            <StackPanel.Effect> 
                                                                <DropShadowEffect Color="Black" Direction="270" ShadowDepth="5"   
                                                                    BlurRadius="5" Opacity="0.5"> 
                                                                </DropShadowEffect> 
                                                            </StackPanel.Effect> 
                                                            <Image Source="/MISInfoManage;component/Images/drag.png" Height="25" Width="60"/> 
                                                            <TextBlock Text="修改" Foreground="Brown" FontWeight="Bold"></TextBlock> 
                                                        </StackPanel> 
                                                    </Border> 
                                                </ControlTemplate> 
                                            </Setter.Value> 
                                        </Setter> 
                                    </Style> 
                                </StackPanel.Resources> 
                                <Button Content="Button1"  Style="{StaticResource buttonStyle}"/> 
                            </StackPanel> 
                        </DataTemplate> 
                    </sdk:DataGridTemplateColumn.CellTemplate> 
                </sdk:DataGridTemplateColumn> 
            </sdk:DataGrid.Columns> 
        </sdk:DataGrid> 
        <Primitives:Popup x:Name="CodeInfoPop" Grid.Row="0" Grid.Column="2"> 
            <StackPanel Width="200"> 
                <StackPanel.Style> 
                    <Style TargetType="StackPanel"> 
                        <Setter Property="Background" Value="Brown"></Setter> 
                    </Style> 
                </StackPanel.Style> 
                <StackPanel.Effect> 
                    <DropShadowEffect 
                    Color="Black" Direction="300" ShadowDepth="10" BlurRadius="5" Opacity="0.6"> 
                    </DropShadowEffect> 
                </StackPanel.Effect> 
                <StackPanel Orientation="Horizontal"> 
                    <TextBlock Text="{Binding Tb_Ename,Source={StaticResource CodeManageResource}}"/> 
                    <TextBlock Text="{Binding CodeEntity.ename}" Margin="5,0,0,0"/> 
                </StackPanel> 
                <StackPanel Orientation="Horizontal"> 
                    <TextBlock Text="{Binding Tb_CNm,Source={StaticResource CodeManageResource}}"/> 
                    <TextBlock Text="{Binding CodeEntity.cname}" Margin="5,0,0,0"/> 
                </StackPanel> 
                <StackPanel Orientation="Horizontal"> 
                    <TextBlock Text="{Binding Tb_Display,Source={StaticResource CodeManageResource}}"/> 
                    <TextBlock Text="{Binding CodeEntity.display_content}" Margin="5,0,0,0"/> 
                </StackPanel> 
            </StackPanel> 
        </Primitives:Popup> 
    </Grid> 
</controls:ChildWindow>

最普遍的布局方式Grid+StackPanel。记得上次好像我在写博客的时候,给每个控件都设置了FontSize,其实这个是没有必要的,只需要在controls:ChildWindow节点中设置,那么整个页面的控件的FontSize都起作用。再往下看,有这样的一些节点

<Style x:Key="buttonStyle" TargetType="Button">...</Style>里面有这么一段代码

<ControlTemplate TargetType="Button"> 
        <Border CornerRadius="2" BorderThickness="1" BorderBrush="Red"> 
               <StackPanel Orientation="Horizontal" Background="Turquoise"> 
                     <StackPanel.Effect> 
                          <DropShadowEffect Color="Black" Direction="270" ShadowDepth="5"   
                                       BlurRadius="5" Opacity="0.5"> 
                          </DropShadowEffect> 
                     </StackPanel.Effect> 
                     <Image Source="/MISInfoManage;component/Images/drag.png" Height="25" Width="60"/> 
                     <TextBlock Text="修改" Foreground="Brown" FontWeight="Bold"></TextBlock> 
               </StackPanel> 
        </Border> 
</ControlTemplate>

这个是定义一个Button的控件模版。在这里我们给StackPanel定义了一个特效DropShadowEffect ,一个阴影效果。在这个模板中我放置了一个图片和一个文本,大家看看上面的图就知道了。整个一按钮变成如此摸样,所以在我们做开发的时候,可以通过设置ControlTemplate来定制我们按钮或者其他控件的模版。在按钮中,我们只需要设置Style="{StaticResource buttonStyle}"即可。再往下走,有这么一段代码

<Primitives:Popup x:Name="CodeInfoPop" Grid.Row="0" Grid.Column="2">这个节点正是定义我们的Popup,当DataGrid的SelectionChanged触发时,弹出Popup。注意Popup这里需要引用System.Windows.Controls.Primitives。好了前台没什么,就这么些。我们看看页面后台,代码如下

namespace MISInfoManage  
{  
    public partial class CodeManageView : ChildWindow  
    {  
        CodeManageModel codeManage;  
        public CodeManageView()  
        {  
            InitializeComponent();  
            codeManage = new CodeManageModel(this);  
            this.LayoutRoot.DataContext = codeManage;  
        }  
    }  
}

我勒了个去,怎么就这么点代码,我告诉你,就这么点代码。我们这次使用了Behavior,那么就不会出现页面后台事件。看看ViewModel到底都干了些什么。

namespace MISInfoManage.ViewModels  
{  
    public class CodeManageModel : INotifyPropertyChanged  
    {  
        public ChildWindow userControl;  
        CodeManageServiceClient client;  
        SelectionChangedBehavior mouseRightButtonBrhavior;  
        public CodeManageModel()  
        {  
            client = new CodeManageServiceClient();  
            this.GetCodesList((obj, args) =>  
            {  
                this.CodesList = args.Result;  
                this.BuildTree();  
            });  
        }  
 
        public CodeManageModel(ChildWindow userControl)  
            : this()  
        {  
            this.userControl = userControl;  
            this.mouseRightButtonBrhavior = new SelectionChangedBehavior(this);  
            mouseRightButtonBrhavior.Attach((userControl as CodeManageView).dgCodeList);  
            this.SetContextMenu();  
        }  
 
        private List<Codes> codesList;  
        public List<Codes> CodesList  
        {  
            get 
            {  
                return codesList;  
            }  
            set 
            {  
                codesList = value;  
                NotifyPropertyChange("CodesList");  
            }  
        }  
 
        private List<Codes> codeList;  
        public List<Codes> CodeList  
        {  
            get 
            {  
                return codeList;  
            }  
            set 
            {  
                codeList = value;  
                NotifyPropertyChange("CodeList");  
            }  
        }  
 
        private Codes codeEntity;  
        public Codes CodeEntity  
        {  
            set 
            {  
                codeEntity = value;  
                NotifyPropertyChange("CodeEntity");  
            }  
            get 
            {  
                return codeEntity;  
            }  
        }  
 
        public void GetCodesList(EventHandler<GetCodeListCompletedEventArgs> handler)  
        {  
            client.GetCodeListCompleted += handler;  
            client.GetCodeListAsync();  
        }  
 
        public void GetCodeListByCondition(string ename, EventHandler<GetCodeListByConditionCompletedEventArgs> handler)  
        {  
            client.GetCodeListByConditionCompleted += handler;  
            client.GetCodeListByConditionAsync(ename);  
        }  
 
        public void BuildTree()  
        {  
            TreeViewItem mainitem = new TreeViewItem();  
            mainitem.Header = "系统参数";  
            mainitem.IsExpanded = true;  
            List<Codes> codeList = null;  
            if (this.CodesList != null && this.CodesList.Count > 0)  
            {  
                codeList = this.CodesList.Distinct<Codes>(new EqualityCompare()).ToList();  
                codeList.ForEach(c =>  
                {  
                    TreeViewItem treeViewItem = new TreeViewItem();  
                    StackPanel stackPanel = new StackPanel();  
                    stackPanel.Orientation = Orientation.Horizontal;  
                    TextBlock textBlock = new TextBlock();  
                    textBlock.Text = c.cname;  
                    textBlock.Tag = c.ename;  
                    textBlock.MouseLeftButtonDown += this.LoadCodeByID;  
                    Image image = new Image();  
                    image.Width = 12;  
                    image.Height = 12;  
                    image.Margin = new Thickness(0, 0, 5, 0);  
                    image.Source = new BitmapImage(new Uri("../Images/Windows.jpg", UriKind.Relative));  
                    stackPanel.Children.Add(image);  
                    stackPanel.Children.Add(textBlock);  
                    treeViewItem.Header = stackPanel;  
                    mainitem.Items.Add(treeViewItem);  
                });  
                (this.userControl as CodeManageView).treeViewCode.Items.Add(mainitem);  
            }  
        }  
 
        private void LoadCodeByID(object sender, MouseButtonEventArgs e)  
        {  
            TextBlock textBlock = sender as TextBlock;  
            string ename = textBlock.Tag.ToString();  
            this.GetCodeListByCondition(ename, (obj, args) =>  
            {  
                this.CodeList = args.Result;  
            });  
        }  
 
        private void SetContextMenu()  
        {  
            ContextMenu contextMenu = new ContextMenu();  
            MenuItem menuItem = new MenuItem();  
            menuItem.Tag = "Modify";  
            TextBlock textBlock = new TextBlock();  
            textBlock.Text = "修改";  
            menuItem.Header = textBlock;  
            contextMenu.Items.Add(menuItem);  
            menuItem = new MenuItem();  
            textBlock = new TextBlock();  
            textBlock.Text = "添加";  
            menuItem.Tag = "Add";  
            menuItem.Header = textBlock;  
            contextMenu.Items.Add(menuItem);  
            menuItem = new MenuItem();  
            textBlock = new TextBlock();  
            textBlock.Text = "关闭";  
            menuItem.Tag = "Close";  
            menuItem.Header = textBlock;  
            menuItem.Click += delegate(object sender, RoutedEventArgs e)  
            {  
                (userControl as CodeManageView).Close();  
            };  
            contextMenu.Items.Add(menuItem);  
            ContextMenuService.SetContextMenu((this.userControl as CodeManageView).dgCodeList, contextMenu);  
        }  
 
        public event PropertyChangedEventHandler PropertyChanged;  
        private void NotifyPropertyChange(string property)  
        {  
            if (PropertyChanged != null)  
            {  
                PropertyChanged(this, new PropertyChangedEventArgs(property));  
            }  
        }  
    }  
 
    public class EqualityCompare : IEqualityComparer<Codes>  
    {  
        public bool Equals(Codes code1, Codes code2)  
        {  
            return code1.ename.Equals(code2.ename);  
        }  
 
        public int GetHashCode(Codes code)  
        {  
            return code.ename.GetHashCode();  
        }  
    }  
}

首先进入构造函数,初始化我们的TreeView,初始化TreeView的时候有这么个代码codeList = this.CodesList.Distinct<Codes>(new EqualityCompare()).ToList();去除List<T>重复。我们知道对象的值都一样,不代表对象一样。所以我们需要实现IEqualityComparer<T>接口,Equals方法确定你要Distinct的规则,在这里就是Ename不相等。GetHashCode必须返回Ename的HashCode。再往下走,这么一段代码

this.mouseRightButtonBrhavior = new SelectionChangedBehavior(this);  
mouseRightButtonBrhavior.Attach((userControl as CodeManageView).dgCodeList);

在这里使用到了Behavior,我们为DataGrid定义了一个行为,我们来看看这个行为

namespace MISInfoManage.Behavior  
{  
    public class SelectionChangedBehavior : Behavior<DataGrid>  
    {  
        CodeManageModel _codeManageModel;  
        public SelectionChangedBehavior():base()  
        {}  
        public SelectionChangedBehavior(CodeManageModel codeManageModel)  
            : this()  
        {  
            _codeManageModel = codeManageModel;  
        }  
        protected override void OnAttached()  
        {  
            base.OnAttached();  
            this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;  
            this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;  
        }  
 
        protected override void OnDetaching()  
        {  
            base.OnDetaching();  
            this.AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;  
            this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;  
        }  
 
        private void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)  
        {  
            if (_codeManageModel.CodeEntity != null)  
            {  
                Popup popup = (this._codeManageModel.userControl as CodeManageView).CodeInfoPop;  
                popup.HorizontalOffset = 20;  
                popup.VerticalOffset = 70;  
                popup.IsOpen = true;  
            }  
        }  
 
        private void AssociatedObject_MouseMove(object sender,MouseEventArgs e)  
        {  
            Popup popup = (this._codeManageModel.userControl as CodeManageView).CodeInfoPop;  
            popup.IsOpen = false;  
        }  
    }  
}

在这里我只说一个基类,其他的就不往出贴了,大家自己看

namespace System.Windows.Interactivity  
{  
    // Summary:  
    //     Encapsulates state information and zero or more ICommands into an attachable  
    //     object.  
    //  
    // Type parameters:  
    //   T:  
    //     The type the System.Windows.Interactivity.Behavior<T> can be attached to.  
    //  
    // Remarks:  
    //     Behavior is the base class for providing attachable state and commands to  
    //     an object.  The types the Behavior can be attached to can be controlled by  
    //     the generic parameter.  Override OnAttached() and OnDetaching() methods to  
    //     hook and unhook any necessary handlers from the AssociatedObject.  
    public abstract class Behavior<T> : Behavior where T : System.Windows.DependencyObject  
    {  
        // Summary:  
        //     Initializes a new instance of the System.Windows.Interactivity.Behavior<T>  
        //     class.  
        protected Behavior();  
 
        // Summary:  
        //     Gets the object to which this System.Windows.Interactivity.Behavior<T> is  
        //     attached.  
        protected T AssociatedObject { get; }  
    }  
}

这里的的AssociatedObject就是泛型T,并且它是System.Windows.DependencyObject类型的。Behavior类是个抽象类,具有Attach,Detach,OnAttached,OnDetaching方法。OnAttached在Attach方法调用以后生效。所以这段代码就实现了DataGrid的SelectionChanged和MouseMove事件,在SelectionChanged以后弹出Popup,MouseMove以后隐藏Popup。这种方式是不是很有效的实现了页面UI和逻辑的分离。再往下走,有个SetContextMenu方法,正是给DataGrid附加一个弹出菜单。在菜单构造好以后,需要调用ContextMenuService.SetContextMenu((this.userControl as CodeManageView).dgCodeList, contextMenu);,第一个参数是一个DependencyObject类型的对象,第二个是ContextMenu对象。类似于这样的内置对象还有FocusManager,它的方法FocusManager.GetFocusedElement()可以直接找到页面获得焦点的元素。我们来看看这个菜单的效果,

233119741.png

当我们点击关闭的时候,将会关闭该页面。OK,今天就讲到这里,时间也不早了,洗洗睡。

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