0.0Binding的基本概念

绑定五元素:

  • 绑定目标
  • 绑定属性
  • 绑定模式
  • 绑定数据源
  • 关联资源

0.1关于MVVM模式下的DataContext

使用MVVM Light框架会自动生成ViewModelLocator类,此类用于注册DataContext。
ViewModel类需要集成ViewModelBase Mode类需要继承ObservableObject
在ViewModelLocator中需要写如下代码:

  1. public class ViewModelLocator
  2. {
  3. /// <summary>
  4. /// Initializes a new instance of the ViewModelLocator class.
  5. /// </summary>
  6. public ViewModelLocator()
  7. {
  8. ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
  9. SimpleIoc.Default.Register<MainViewModel>();
  10. SimpleIoc.Default.Register<SongViewModel>();
  11. SimpleIoc.Default.Register<UserInfoViewModel>();
  12. SimpleIoc.Default.Register<ComplexInfoViewModel>();
  13. }
  14. #region 实例化
  15. public MainViewModel Main
  16. {
  17. get
  18. {
  19. return ServiceLocator.Current.GetInstance<MainViewModel>();
  20. }
  21. }
  22. public SongViewModel songViewModel {
  23. get {
  24. return ServiceLocator.Current.GetInstance<SongViewModel>();
  25. }
  26. }
  27. public UserInfoViewModel userInfoViewModel {
  28. get {
  29. return ServiceLocator.Current.GetInstance<UserInfoViewModel>();
  30. }
  31. }
  32. public ComplexInfoViewModel complexInfoViewModel {
  33. get {
  34. return ServiceLocator.Current.GetInstance<ComplexInfoViewModel>();
  35. }
  36. }
  37. #endregion
  38. public static void Cleanup()
  39. {
  40. // TODO Clear the ViewModels
  41. }
  42. }

需要在App.xaml中声明名称空间:

 <Application.Resources>
    <ResourceDictionary>
      <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:yyy.ViewModel" />
    </ResourceDictionary>
  </Application.Resources>

在对应的View中两种方式:

//方式一:
DataContext="{Binding Source={StaticResource Locator},Path=songViewModel}"
//方式二:在xaml后台代码中写,直接new一个实例
base.DataContext = new SongViewModel();

1.使用集合对象作为列表控件的ItemsSource

后台数据

public class ComplexInfoViewModel:ViewModelBase{
    public ComplexInfoViewModel(){
        InitListBox();
    }

    //生成DataTable数据
    private void InitListBox(){
       EmpList = new List<Employee>() {
                new Employee(){ID = 1,Age =23,Name="Lucy"},
                new Employee(){ID = 2,Age =25,Name="Jack"},
                new Employee(){ID = 3,Age =26,Name="John"},
            };
    }

    private List<Employee> empList;
    //雇员列表
        public List<Employee> EmpList {
            get { return empList; }
            set {
                empList = value;
                RaisePropertyChanged();
            }
        }
}

前台代码

<TextBlock Text="StudentID:" Margin="5"/>
<TextBox x:Name="textBoxID" Text="{Binding ElementName=listBoxEmployee,Path=SelectedItem.Name}"/>
<TextBlock Text="Student List" Margin="5"/>
<ListBox x:Name="listBoxEmployee" Margin="5" Height="80" ItemsSource="{Binding EmpList}">
         <ListBox.ItemTemplate>
             <DataTemplate>
                <StackPanel>
                    <StackPanel Orientation="Horizontal">
                         <TextBlock Text="{Binding ID}" Margin="2"/>
                         <TextBlock Text="{Binding Age}" Margin="2"/>
                         <TextBlock Text="{Binding Name}" Margin="2"/>
                     </StackPanel>
                 </StackPanel>
             </DataTemplate>
          </ListBox.ItemTemplate>
</ListBox>

实现效果

image.png

2.使用ADO.NET对象作为Binding的源

后台数据

DataTable绑定到ListView中。
使用MVVM Light框架,ViewModel层继承自ViewModelBase。

