包氏波动思想

      有了前面介绍的AttachedBehavior,所有控件的任意方法都可以转换为Command了。

      但是,对于列表控件而言,还有一条解决方案,这包括DataGrid、ListBox、ComboBox等等,而对于CheckBox、RadioButton而言,也可以将其视为由多个选项组成的控件,所以,也在本文涉及的范围之内。

      先来看一个例子:

      (一)DataGrid

      一个DataGrid,选择其中的任意一行,都会在GroupBox中显示这一行的详细信息。对,这是Master-Detail技术:

      在传统窗体编程模型中,使用DataGrid的SelectionChanged事件,可以轻松实现这一功能:       

<data:DataGrid SelectionChanged="dataGrid1_SelectionChanged" … >

 

        private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var selectStudent 
= e.AddedItems[0as Student;
            tbUserName.Text 
= selectStudent.UserName;
            tbScore.Text 
= selectStudent.Score.ToString();
        }

      clip_image002

      代码下载:WpfApplication7_1.zip

      但如今,我们要使用MVP来实现这个小例子,所以像SelectionChanged这样的事件是不能使用的,怎么办呢?

      仔细观察GridView控件,会发现它有一个SelectedItem属性,当SelectionChanged事件触发后,这个属性会被设置为当前选中的那一行。于是,我们在数据(Model)中,可以建立同名的一个属性,将它们进行绑定。那么,选中不同的行,GridView的SelectedItem就会改变,这一“波动”会导致数据(Model)中的相应属性(这里是SelectedStudent)也跟着变动,我们可以在数据(Model)中捕获到这一“波动”,从而执行一些自己的逻辑。 

    <data:DataGrid ItemsSource="{Binding StudentList}" SelectedItem="{Binding SelectedStudent}" … />


    public class ScoreListViewModel : INotifyPropertyChanged
    
{
        
public ScoreListViewModel()
        
{
            
this.StudentList = StudentService.RetrieveStudentList();
            
this.SelectedStudent = this.StudentList[0];
        }


        
public ScoreListView View getset; }

        
private Student selectedStudent;

        
public Student SelectedStudent
        
{
            
get
            
{
                
return this.selectedStudent;
            }

            
set
            
{
                
if (this.selectedStudent != value)
                
{
                    
this.selectedStudent = value;
                    OnPropertyChanged(
"SelectedStudent");
                }

            }

        }

    }

      代码下载:WpfApplication7_2.zip

      这种设计思想是我在实际编程中自己琢磨出来的,事后在Prism的codeplex论坛上发现之前就已经有人这么实现了。真是英雄所见略同啊!因为这种设计的适用性是很广的,所以我将其总结为——

      包氏波动思想定义:对于列表性控件(DataGrid、ListBox、ComboBox等等),以及像CheckBox、RadioButton这样的带有选项集合的控件,它们都具有类似SelectedItem这样的属性,我们可以在MVP模式中绑定这些属性,以监视选择项的变化。

      接下来的章节我将分别展示这些控件的另类绑定方式,以证实包氏波动思想的伟大价值。


      (二)ListBox

      将上面例子中的DataGrid换为ListBox就可以了,其它地方无需任何改变。 

    <ListBox Margin="12,12,66,144" Name="listBox1" ItemsSource="{Binding StudentList}" SelectedItem="{Binding SelectedStudent}">
        
<ListBox.ItemTemplate>
            
<DataTemplate>
                
<StackPanel Orientation="Horizontal">
                    
<TextBlock Text="{Binding Path=UserName}" Width="40" />
                    
<TextBlock Text="{Binding Path=Score}"/>
                
</StackPanel>
            
</DataTemplate>
        
</ListBox.ItemTemplate>
    
</ListBox>

      代码下载: WpfApplication7_3.zip

      (三)CheckBox

      CheckBox中有一个属性IsChecked,我们只要监视着该属性的波动,就可以了。

      代码下载: WpfApplication7_4.zip


      (四)RadioButton

      这个实现起来比较费事,细节请参见Clingingboy的文章:《WPF and SL RadioButtonList Tip》

      这个土人居然没给出个Demo,我只好自己动手丰衣足食,代码下载: WpfTestRadioButton.zip

      但是我们发现,这个例子太简单了,只是2个性别“男”和“女”的选择,如果用CheckBox也是可以模拟出来的。

      于是,在此基础上,我试着添加了第3性“女博士”,来扩展RadionButton的应用。

      代码下载: WpfTestRadioButton_extension.zip


      (五)ComboBox

      这个控件的波动思想,在WPF中实现起来比较简单。

      代码下载: WpfTestComboBox.zip

      注:这个控件玩起来有时候比较烦,就是当下拉列表中有一行默认空白选项时,我们会在《最复杂的MVVM模式》一文中遇到这一场景。


      (六)总结

      最常用的就是这几种了,也是最典型的几种。其它列表控件的绑定方式均可依样画葫芦,我这里就不多讲了。

      对于列表控件,究竟是使用AttachedBehavior,还是使用“包氏波动思想”(继续陶醉中),我的取舍是后者,只有在不能用波动思想的时候,才会考虑AttachedBehavior。波动思想俨然是基于数据的,这和MVP模式将数据从UI中剥离不谋而合;而AttachedBehavior则是传统Event编程模型的变形,我一直认为它是Event和Command妥协的产物,尤其在描述数据上,是远远不如包氏波动思想的。

      哈哈,老包卖瓜,自卖自夸,不管这种思想是不是姓包,我们都要养成“基于数据编程”的习惯,而不再是“基于控件编程”。


      这是一种境界的提升。

posted @ 2009-10-14 00:37  包建强  Views(4775)  Comments(20Edit  收藏  举报