• Command之必杀技——AttachedBehavior


    (一)前言

          最简单的Command,是由Prism为Silverlight提供的,执行ButtonBase的Click事件。

          我在另一篇文章《MVP的前生来世》中已经使用过这个Command了。温习一下,基于MVVM实现,分为两个步骤:


          1) 在ViewModel中声明这个Command,并定义该Command所对应的方法:

            public DelegateCommand<RoutedEventArgs> SaveCommand = new DelegateCommand<RoutedEventArgs>(OnSave);

            
    void OnSave(RoutedEventArgs e)
            {
                
    //do something
            }

          2) 在XAML中的Button中,使用Command语法,取代原先的Click事件(这里是Silverlight):

    <Button Height="20" cmd:Click.Command="{Binding SaveCommand}" Content="Save"/>

          更多Prism中关于Command的详细内容,请参见我的另一篇文章《Prism深入研究之Command》。

    (二)WPF提供的ICommand接口

          WPF中有一个ButtonBase基类,这个基类中含有一个Command属性,它是ICommand类型的,还包括一个CommandParameter属性和一个Click事件:

            public abstract class ButtonBase : ContentControl, ICommandSource
            
    {
                
    public ICommand Command getset; }

                
    public object CommandParameter getset; }

                
    public event RoutedEventHandler Click;
            }


            
    public interface ICommand
            
    {
                
    event EventHandler CanExecuteChanged;

                
    bool CanExecute(object parameter);

                
    void Execute(object parameter);
            }

          触发Click事件会直接执行Command属性中定义的Execute方法,所以像Button(如上所示)、RadioButton、ToggleButton、CheckBox、RepeatButton和GridViewColumnHeader,这些直接或间接从这个基类派生的控件,都可以使用Command属性,而不使用事件机制。

          在此,要订正一点,并不是说点击Button就不触发Click事件了,Click事件还是要触发,只是我们不再手动为Click事件添加方法了,而是把相应的逻辑添加到Command的Execute方法中。

    原先的事件模型: 

    <Button x:Name="button1" Click="button1_Click" />

            this.button1.Click += new RoutedEventHandler(this.button1_Click);

            
    private void button1_Click(object sender, RoutedEventArgs e)
            {
                MessageBox.Show(
    "After click");
            }


          现在的Command模型: 

    <Button Command="{Binding}" />
                this.button.DataContext = ClickCommand;
            public DelegateCommand<object> ClickCommand { getprivate set; }

            ClickCommand 
    = new DelegateCommand<object>(OnClick, arg => true);

            
    void OnClick(object e)
            {
                MessageBox.Show(
    "After click");
            }

          信手写了个Demo,以比较二上述两种编程方式的不同,代码下载:WpfApplication10.zip


    (二)Silverlight下ButtonBase的Click实现

          在Silverlight中,虽然也有ICommand接口,但是并未在ButtonBase基类中提供ICommand类型的Command属性,所以不能使用上述机制来“化Event为Command”。

          直到有一天,一位微软MVP提出了AttachedBehavior的思想,才彻底解决了这个问题。

          AttachedBehavior定义:把由控件触发的事件和Presenter(或ViewModel)中的代码相关联。

          AttachedBehavior由两部分组成:一个attached 属性和一个behavior对象。attached 属性建立目标控件和behavior对象之间的关系;behavior对象则监视着目标控件,当目标控件的状态改变或事件触发时,就执行一些操作,我们可以在这里写一些自己的逻辑。

          微软P & P Team根据这一思想,在Prism中成功地将ButtonBase的Click事件转换为了Command。

          实现过程如下:

          1)创建CommandBehaviorBase<T>泛型类,可以将其看作Behavior基类: 

        public class CommandBehaviorBase<T> 
            
    where T: Control

          这里T继承自Control基类,我们后面会看到这样设置的灵活性。

          类图如下所示:

    clip_image002

          我们看到,CommandBehaviorBase<T>中有3个属性,看着眼熟,这不由使我们想起了WPF中ButtonBase的定义: 

        public abstract class ButtonBase
        
    {
            
    public ICommand Command getset; }

            
    public object CommandParameter getset; }

            
    public IInputElement CommandTarget getset; }

            
    //省略一些成员
        }

          就是说,我们把这3个属性从ButtonBase提升到了Control级别,从而可以让这个CommandBehaviorBase<T>泛型类适用于绝大多数控件(这里没有说全部哦,有一些特例我会在下文介绍)。

          TargetObject会在构造函数中初始化,这个属性就是目标控件了。

          CommandBehaviorBase<T>暴露了一个ExecuteCommand方法,可以在它的派生类中调用甚至重写该方法,以执行相应的Command: 

            protected virtual void ExecuteCommand()
            
    {
                
    if (this.Command != null)
                
    {
                    
    this.Command.Execute(this.CommandParameter);
                }

            }

          CommandBehaviorBase<T>的UpdateEnabledState方法是比较有趣的,在设置Command和CommandParameter属性的时候,都会调用该方法,从而根据Command的CanExecute方法返回true/false,决定目标控件的IsEnabled属性。

          2)定义ButtonBaseClickCommandBehavior类,这时一个具体的Behavior,所以它派生自CommandBehaviorBase<ButtonBase>。类的定义及其关系图如下所示:
    clip_image004

        public class ButtonBaseClickCommandBehavior : CommandBehaviorBase<ButtonBase>
        
    {
            
    public ButtonBaseClickCommandBehavior(ButtonBase clickableObject) : base(clickableObject)
            
    {
                clickableObject.Click 
    += OnClick;
            }

          
            
    private void OnClick(object sender, System.Windows.RoutedEventArgs e)
            
    {
                ExecuteCommand();
            }

        }

          这个类很简单,在它的构造函数中传递ButtonBase目标控件,并把OnClick方法绑到这个目标控件的Click事件上。

          至此,我们创建了一个Behavior,它是为ButtonBase的Click事件量身打造的。

          3)最后我们来创建attached属性,并建立目标控件和behavior对象之间的关系。

          为此,需要为ButtonBase的Click事件单独创建一个Click类。这个Click类是用于在XAML绑定Command的: 

    public static class Click

          我们要在类中注册一个依赖属性(Dependency Property,简称DP)ClickCommandBehavior,用来将之前创建的ButtonBaseClickCommandBehavior永久性保存在类中,而GetOrCreateBehavior方法则用来得到这个behavior:

        public static class Click
        
    {
            
    private static readonly DependencyProperty ClickCommandBehaviorProperty = DependencyProperty.RegisterAttached(
                
    "ClickCommandBehavior",
                
    typeof(ButtonBaseClickCommandBehavior),
                
    typeof(Click),
                
    null);

            
    private static ButtonBaseClickCommandBehavior GetOrCreateBehavior(ButtonBase buttonBase)
            
    {
                ButtonBaseClickCommandBehavior behavior 
    = buttonBase.GetValue(ClickCommandBehaviorProperty) as ButtonBaseClickCommandBehavior;
                
    if (behavior == null)
                
    {
                    behavior 
    = new ButtonBaseClickCommandBehavior(buttonBase);
                    buttonBase.SetValue(ClickCommandBehaviorProperty, behavior);
                }


                
    return behavior;
            }

        }

          为了能写出下面这样的绑定语法,Click必须是静态的(static),其中cmd是对Click所在namespace的引用: 

    <Button cmd:Click.Command="{Binding SaveCommand}" cmd:Click.CommandParameter="BaoBao" />

          我们要在Click类中添加两个属性Command和CommandParameter,而且为了实现持久性绑定,要把它们设计成DP: 

            public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
                
    "Command",
                
    typeof(ICommand),
                
    typeof(Click),
                
    new PropertyMetadata(OnSetCommandCallback));

            
    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached(
                
    "CommandParameter",
                
    typeof(object),
                
    typeof(Click),
                
    new PropertyMetadata(OnSetCommandParameterCallback));

          我们分别为这两个DP设计了回调方法: 

            private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            
    {
                ButtonBase buttonBase 
    = dependencyObject as ButtonBase;
                
    if (buttonBase != null)
                
    {
                    ButtonBaseClickCommandBehavior behavior 
    = GetOrCreateBehavior(buttonBase);
                    behavior.Command 
    = e.NewValue as ICommand;
                }

            }


            
    private static void OnSetCommandParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            
    {
                ButtonBase buttonBase 
    = dependencyObject as ButtonBase;
                
    if (buttonBase != null)
                
    {
                    ButtonBaseClickCommandBehavior behavior 
    = GetOrCreateBehavior(buttonBase);
                    behavior.CommandParameter 
    = e.NewValue;
                }

            }

          它们分别调用GetOrCreateBehavior方法,以获取永久性存储在类中的behavior实例,并将在XAML中设置的Command和CommandParameter保存在behavior实例中。

          至此,一个AttachedBehavior设计完毕。

          Prism提供的实现位于Composite.Presentation.Desktop这个项目中,如图所示:

    clip_image006


    (三)对Click的重构

          Prism为我们提供的ButtonBase的Click实现,是一个很不错的参考,为我们提供了AttachedBehavior的编程模型。

          但是,根据我的经验,CommandParameter一般是不会使用的,因为我们在实际编程中,是有机会在Presenter(或ViewModel)中把Command所需要的参数传递到要执行的OnExecute方法的。所以,我尝试着简化Click的编程模型,就是把和CommandParameter、TargetObject有关的逻辑全都删除:

          CommandBehaviorBase类修改如下: 

        public class CommandBehaviorBase<T> 
            
    where T: Control
        
    {
            
    private ICommand command;

            
    public ICommand Command
            
    {
                
    get return command; }
                
    set
                
    {
                    
    this.command = value;
                }

            }


            
    protected virtual void ExecuteCommand()
            
    {
                
    if (this.Command != null)
                
    {
                    
    this.Command.Execute(null);
                }

            }

        }

          Click类修改如下: 

        public static class Click
        
    {
            
    private static readonly DependencyProperty ClickCommandBehaviorProperty = DependencyProperty.RegisterAttached(
                
    "ClickCommandBehavior",
                
    typeof(ButtonBaseClickCommandBehavior),
                
    typeof(Click),
                
    null);

            
    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
                
    "Command",
                
    typeof(ICommand),
                
    typeof(Click),
                
    new PropertyMetadata(OnSetCommandCallback));

            
    public static void SetCommand(ButtonBase buttonBase, ICommand command)
            
    {
                buttonBase.SetValue(CommandProperty, command);
            }


            
    public static ICommand GetCommand(ButtonBase buttonBase)
            
    {
                
    return buttonBase.GetValue(CommandProperty) as ICommand;
            }


            
    private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            
    {
                ButtonBase buttonBase 
    = dependencyObject as ButtonBase;
                
    if (buttonBase != null)
                
    {
                    ButtonBaseClickCommandBehavior behavior 
    = GetOrCreateBehavior(buttonBase);
                    behavior.Command 
    = e.NewValue as ICommand;
                }

            }


            
    private static ButtonBaseClickCommandBehavior GetOrCreateBehavior(ButtonBase buttonBase)
            
    {
                ButtonBaseClickCommandBehavior behavior 
    = buttonBase.GetValue(ClickCommandBehaviorProperty) as ButtonBaseClickCommandBehavior;
                
    if (behavior == null)
                
    {
                    behavior 
    = new ButtonBaseClickCommandBehavior(buttonBase);
                    buttonBase.SetValue(ClickCommandBehaviorProperty, behavior);
                }


                
    return behavior;
            }

        }

          ButtonBaseClickCommandBehavior类修改如下:

        public class ButtonBaseClickCommandBehavior : CommandBehaviorBase<ButtonBase>
        
    {
            
    public ButtonBaseClickCommandBehavior(ButtonBase clickableObject)
            
    {
                clickableObject.Click 
    += OnClick;
            }

          
            
    private void OnClick(object sender, System.Windows.RoutedEventArgs e)
            
    {
                ExecuteCommand();
            }

        }

          精简之后的逻辑,是不是很清晰了呢?所以,我们在编写自己的Command时,功能够用就好,别搞得太复杂。

          我在此基础上写了一个示例,运行良好。示例下载:SLPrismApplicationForClickCommand.zip


    (四)定义自己的Command

          说了这么多,无非是为编写我们自己的Command做准备,不光是Silverlight要用到AttachedBehavior,就连WPF也照用不误,因为WPF只在ButtonBase中实现了Command,而并没有在其他控件中建立类似的机制。

          授人以渔,不如授人以渔。看到有朋友在搜集各种AttachedBehavior以备不时之需,其实没有必要,完全可以随心所欲进行定制。下面将介绍5种自定义AttachedBehavior,都是我在项目中所遇到的,基本涵盖了所有类型的控件事件。


    (五)ButtonBase的MouseOver事件

          其实很简单,只要把上面Click那个AttachedBehavior中相应的Click事件修改为MouseOver事件就可以了,然后当你把鼠标移动到Button,就会执行相应的命令。

          第一次重构后的代码下载,为了代码可读性更强,我还把带有Click的名字都改为了MouseOver:SLAttachedBehavior_1.zip
          但是,仔细观察新的代码,我们发现,它只适用于ButtonBase。而MouseOver事件是在UIELement这个类中定义的,类的关系如图所示:


          所以,我们可以把MouseOver的AttachedBehavior的范围做的更广泛一些——适用于所有UIELement。

          修改CommandBehaviorBase<T>,让T的约束范围限制为DependencyObject: 

        public class CommandBehaviorBase<T>
            
    where T : DependencyObject

          修改UIElementMouseMoveCommandBehavior,使其派生于CommandBehaviorBase<UIElement>,并修改其构造函数,即将ButtonBase修改为UIElement:

        public class UIElementMouseMoveCommandBehavior : CommandBehaviorBase<UIElement>
        
    {
            
    public UIElementMouseMoveCommandBehavior(UIElement obj)
            
    {
                obj.MouseMove 
    += OnMouseMove;
            }


            
    private void OnMouseMove(object sender, System.Windows.RoutedEventArgs e)
            
    {
                ExecuteCommand();
            }

        }

          修改MouseMove类,将ButtonBase修改为UIElement。

          示例代码下载如下:SLAttachedBehavior_2.zip

          一切工作良好。至此,我们得出一个结论:对一个事件,先找到它是由哪个类中提供的,然后将CommandBehaviorBase<T>中的T相应替换为这个类,其它地方照猫画虎即可。


    (六)TextBox的TextChanged事件

          有了前面的指导思想,这个例子实现起来就简单了。

    TextBox的TextChanged事件是定义在TextBox中生的,是TextBox所独有的,没有通用性,所以要将CommandBehaviorBase<T>中的T替换为TextBox: 

        public class TextBoxTextChangedCommandBehavior : CommandBehaviorBase<TextBox>
        
    {
            
    public TextBoxTextChangedCommandBehavior(TextBox obj)
            
    {
                obj.TextChanged 
    += OnTextChanged;
            }


            
    private void OnTextChanged(object sender, TextChangedEventArgs e)
            
    {
                ExecuteCommand();
            }

        }

          TextBox是从Control派生的,所以将CommandBehaviorBase<T>的定义修改为: 

        public class CommandBehaviorBase<T>
            
    where T : Control

          接下来定义静态类TextChanged,只要把上面那个例子中MouseMove类的MouseMove全都替换为TextChanged,把UIElement全都替换为TextBox即可。

          示例代码下载:SLAttachedBehavior_3.zip

          这个AttachedBehavior在《Command探究》一文中 Dirty Save示例中会使用到。


    (七)TextBlock的MouseLeftButtonUp事件

          TextBlock控件没有Click事件,但是可以用MouseLeftButtonUp事件来模拟。

          如果是单独一个TextBlock的MouseLeftButtonUp事件,按照上面的编码模式去套,是很容易的。

          沿着TextBlock的继承关系一直向上找,直到在UIElement基类中发现MouseLeftButtonUp事件。

          于是创建适用于所有UIElement的MouseLeftButtonUpCommandBehavior, 

        public class CommandBehaviorBase<T> { }

        
    public class MouseLeftButtonUpCommandBehavior : CommandBehaviorBase<UIElement>
        {
            
    public MouseLeftButtonUpCommandBehavior(UIElement clickableObject)
            {
                clickableObject.MouseLeftButtonUp 
    += OnMouseLeftButtonUp;
            }

            
    private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                ExecuteCommand();
            }
        }

          然后仿照上面例子中的Click类,创建一个MouseLeftButtonUp类,实现很简单,把Click类中的Click全都替换为MouseLeftButtonUp,把ButtonBase全都替换为UIElement即可: 

        public static class MouseLeftButtonUp
        
    {
            
    private static readonly DependencyProperty MouseLeftButtonUpCommandBehaviorProperty = DependencyProperty.RegisterAttached(
            
    "MouseLeftButtonUpCommandBehavior",
            
    typeof(MouseLeftButtonUpCommandBehavior),
            
    typeof(MouseLeftButtonUp),
            
    null);

            
    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
            
    "Command",
            
    typeof(ICommand),
            
    typeof(MouseLeftButtonUp),
            
    new PropertyMetadata(OnSetCommandCallback));

            
    public static void SetCommand(UIElement element, ICommand command)
            
    {
                element.SetValue(CommandProperty, command);
            }


            
    public static ICommand GetCommand(UIElement element)
            
    {
                
    return element.GetValue(CommandProperty) as ICommand;
            }


            
    private static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
            
    {
                UIElement element 
    = dependencyObject as UIElement;

                
    if (element != null)
                
    {
                    MouseLeftButtonUpCommandBehavior behavior 
    = GetOrCreateBehavior(element);
                    behavior.Command 
    = e.NewValue as ICommand;
                }

            }


            
    private static MouseLeftButtonUpCommandBehavior GetOrCreateBehavior(UIElement element)
            
    {
                MouseLeftButtonUpCommandBehavior behavior 
    = element.GetValue(MouseLeftButtonUpCommandBehaviorProperty) as MouseLeftButtonUpCommandBehavior;

                
    if (behavior == null)
                
    {
                    behavior 
    = new MouseLeftButtonUpCommandBehavior(element);
                    element.SetValue(MouseLeftButtonUpCommandBehaviorProperty, behavior);
                }


                
    return behavior;
            }

        }

          示例代码下载:WpfApplication3.zip

          这个例子只是让大家熟悉如何自定义AttachedBehavior,相信大家已经掌握了,下面我们研究一些高级货。


    (八)GridView中TextBlock的MouseLeftButtonUp事件

          单个TextBlock的MouseLeftButtonUp事件是很容易实现的。但是,在GridView的模板列中的TextBlock的MouseLeftButtonUp事件,就比较难以实现了。

          如果我们还像过去一样编码,在模板列的TextBlock中绑定这个Command: 

            <data:DataGridTemplateColumn Header="UserName" >
                
    <data:DataGridTemplateColumn.CellTemplate>
                    
    <DataTemplate>
                        
    <TextBlock Text="{Binding Path=UserName}" cmd:MouseLeftButtonUp.Command="{Binding MouseLeftButtonUpCommand}" />
                    
    </DataTemplate>
                
    </data:DataGridTemplateColumn.CellTemplate>
            
    </data:DataGridTemplateColumn>

          但是,点击DataGrid中每一行的UserName列,却发现没有任何反应,就是说,Command失效了。

          错误代码下载:WpfApplication7_error.zip

          怎么办呢?

          有一个馊主意,既然TextBlock是在DataGrid中的,而且DataGrid也具有MouseLeftButtonUp事件,所以不妨在DataGrid添加这个Command,如下所示: 

            <data:DataGrid AutoGenerateColumns="False" Margin="12,12,66,0" Name="dataGrid1"
                ItemsSource
    ="{Binding StudentList, Mode=TwoWay}" Height="140" VerticalAlignment="Top"
                cmd:MouseLeftButtonUp.Command
    ="{Binding MouseLeftButtonUpCommand}"
                
    >

          貌似运行良好哦。

          示例代码下载:WpfApplication7_error2.zip

          但是,大家想过没有,这是治标不治本的办法,如果DataGrid中有多个TextBlock,那该如何分辨是哪个TextBlock触发了MouseLeftButtonUp事件?

          此外,在DataGrid中,不光是TextBlock的MouseLeftButtonUp事件失效,我们将其换成Button的Click事件,会发现也不行。 

            <data:DataGridTemplateColumn Header="Score">
                
    <data:DataGridTemplateColumn.CellTemplate>
                    
    <DataTemplate>
                        
    <Button Content="{Binding Path=Score}" Command="{Binding Path=MouseLeftButtonUpCommand" />
                    
    </DataTemplate>
                
    </data:DataGridTemplateColumn.CellTemplate>
            
    </data:DataGridTemplateColumn>

          这次就不能把Button的Click事件放到DataGrid上了,因为DataGrid可是没有这个Click的哦?

          为什么会这样呢?

          在于我们的绑定语法写的不对。

          1)先来看一下WPF。像DataView、ListView这样的数据集合控件,对于其中的TextBlock、Button这样的控件,它们的Command绑定语法应该写成这样: 

            <data:DataGridTemplateColumn Header="UserName" >
                
    <data:DataGridTemplateColumn.CellTemplate>
                    
    <DataTemplate>
                        
    <TextBlock Text="{Binding Path=UserName}" local:MouseLeftButtonUp.Command="{Binding Path=DataContext.MouseLeftButtonUpCommand, RelativeSource={RelativeSource AncestorType={x:Type data:DataGrid}}}" />
                    
    </DataTemplate>
                
    </data:DataGridTemplateColumn.CellTemplate>
            
    </data:DataGridTemplateColumn>

          就是说,在WPF中,使用到了RelativeResource,将DataGrid中的TextBlock控件的数据源指到外面去(跳出三界外,不在五行中),此时路径要指向DataGrid的DataContext的MouseLeftButtonUpCommand。

          对于Button,也是如法炮制: 

            <data:DataGridTemplateColumn Header="Score">
                
    <data:DataGridTemplateColumn.CellTemplate>
                    
    <DataTemplate>
                        
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                            
    <Button Content="Buy" Command="{Binding Path=DataContext.BuyCommand, RelativeSource={RelativeSource AncestorType={x:Type data:DataGrid}}}" Width="30" Height="20" Cursor="Hand" />
                            
    <Button Content="Sell" Command="{Binding Path=DataContext.SellCommand, RelativeSource={RelativeSource AncestorType={x:Type data:DataGrid}}}" Width="30" Height="20" Cursor="Hand" />
                        
    </StackPanel>
                    
    </DataTemplate>
                
    </data:DataGridTemplateColumn.CellTemplate>
            
    </data:DataGridTemplateColumn>

          示例代码下载:WpfApplication8.zip

          2)再来看一下Silverlight。

          由于Silverlight不支持RelativeResource语法,所以我们要另想别的办法。

          还记得XAML中有一种Resource语法么,我们可以把Command存在Resource中,相当于建立了一个全局变量,让DataGrid中的TextBlock和Button直接指向这个Resource(有点儿直辖市的味道哦)。

          能够存储Command的Resource,要额外花些心思来构思,为此要创建一个名为ObservableCommand的类,以后存储Command就靠它了:

    public class ObservableCommand : ObservableObject<ICommand> { }

          在Xaml中,我们建立3个资源:

            <UserControl.Resources>
                
    <local:ObservableCommand x:Key="BuyCommand" />
                
    <local:ObservableCommand x:Key="SellCommand" />
                
    <local:ObservableCommand x:Key="MouseLeftButtonUpCommand" />
            
    </UserControl.Resources>

          并在后台的代码文件中,在ViewModel的set属性中,手动把它们和ViewModel的Command绑定在一起: 

        public partial class ScoreListView : UserControl
        {
            public ScoreListView()
            {
                InitializeComponent();

                this.ViewModel = new ScoreListViewModel();
                this.ViewModel.View = this;
            }

            public ScoreListViewModel ViewModel
            {
                get
                {
                    return this.DataContext as ScoreListViewModel;
                }

                set
                {
                    this.DataContext = value;

                    ((ObservableCommand)this.Resources["BuyCommand"]).Value = value.BuyCommand;
                    ((ObservableCommand)this.Resources["SellCommand"]).Value = value.SellCommand;
                    ((ObservableCommand)this.Resources["MouseLeftButtonUpCommand"]).Value = value.MouseLeftButtonUpCommand;
                }
            }
        }

          这样,我们就可以在DataGrid的TextBlock和Button中把它们绑定到资源上了: 

            <data:DataGrid AutoGenerateColumns="False" Margin="12" Name="dataGrid1" ItemsSource="{Binding StudentList}">
                
    <data:DataGrid.Columns>
                    
    <data:DataGridTemplateColumn Header="UserName" >
                        
    <data:DataGridTemplateColumn.CellTemplate>
                            
    <DataTemplate>
                                
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                                    
    <TextBlock Text="{Binding Path=UserName}" local:MouseLeftButtonUp.Command="{Binding Path=Value, Source={StaticResource MouseLeftButtonUpCommand}}" />
                                
    </StackPanel>
                            
    </DataTemplate>
                        
    </data:DataGridTemplateColumn.CellTemplate>
                    
    </data:DataGridTemplateColumn>

                    
    <data:DataGridTemplateColumn Header="Score">
                        
    <data:DataGridTemplateColumn.CellTemplate>
                            
    <DataTemplate>
                                
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
                                    
    <Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource BuyCommand}}" Width="30" Height="20" Cursor="Hand" Content="Buy" />
                                    
    <Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource SellCommand}}" Width="30" Height="20" Cursor="Hand" Content="Sell" />
                                
    </StackPanel>
                            
    </DataTemplate>
                        
    </data:DataGridTemplateColumn.CellTemplate>
                    
    </data:DataGridTemplateColumn>
                
    </data:DataGrid.Columns>
            
    </data:DataGrid>

          注意绑定语法:

    <Button Commands:Click.Command="{Binding Path=Value, Source={StaticResource BuyCommand}}" />

          这里绑定的是BuyCommand资源的Value值。

          示例代码下载:SilverlightApplication12.zip


    (九)Popup Window的弹出和关闭事件

          这个例子是由Prism的StockTrader RI提供的,堪称绝世经典之作。

          关于这个功能的介绍,请参见《Prism研究RI分析 之六 PopupView》。别写到RI分析的时候没得写了。


    (十)结语

          AttachedBehavior是好东西啊!虽然有点神秘,但却不是那么费解。希望大家仔细阅读此文,多研究我提供的源码。我尽量把示例做的简单,而没有使用太多的Prism框架。

  • 相关阅读:
    Java并发之线程管理(线程基础知识)
    spring aop使用
    java动态代理
    java深拷贝与浅拷贝
    装饰模式(也叫包装模式)
    Spring基于XML方式的使用
    javaWeb域对象
    静态代理和动态代理
    getAnnotation的一个坑
    (转)文件上传org.apache.tomcat.util.http.fileupload.FileUploadException: Stream closed
  • 原文地址:https://www.cnblogs.com/Jax/p/1581109.html
Copyright © 2020-2023  润新知