public class ComplexInfoViewModel:ViewModelBase{
    public ComplexInfoViewModel(){
        InitListView();
    }

    //生成DataTable数据
    private void InitListView(){
        DtStudentInfo = new DataTable();
        DtStudentInfo.Columns.Add("ID",Type.GetType("System.Int32"));
        DtStudentInfo.Columns.Add("Age",Type.GetType("System.Int32"));
        DtStudentInfo.Columns.Add("Name", Type.GetType("System.String"));
      //DtStudentInfo.Columns.Add("IsChoose", Type.GetType("System.Boolen"));
        DtStudentInfo.Rows.Add(new object[] { 1, 23, "Lucy"});
        DtStudentInfo.Rows.Add(new object[] { 2, 24, "Jack" });
        DtStudentInfo.Rows.Add(new object[] { 3, 25, "John"});
        DtStudentInfo.Rows.Add(new object[] { 4, 26, "Albert"});
    }

    private DataTable dtStudentInfo;
        /// <summary>
        /// 学生信息,具有属性变更后通知属性
        /// </summary>
        public DataTable DtStudentInfo {
            get { return dtStudentInfo; }
            set {
                dtStudentInfo = value;
                RaisePropertyChanged();
            }
        }
}

前台代码

DataTable的数据显示一般使用ListView来显示。DataTable的DefaultView用于Binding的数据源。DefaultView本身作为数据源。

<ListView ItemSource="{Binding DtStudentInfo.DefaultView}">
  <ListView.View>
    <GridView>
      <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding ID}"/>
      <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding Name}"/>
      <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding Age}"/>
    </GridView>
  </ListView.View>
</ListView>

此处可用DataContent直接绑定dt,ItemSource三无绑定也可以实现,自动树形查找。

 <ListView x:Name="listViewStudents2" 
           DataContext="{Binding DtStudentInfo}" 
           ItemsSource="{Binding}">
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding ID}"/>
                        <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding Name}"/>
                        <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding Age}"/>
                    </GridView>
                </ListView.View>
            </ListView>

实现效果

image.png

3.(ListView)使用XML数据作为Binding的源

当使用XML数据作为Binding的Source时,我们将使用XPath属性而不是Path属性来制定数据的来源。

数据源

StudentList 根节点
Student是XML的元素 ID是元素的Attribute,XPath=@ID 属性需要加@
XPath = Name 子级元素无需加@

<?xml version="1.0" encoding="utf-8" ?>
<StudentList>
    <Student ID="1">
        <Name>Tim</Name>
        <Age>24</Age>
    </Student>
    <Student ID="2">
        <Name>Lucy</Name>
        <Age>25</Age>
    </Student>
    <Student ID="3">
        <Name>Albert</Name>
        <Age>26</Age>
    </Student>
</StudentList>

后台代码

public class ComplexInfoViewModel:ViewModelBase{
    public ComplexInfoViewModel(){
       InitXmlDoc();
    }

    //读取DataTable数据
    private void InitXmlDoc() {
            XmlDocument DocStudent = new XmlDocument();
            DocStudent.Load(@"../Debug/Resources/RawData.xml");

            Xdp = new XmlDataProvider();
            Xdp.Document = DocStudent;
            Xdp.XPath = @"/StudentList/Student";//Xdp的路径为根元素StudentList下面的子节点Student
        }

     private XmlDataProvider xdp;

        public XmlDataProvider Xdp {
            get { return xdp; }
            set {
                xdp = value;
                RaisePropertyChanged();
            }
        }
}

前台代码

<ListView ItemsSource="{Binding Xpd.Data}">
     <ListView.View>
         <GridView>
            <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding XPath=@ID}"/>
            <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding XPath=Name}"/>
         </GridView>
      </ListView.View>
</ListView>

运行效果

image.png

动态绑定数据到ListView中

