集合数据源:ItemsSource
集合控件模班有:
ItemsControl
ListView
ListBox
DataGrid
ComboBox
TabControl
Menu
TreeView
在XAML中使用:
以下以ListBox为例:
xaml代码:
<TextBlock Text="{Binding Name}" />
<ListBox x:Name="lb" ItemsSource="{Binding List}" Grid.ColumnSpan="2" >
<!--子项模板-->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" TextBlock.Foreground="Gray" Name="sp">
<TextBlock Text="{Binding Id}" Margin="10,0"/>
<TextBlock Text="{Binding Header}" Margin="10,0"/>
<!--ListBox的数据源是Window.DataContext
ListItem中的数据源是ListBox中ItemsSource确定-->
<TextBlock Text="{Binding DataContext.Name,RelativeSource={RelativeSource AncestorType=ListBox}}" Margin="10,0"/>
<TextBlock Text="{Binding DataContext.Name,ElementName=lb}" Margin="10,0"/>
<TextBlock Text="{Binding State}" Margin="10,0"/>
<Button Content="处理" Name="btn" Visibility="Collapsed" Click= "btn_Click"/>
</StackPanel>
<DataTemplate.Triggers>
<!--Trigger 判断对象的基本属性-->
<!--DataTrigger 判断的是数据源中的数据属性-->
<!--数据触发 如果state是1 则变红色-->
<!--数据模板选择器-->
<DataTrigger Binding="{Binding State}" Value="1">
<Setter TargetName="sp" Property="TextBlock.Foreground" Value="Red"/>
<Setter TargetName="btn" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#代码:
using System.Windows;
namespace XH.BindingLesson.DataCollection
{
/// <summary>
/// DataCollectionWindow.xaml 的交互逻辑
/// </summary>
public partial class DataCollectionWindow : Window
{
public DataCollectionWindow()
{
InitializeComponent();
this.DataContext = new DataSource();
}
}
public class DataSource
{
public string Name { get; set; } = "Hello";
// ObservableCollection 通知属性
public ObservableCollection<DataItem> List { get; set; } =
new ObservableCollection<DataItem>() {
new DataItem { Id =1,Header ="AAA",State = 0 },
new DataItem { Id =2,Header ="BBB",State = 1 },
new DataItem { Id =3,Header ="CCC",State = 0 }
};
}
public class DataItem
{
public int Id { get; set; }
public string Header { get; set; }
public int State { get; set; }
}
}
结果演示:
注意:
在集合中:ObservableCollection为通知属性,可以变化之后通知到UI界面上。
DataTrigger 判断的是数据源中的数据属性
子项模板显示
模板混合显示
上述案例中 显示的Name就是混合显示
模板触发器
<DataTemplate.Triggers>
<!--Trigger 判断对象的基本属性-->
<!--DataTrigger 判断的是数据源中的数据属性-->
<!--数据触发 如果state是1 则变红色-->
<!--数据模板选择器-->
<DataTrigger Binding="{Binding State}" Value="1">
<Setter TargetName="sp" Property="TextBlock.Foreground" Value="Red"/>
<Setter TargetName="btn" Property="Visibility" Value="Visible"/>
</DataTrigger>
</DataTemplate.Triggers>
模板选择器
需求:上述案例中,使用的数据事件,但是按钮都会绘制,只是不显示,这是占有内存,可以优化为根据state进行不一样的显示,选择不一样的模板:
关键字:ItemTemplateSelector
XAML代码:
<Window.Resources>
<DataTemplate x:Key="dt1" DataType="ListBox">
<StackPanel Orientation="Horizontal" TextBlock.Foreground="Gray" Name="sp">
<TextBlock Text="{Binding Id}" Margin="10,0"/>
<TextBlock Text="{Binding Header}" Margin="10,0"/>
<!--ListBox的数据源是Window.DataContext
ListItem中的数据源是ListBox中ItemsSource确定-->
<TextBlock Text="{Binding DataContext.Name,RelativeSource={RelativeSource AncestorType=ListBox}}" Margin="10,0"/>
<TextBlock Text="{Binding DataContext.Name,ElementName=lb}" Margin="10,0"/>
<TextBlock Text="{Binding State}" Margin="10,0"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="dt2" DataType="ListBox">
<StackPanel Orientation="Horizontal" TextBlock.Foreground="Red" Name="sp">
<TextBlock Text="{Binding Id}" Margin="10,0"/>
<TextBlock Text="{Binding Header}" Margin="10,0"/>
<!--ListBox的数据源是Window.DataContext
ListItem中的数据源是ListBox中ItemsSource确定-->
<TextBlock Text="{Binding DataContext.Name,RelativeSource={RelativeSource AncestorType=ListBox}}" Margin="10,0"/>
<TextBlock Text="{Binding DataContext.Name,ElementName=lb}" Margin="10,0"/>
<TextBlock Text="{Binding State}" Margin="10,0"/>
<Button Content="处理" Name="btn" Click= "btn_Click"/>
</StackPanel>
</DataTemplate>
<local:MyDataTempkateSelector x:Key="mdts"
NormalTemplate="{StaticResource dt1}"
WarningTemplate="{StaticResource dt2}" />
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding List}"
ItemTemplateSelector="{StaticResource mdts}" />
</Grid>
在C#中设置混合模板类:
using System.Windows;
using System.Windows.Controls;
namespace XH.BindingLesson.DataCollection
{
public class MyDataTempkateSelector:DataTemplateSelector
{
public DataTemplate NormalTemplate { get; set; }
public DataTemplate WarningTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// 根据不一样的state 返回模板 然后在xaml中根据属性绑定不一样的模板
var di = item as DataItem;
if(di.State == 1)
return WarningTemplate;
return NormalTemplate;
}
}
}
显示结果和之前一样,但是渲染的时候,界面有button就渲染,没有不渲染,相比之下,优化内存。
不同集合控件的处理
1. ItemsControl、ListBox
xaml代码:
<Window x:Class="XH.BindingLesson.Example.ColumnChartWindow"
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:local="clr-namespace:XH.BindingLesson.Example"
mc:Ignorable="d"
Title="ColumnChartWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" ItemsSource="{Binding datas}" Visibility="Visible">
<!--修改 数据显示的排列模板ItemsPanel-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Value}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="Red"/>
<Border Grid.Row="1" Background="Gray" Height="1" VerticalAlignment="Bottom"/>
<Border Grid.Row="1" Height="{Binding Value}" Width="12" Background="Green" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding LabelText}" Grid.Row="2" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!--ListBox 比 ItemsControl 多一层属性:ListBoxItem ,每一个ListBoxItem都做了水平垂直居中处理-->
<ListBox Grid.Row="1"
ItemsSource="{Binding datas}"
Visibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<!--<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="VerticalContentAlignment" Value="Bottom"/>
</Style>
</ListBox.Resources>-->
<!--两个写法一样 ,没有区别-->
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!--<Setter Property="VerticalContentAlignment" Value="Bottom"/>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="{TemplateBinding VerticalAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
</ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Gray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<!--修改 数据显示的排列模板ItemsPanel-->
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Value}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="Red"/>
<Border Grid.Row="1" Background="Gray" Height="1" VerticalAlignment="Bottom"/>
<Border Grid.Row="1" Height="{Binding Value}" Width="12" Background="Green" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding LabelText}" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!--<TextBlock Text="Hello" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Green" />-->
</Grid>
</Window>
C#代码:
using System.Collections.ObjectModel;
using System.Windows;
namespace XH.BindingLesson.Example
{
/// <summary>
/// ColumnChartWindow.xaml 的交互逻辑
/// </summary>
public partial class ColumnChartWindow : Window
{
public ColumnChartWindow()
{
InitializeComponent();
this.DataContext = new ChartDataSource();
}
}
public class ChartDataSource
{
public ObservableCollection<ColumnItem> datas { get; set; } = new ObservableCollection<ColumnItem>();
public ChartDataSource()
{
for (int i = 0; i < 15; i++)
{
datas.Add(new ColumnItem()
{
Value = new Random().Next(10, 200),
LabelText = DateTime.Now.ToString("mm:ss"),
});
}
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
// 报错 :跨UI线程 ObservableCollection这个类型的数据 会导致界面的对象渲染变更(控件的增减)
// 界面上的操作都需要在UI线程(主线程)处理
// Task属于子线程(后台线程)
try
{
Application.Current.Dispatcher.Invoke(() =>
{
datas.Add(new ColumnItem()
{
Value = new Random().Next(10, 200),
LabelText = DateTime.Now.ToString("mm:ss"),
});
if (datas.Count > 20)
datas.RemoveAt(0);
});
}
catch (Exception ex) { }
}
});
}
}
public class ColumnItem
{
public string LabelText { get; set; }
public int Value { get; set; }
}
}
区别:ListBox 比 ItemsControl 多一层属性:ListBoxItem ,每一个ListBoxItem都做了水平垂直居中处理
显示:两个可以显示一样的效果
2. ListView、DataGrid
ListView:属性 DisplayMemberBinding 绑定
<ListView ItemsSource="{Binding List}">
<ListView.View>
<GridView>
<GridViewColumn Header="Id" DisplayMemberBinding="{Binding Id}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Header}"/>
<GridViewColumn Header="State" DisplayMemberBinding="{Binding State}"/>
</GridView>
</ListView.View>
</ListView>
DataGrid:
<DataGrid ItemsSource="{Binding List}" AutoGenerateColumns="False" CanUserAddRows="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="编号" Binding="{Binding Id}"/>
<DataGridTextColumn Header="名字" Binding="{Binding Header}"/>
<DataGridTextColumn Header="状态" Binding="{Binding State}">
<DataGridTextColumn.CellStyle>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="0">
<Setter Property="Border.Background" Value="red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
效果:
3. TreeView
<TreeView ItemsSource="{Binding List}">
<TreeView.ItemTemplate>
<!--绑定每一个子项的数据 自动合并每个数据-->
<HierarchicalDataTemplate ItemsSource="{Binding Sub}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Header}" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
HierarchicalDataTemplate :
只需要设置一个父类和子类样式,所有都适配,可以无限层级嵌套绑定每一个子项的数据 自动合并每个数据
4. 其他集合控件
ComboBox:
属性:
DisplayMemberPath 显示界面上看到的字段属性
SelectedValuePath 需要返回的属性 界面上看不到
SelectedValue 返回的Value值,前提需要设置SelectedValuePath
SelectedItem 返回的Item值 object
IsDropDownOpen 是否打开状态
注意:一般显示名字返回Id
<ComboBox Width="200" Height="45"
VerticalAlignment="Top"
SelectedIndex="0"
ItemsSource="{Binding List}"
IsDropDownOpen="False"
DisplayMemberPath="Header"
SelectedValuePath="Id"
SelectedValue="{Binding CurrentId}"
SelectedItem="{Binding CurrentItem}">
</ComboBox>
案例:
直方图 柱状图
xaml代码:
<Window x:Class="XH.BindingLesson.Example.ColumnChartWindow"
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:local="clr-namespace:XH.BindingLesson.Example"
mc:Ignorable="d"
Title="ColumnChartWindow" Height="450" Width="800">
<Grid>
<ItemsControl ItemsSource="{Binding datas}">
<!--修改 数据显示的排列模板ItemsPanel-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Value}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Foreground="Red"/>
<Border Grid.Row="1" Background="Gray" Height="1" VerticalAlignment="Bottom"/>
<Border Grid.Row="1" Height="{Binding Value}" Width="12" Background="Green" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding LabelText}" Grid.Row="2" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
C#代码:
using System.Collections.ObjectModel;
using System.Windows;
namespace XH.BindingLesson.Example
{
/// <summary>
/// ColumnChartWindow.xaml 的交互逻辑
/// </summary>
public partial class ColumnChartWindow : Window
{
public ColumnChartWindow()
{
InitializeComponent();
this.DataContext = new ChartDataSource();
}
}
public class ChartDataSource
{
public ObservableCollection<ColumnItem> datas { get; set; } = new ObservableCollection<ColumnItem>();
public ChartDataSource()
{
for (int i = 0; i < 15; i++)
{
datas.Add(new ColumnItem()
{
Value = new Random().Next(10, 200),
LabelText = DateTime.Now.ToString("mm:ss"),
});
}
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000);
// 报错 :跨UI线程 ObservableCollection这个类型的数据 会导致界面的对象渲染变更(控件的增减)
// 界面上的操作都需要在UI线程(主线程)处理
// Task属于子线程(后台线程)
try
{
Application.Current.Dispatcher.Invoke(() =>
{
datas.Add(new ColumnItem()
{
Value = new Random().Next(10, 200),
LabelText = DateTime.Now.ToString("mm:ss"),
});
if (datas.Count > 20)
datas.RemoveAt(0);
});
}
catch (Exception ex) { }
}
});
}
}
public class ColumnItem
{
public string LabelText { get; set; }
public int Value { get; set; }
}
}
显示效果:
注意:
界面上的操作都需要在UI线程(主线程)处理
Task属于子线程(后台线程)
需要使用Dispatcher 进行跨UI线程使用
修改数据显示的排列模板:使用ItemsPanel --> ItemsPanelTemplate
集合数据多级联动
C#代码:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace XH.BindingLesson.DataCollection
{
public class MultiLevelDataSource : INotifyPropertyChanged
{
// 每个列表中的数据 Id/Header/ParentId
public event PropertyChangedEventHandler? PropertyChanged;
public int CurrentIndex { get; set; } = 0;
private int currentProvince;
public int CurrentProvince
{
get { return currentProvince; }
set
{
currentProvince = value;
CityList.Clear();
var cs = AreaAll.Where(a => a.ParentCode == value).ToList();
foreach (var item in cs)
{
CityList.Add(item);
}
DistrictList.Clear();
//var dt = AreaAll.Where(a => a.ParentCode == CityList[0].Code).ToList();
//foreach (var item in dt)
//{
// DistrictList.Add(item);
//}
CurrentIndex = 0;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentIndex"));
}
}
private int currentCity;
public int CurrentCity
{
get { return currentCity; }
set
{
currentCity = value;
DistrictList.Clear();
var cs = AreaAll.Where(a => a.ParentCode == value).ToList();
foreach (var item in cs)
{
DistrictList.Add(item);
}
CurrentIndex = 0;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentIndex"));
}
}
public List<Area> AreaAll { get; set; } = new List<Area>();
public ObservableCollection<Area> ProvinceList { get; set; } = new ObservableCollection<Area>();
public ObservableCollection<Area> CityList { get; set; } = new ObservableCollection<Area>();
public ObservableCollection<Area> DistrictList { get; set; } = new ObservableCollection<Area>();
public MultiLevelDataSource()
{
//
AreaAll.Add(new Area { Code = 1, Header = "湖北", ParentCode = 0 });
AreaAll.Add(new Area { Code = 2, Header = "江苏", ParentCode = 0 });
AreaAll.Add(new Area { Code = 3, Header = "武汉", ParentCode = 1 });
AreaAll.Add(new Area { Code = 4, Header = "襄阳", ParentCode = 1 });
AreaAll.Add(new Area { Code = 5, Header = "南京", ParentCode = 2 });
AreaAll.Add(new Area { Code = 6, Header = "苏州", ParentCode = 2 });
AreaAll.Add(new Area { Code = 7, Header = "武昌", ParentCode = 3 });
AreaAll.Add(new Area { Code = 8, Header = "洪山", ParentCode = 3 });
AreaAll.Add(new Area { Code = 9, Header = "江岸", ParentCode = 3 });
AreaAll.Add(new Area { Code = 10, Header = "襄城", ParentCode = 4 });
AreaAll.Add(new Area { Code = 11, Header = "樊成", ParentCode = 4 });
AreaAll.Add(new Area { Code = 12, Header = "襄州", ParentCode = 4 });
AreaAll.Add(new Area { Code = 13, Header = "玄武", ParentCode = 5 });
AreaAll.Add(new Area { Code = 14, Header = "建邺", ParentCode = 5 });
AreaAll.Add(new Area { Code = 15, Header = "鼓楼", ParentCode = 5 });
AreaAll.Add(new Area { Code = 16, Header = "姑苏", ParentCode = 6 });
AreaAll.Add(new Area { Code = 17, Header = "相城", ParentCode = 6 });
AreaAll.Add(new Area { Code = 18, Header = "吴中", ParentCode = 6 });
// 数据初始化
var ps = AreaAll.Where(a => a.ParentCode == 0).ToList();
foreach (var item in ps)
{
ProvinceList.Add(item);
}
var cs = AreaAll.Where(a => a.ParentCode == ProvinceList[0].Code).ToList();
foreach (var item in cs)
{
CityList.Add(item);
}
var dt = AreaAll.Where(a => a.ParentCode == CityList[0].Code).ToList();
foreach (var item in dt)
{
DistrictList.Add(item);
}
CurrentIndex = 0;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentIndex"));
}
}
public class Area
{
public int Code { get; set; }
public string Header { get; set; }
public int ParentCode { get; set; }
}
}
xaml代码:
<Window x:Class="XH.BindingLesson.DataCollection.ComboBoxWindow"
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:local="clr-namespace:XH.BindingLesson.DataCollection"
mc:Ignorable="d" FontSize="30"
Title="ComboBoxWindow" Height="450" Width="800">
<Window.DataContext>
<local:DataSource />
</Window.DataContext>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel.DataContext>
<local:MultiLevelDataSource />
</StackPanel.DataContext>
<ComboBox Width="200"
Height="45"
ItemsSource="{Binding ProvinceList}"
DisplayMemberPath="Header"
SelectedIndex="{Binding CurrentIndex}"
SelectedValuePath ="Code"
SelectedValue="{Binding CurrentProvince}"/>
<ComboBox Width="200" Height="45" ItemsSource="{Binding CityList}"
SelectedValuePath="Code"
SelectedValue="{Binding CurrentCity}"
DisplayMemberPath="Header"
SelectedIndex="{Binding CurrentIndex}"/>
<ComboBox Width="200"
Height="45"
ItemsSource="{Binding DistrictList}"
DisplayMemberPath="Header"
SelectedIndex="{Binding CurrentIndex}"/>
</StackPanel>
</Window>
演示: