0.0Binding的基本概念
绑定五元素:
- 绑定目标
- 绑定属性
- 绑定模式
- 绑定数据源
- 关联资源
0.1关于MVVM模式下的DataContext
使用MVVM Light框架会自动生成ViewModelLocator类,此类用于注册DataContext。
ViewModel类需要集成ViewModelBase Mode类需要继承ObservableObject
在ViewModelLocator中需要写如下代码:
public class ViewModelLocator
{
/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<SongViewModel>();
SimpleIoc.Default.Register<UserInfoViewModel>();
SimpleIoc.Default.Register<ComplexInfoViewModel>();
}
#region 实例化
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public SongViewModel songViewModel {
get {
return ServiceLocator.Current.GetInstance<SongViewModel>();
}
}
public UserInfoViewModel userInfoViewModel {
get {
return ServiceLocator.Current.GetInstance<UserInfoViewModel>();
}
}
public ComplexInfoViewModel complexInfoViewModel {
get {
return ServiceLocator.Current.GetInstance<ComplexInfoViewModel>();
}
}
#endregion
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
需要在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>
实现效果
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>
实现效果
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>
运行效果
动态绑定数据到ListView中
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>
效果图
直接使用后台代码生成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
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
查询回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}"
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