image.png
ViewModel代码:

 private IEnumerable listBoxData;

        public IEnumerable ListBoxData {
            get { return listBoxData; }
            set {
                listBoxData = value;
                RaisePropertyChanged();
            }
        }

    private void InitImage() {
            ListBoxData = new ObservableCollection<dynamic>() {
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\1.gif",Info="鬼刀1"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\2.jpg",Info="鬼刀2"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\3.png",Info="鬼刀3"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\4.png",Info="鬼刀4"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\5.jpg",Info="鬼刀5"},
            };
        }

View代码:
ListBox中的Panel使用了ItemsPanelTemplate模板,这个模板里面放了一个WrapPanel,向上层寻找数据源,寻找数据源的对象为ListBox
ListBox的数据模板里面有一个StackPanel
StackPanel里面有两个控件,一个是MediaElement播放gif
一个是TextBlock
如何实现MediaElement循环播放:MediaEnded事件

 <ListBox ItemsSource="{Binding ListBoxData}">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel  Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>

                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <MediaElement  Name="gifMedia" Source="{Binding Img}" Height="200" MediaEnded="MediaElement_MediaEnded"/>
                            <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

     //后台代码
     private void MediaElement_MediaEnded(object sender, RoutedEventArgs e) {
            ((MediaElement)sender).Position = ((MediaElement)sender).Position - TimeSpan.FromSeconds(10);
        }

4.(TreeView)使用XmlDataProvider数据作为Binding的源

数据源

FileSystem为根节点 XPath选择根节点下面的Folder子节点

<?xml version="1.0" encoding="utf-8" ?>
<FileSystem xmlns="">
  <Folder Name="Books">
    <Folder Name="Windows">
      <Folder Name="WPF"/>
      <Folder Name="MFC"/>
      <Folder Name="Delphi"/>
    </Folder>

    <Folder Name="Tools">
      <Folder Name="Development"/>
      <Folder Name="Designment"/>
      <Folder Name="Players"/>
    </Folder>
  </Folder>
</FileSystem>

后台代码

public class ComplexInfoViewModel:ViewModelBase{
    public ComplexInfoViewModel(){
       InitXmlDoc();
    }

    //读取DataTable数据
    private void InitXmlDoc() {
            XmlDocument xdcBooks = new XmlDocument();
            xdcBooks.Load(@"../Debug/Resources/Books.xml");

            XdpBooks = new XmlDataProvider();
            XdpBooks.Document = xdcBooks;
            XdpBooks.XPath = @"/FileSystem/Folder";
        }

    private XmlDataProvider xdpBooks;

        public XmlDataProvider XdpBooks {
            get { return xdpBooks; }
            set {
                xdpBooks = value;
                RaisePropertyChanged();
            }
        }
}

前台代码

 <TreeView DataContext="{Binding XdpBooks}" ItemsSource="{Binding}">
      <TreeView.ItemTemplate>
         <HierarchicalDataTemplate ItemsSource="{Binding XPath=Folder}">
              <TextBlock Text="{Binding XPath=@Name}"/>
         </HierarchicalDataTemplate>
      </TreeView.ItemTemplate>
</TreeView>

效果图

image.png

直接使用后台代码生成TreeView的List数据
Model代码 数据源:此处需要注意的是类的属性中有List<类>子项,用于迭代。

public class TypeTreeModel : ObservableObject {
        public List<TypeTreeModel> Children { get; set; } = new List<TypeTreeModel>();
        public float NodeID { get; set; }
        public string NodeName { get; set; }
    }

ViewModel代码:注意此处是List而不是TreeModel!!!!!!!!!

  public class EmployeeViewModel:ViewModelBase {
        public EmployeeViewModel() {
            InitTree();
        }
            private void InitTree() {
            Tree = new List<TypeTreeModel>() { 
                new TypeTreeModel() {
                    NodeID = 1f,NodeName="苹果",Children = new List<TypeTreeModel>() {
                        new TypeTreeModel() {
                            NodeID=1.1f,NodeName="苹果A"
                        },
                        new TypeTreeModel() {
                            NodeID=1.2f,NodeName="苹果B"
                        },
                    }
                }
            };
        }

        private List<TypeTreeModel> tree;

        public List<TypeTreeModel> Tree {
            get { return tree; }
            set {
                tree = value;
                RaisePropertyChanged();
            }
        }
}

View代码

 <StackPanel Grid.Row="1">
            <TreeView ItemsSource="{Binding Tree}" Height="100">
                <TreeView.ItemTemplate>
                    <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                        <TextBlock Text="{Binding NodeName}"/>
                    </HierarchicalDataTemplate>
                </TreeView.ItemTemplate>
            </TreeView>
</StackPanel>

5.使用LINQ检索结果作为Binding的源

自3.0版本开始,.NET Framentwork开始支持LINQ(Language-Integrated Query,语言集成查询),使用LINQ查询,查询的结果是IEnumerable,可作为列表控件的ItemsSource来使用。

查询回IEnumerable绑定给ItemsSource:

public class ComplexInfoViewModel:ViewModelBase{
    private IEnumerable<Student> iem;

        public IEnumerable<Student> Iem {
            get { return iem; }
            set {
                iem = value;
                this.RaisePropertyChanged();
            }
        }

    public StudentViewModel() {
            InitList();
        }

     private void InitList() {
            List<Student> listTemp = new List<Student>() {
                new Student(){ID=0,Name="Tim",Age=29},
                new Student(){ID=1,Name="Ton",Age=29},
                new Student(){ID=2,Name="Kyle",Age=29},
                new Student(){ID=3,Name="Tom",Age=29},
                new Student(){ID=4,Name="Lucy",Age=29},
                new Student(){ID=5,Name="Mike",Age=29},
            };
         //此处使用LINQ从listTemp查询以“T”开头的对象
            Iem = from stu in listTemp where stu.Name.StartsWith("T") select stu;

        }
}
 <ListView x:Name="listViewClass" ItemsSource="{Binding Iem}">
    <ListView.View>
      <GridView>
           <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding Path=ID}"/>
           <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding Path=Name}"/>
           <GridViewColumn Header="Age" Width="60" DisplayMemberBinding="{Binding Path=Age}"/>
       </GridView>
     </ListView.View>
</ListView>

从XDocument对象使用LINQ:

<?xml version="1.0" encoding="utf-8" ?>
<StudentList>
  <Class>
    <Student ID="1" Name="Jack" Age="24"/>
    <Student ID="2" Name="Lucy" Age="22"/>
    <Student ID="3" Name="Tom" Age="24"/>
    <Student ID="4" Name="Tock" Age="34"/>
    <Student ID="5" Name="df" Age="26"/>
    <Student ID="6" Name="ffg" Age="27"/> 
  </Class>
</StudentList>
public class ComplexInfoViewModel:ViewModelBase{
    private IEnumerable<Student> iemStudent;

        public IEnumerable<Student> IemStudent {
            get { return iemStudent; }
            set {
                iemStudent = value;
                this.RaisePropertyChanged();
            }
        }

    public StudentViewModel() {
            InitList();
        }

     private void InitList() {
           XDocument xdc = XDocument(@"../Debug/Bin/Resources/RawData.xml);
           ItemStudent = from element in xdc.Descendants("Student")
                         where element.Attribute("Name").StartWith("T")
                         select new Student(){
                             ID = int.Parse(element.Attribute("ID").Value),
                             Name = element.Attribute("Name").Value,
                             Age = element.Attribute("Age").Value            
        }
}
<ListView ItemSource="{Binding ItemStudent}">
  <ListView.View>
    <GridView>
      <GridViewColumn Header="ID" Width="60" DisplayMemberBinding="{Binding Path="ID"}"/>
      <GridViewColumn Header="Name" Width="60" DisplayMemberBinding="{Binding Path="Name"}"/>
      <GridViewColumn Header="Age" Width="60" DisplayMemberBinding="{Binding Path="Age"}"/>
    </GridView>
   </ListView.View>
</ListView>

6. 使用ObjectDataProvider对象作为Binding的Source

ObjectDataProvider用于动态调用类的方法Binding,有时候我们需要的数据可能是方法的返回值,在不动底层类库或在黑盒引用类库的基础上使用ObjectDataProvider来包装作为Binding源的数据对象了。对象数据提供者,把对象作为数据源提供给Binding,和XmlDataProvider类似,把Xml数据作为数据源提供给Binding。父类为DataSourceProvider。

黑盒类

namespace MVVMExample.Model{
    public class Calculator {
        public string Add(string arg1,string arg2) {
            double x = 0;
            double y = 0;
            double z = 0;
            if(double.TryParse(arg1,out x)&&double.TryParse(arg2,out y)) {
                z = x + y;
                return z.ToString();
            }
            return "Input Error";
        }
    }
}

XAML代码:此处需要引用Calculator类的名称空间

<Window xmlns:localMode = "clr-namespace:MVVMExample.Model"
<Window.Resources>
<ObjectDataProvider x:Key="Odp" ObjectType="{x:Type localModel:Calculator}"
                    MethodName = "Add">
  <ObjectDataProvider.MethodParameters>
    <sys:String>0</sys:String>
    <sys:String>0</sys:String>
  </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<StackPanel>
  <TextBox Text="{Binding Source={StaticResource Odp},
                 Path=MethodParameters[0]
                 BindsDirectlyToSource=True,
                 UpdateSourceTrigger = PropertyChanged}"/>
    <TextBox Text="{Binding Source={StaticResource Odp},
                 Path=MethodParameters[1]
                 BindsDirectlyToSource=True,
                 UpdateSourceTrigger = PropertyChanged}"/>
    <TextBox Text="{Binding Source={StaticResource Odp},
                 Mode = OneWay"/>

7.使用Binding的RelativeSource

相对数据源。RelativeSource类有三个静态属性:PreviousData、Self、TemplatedParent。

<StackPanel Name="Albert">
  <StackPanel Name="WPF">
    <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel
                     AncestorLevel=2},Path=Name}"

image.png

8.自定义用户控件的使用

项目模板 、项目容器两者结合起来用。ItemTemplate定义了DataTemplate(数据模板里面一定是具体的控件) ItemPanel定义了ItemsPanelTemplate(是一个容器wrappanel啊)

自定义控件代码:

Grid中有一个StackPanel装了Image和TextBlock,分辨有对应绑定的数据源。Style样式里面有样式触发的方式,当鼠标IsMouseOver的时候属性RenderTransform发生偏转10度同时背景颜色变为蓝色

 <Grid>
        <Grid.Resources>
            <Style TargetType="{x:Type StackPanel}">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="RenderTransform">
                            <Setter.Value>
                                <RotateTransform Angle="10"></RotateTransform>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Background" Value="#3B9CFB"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>

        <StackPanel Orientation="Vertical" Margin="10">
            <Image Source="{Binding Img}" Width="50" Height="50"/>
            <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/>
        </StackPanel>
    </Grid>

引用自定义控件

项目控件 数据源绑定ListBoxData:

private IEnumerable listBoxData;
        public IEnumerable ListBoxData {
            get { return listBoxData; }
            set {
                listBoxData = value;
                RaisePropertyChanged();
            }
        }
 private void InitImage() {
            ListBoxData = new ObservableCollection<dynamic>() {
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\1.gif",Info="鬼刀1"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\2.jpg",Info="鬼刀2"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\3.png",Info="鬼刀3"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\4.png",Info="鬼刀4"},
                new {Img="E:\\TestCode\\MVVMExampleTwo\\Resources\\5.jpg",Info="鬼刀5"},
            };
        }

C#

自动换行

x

1

xmlns:UCControl=”clr-namespace:MVVMExampleTwo.UserControl”

2

3

4 5 6 7 8 9 10 11 12 13 14 15 16 17

各类控件的Binding操作 - 图7