在DataGrid中绑定自定义标头控件
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在DataGrid中绑定自定义标头控件相关的知识,希望对你有一定的参考价值。
我有一个自定义列标题,其中每列的标题都有TextBox
,其中包含列的名称和ComboBox
,其中包含有关列类型的信息,例如: “日期”,“数字”等
我正在尝试绑定ComboBox
并保持其值在某处,以便当用户从ComboBox
中选择新值时,我可以重新创建表,其中列的类型已更改。基本上我只需要以某种方式将每个ComboBox
的值以某种方式存储在列表中。我想对TextBox
做同样的事情,它应该包含列的名称。
这就是我到目前为止所拥有的。
<DataGrid x:Name="SampleGrid" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" ItemsSource="{Binding SampledData}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding ., Mode=OneWay}"/>
<ComboBox>
// How can I set it from ViewModel?
<ComboBoxItem Content="Date"></ComboBoxItem>
<ComboBoxItem Content="Number"></ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
视图模型:
private DataTable _sampledData = new DataTable();
public DataTable SampledData
{
get => _sampledData;
set { _sampledData = value; NotifyOfPropertyChange(() => SampledData); }
}
只要我稍后可以将映射传递给ViewModel,欢迎代码中的解决方案。
编辑:我一直在尝试使用ViewModel的List
工作,但没有运气:
public class ShellViewModel : Screen
{
public List<MyRowViewModel> Rows { get; set; }
public ShellViewModel()
{
Rows = new List<MyRowViewModel>
{
new MyRowViewModel { Column1 = "Test 1", Column2= 1 },
new MyRowViewModel { Column1 = "Test 2", Column2= 2 }
};
}
}
视图
<DataGrid ItemsSource="{Binding Rows}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding ., Mode=OneWay}"/>
<ComboBox ItemsSource="{Binding ??????}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.Resources>
</DataGrid>
行
public class MyRowViewModel : PropertyChangedBase
{
public string Column1 { get; set; }
public int Column2 { get; set; }
}
Aaditi:
为了澄清,我需要一个处理动态列数的解决方案,因此一些文件可能存储3列,有些文件可能存储40列。我使用它来解析csv文件以便稍后显示数据。为此,我必须知道文件包含哪些类型的值。因为某些类型可能不明确,我让用户决定他们想要哪些类型。这与Excel的“从文件加载”向导完全相同。
该向导加载一小块数据(100条记录),并允许用户决定列的类型。它会自动将列解析为:
- 让用户看看数据的外观
- 验证是否可以实际解析列(例如
68.35
无法解析为DateTime
)
另一件事是命名每一列。有人可能会为每个名为C1
,C2
的列加载csv ...但是他们想要分配有意义的名称,例如Temperature
,Average
。这当然也必须在以后解析,因为两列不能具有相同的名称,但是一旦我有一个可绑定的DataGrid
,我可以处理这个。
让我们将您的问题分解为多个部分并分别解决每个部分。
首先,DataGrid
itemsource
,为了让事情变得更容易,让我们说我们的DataGrid
只有两列,第1列和第2列.DataGrid
项目的基本模型应如下所示:
public class DataGridModel
{
public string FirstProperty { get; set; }
public string SecondProperty { get; set; }
}
现在,假设你有一个MainWindow
(有一个ViewModel或DataContext
设置为后面的代码),其中有一个DataGrid
,让我们将DataGridCollection
定义为它的ItemSource
:
private ObservableCollection<DataGridModel> _dataGridCollection=new ObservableCollection<DataGridModel>()
{
new DataGridModel(){FirstProperty = "first item",SecondProperty = "second item"},
new DataGridModel(){FirstProperty = "first item",SecondProperty = "second item"},
new DataGridModel(){FirstProperty = "first item",SecondProperty = "second item"}
};
public ObservableCollection<DataGridModel> DataGridCollection
{
get { return _dataGridCollection; }
set
{
if (Equals(value, _dataGridCollection)) return;
_dataGridCollection = value;
OnPropertyChanged();
}
}
第二,现在有趣的部分,列结构。让我们为你的DataGrid
列定义一个模型,该模型将包含设置DataGrid
列所需的所有属性,包括:
-DataTypesCollection:包含组合框项目源的集合。 -HeaderPropertyCollection:元组的集合,每个Tuple
代表一个列名和一个数据类型,数据类型基本上是列的combobox
的选定项。
public class DataGridColumnsModel:INotifyPropertyChanged
{
private ObservableCollection<string> _dataTypesCollection = new ObservableCollection<string>()
{
"Date","String","Number"
};
public ObservableCollection<string> DataTypesCollection
{
get { return _dataTypesCollection; }
set
{
if (Equals(value, _dataTypesCollection)) return;
_dataTypesCollection = value;
OnPropertyChanged();
}
}
private ObservableCollection<Tuple<string, string>> _headerPropertiesCollection=new ObservableCollection<Tuple<string, string>>()
{
new Tuple<string, string>("Column 1", "Date"),
new Tuple<string, string>("Column 2", "String")
}; //The Dictionary has a PropertyName (Item1), and a PropertyDataType (Item2)
public ObservableCollection<Tuple<string,string>> HeaderPropertyCollection
{
get { return _headerPropertiesCollection; }
set
{
if (Equals(value, _headerPropertiesCollection)) return;
_headerPropertiesCollection = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
现在,你在MainWindow的viewmodel(或codebehind)中定义了一个DataGridColumnsModel
的实例,我们将使用它来保存我们的DataGrid
结构:
private DataGridColumnsModel _dataGridColumnsModel=new DataGridColumnsModel();
public DataGridColumnsModel DataGridColumnsModel
{
get { return _dataGridColumnsModel; }
set
{
if (Equals(value, _dataGridColumnsModel)) return;
_dataGridColumnsModel = value;
OnPropertyChanged();
}
}
第三,得到列的TextBox
的值。为此,将使用MultiBinding
和MultiValueConverter
,将传递给MultiBinding
的第一个属性是我们定义的元组集合(列的名称和数据类型):HeaderPropertyCollection
,第二个是当前列将使用祖先绑定到DisplayIndex
从DataGridColumnHeader
检索的索引:
<TextBox >
<TextBox.Text>
<MultiBinding Converter="{StaticResource GetPropertConverter}">
<Binding RelativeSource="{RelativeSource AncestorType={x:Type Window}}" Path="DataGridColumnsModel.HeaderPropertyCollection"/>
<Binding Path="DisplayIndex" Mode="OneWay" RelativeSource="{RelativeSource RelativeSource={x:Type DataGridColumnHeader}}"/>
</MultiBinding>
</TextBox.Text>
转换器将使用元组集合中的索引简单地检索正确的项目:
public class GetPropertConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
try
{
var theCollection = values[0] as ObservableCollection<Tuple<string, string>>;
return (theCollection?[(int)values[1]])?.Item1; //Item1 is the column name, Item2 is the column's ocmbobox's selectedItem
}
catch (Exception)
{
//use a better implementation!
return null;
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
第四,最后一部分是在DataGrid
选择改变时更新ItemSource
的Combobox
,因为你可以使用System.Windows.Interactivity命名空间中定义的交互工具(它是Expression.Blend.Sdk的一部分,使用NuGet来安装它:安装包Expression.Blend.Sdk):
<ComboBox ItemsSource="{Binding DataGridColumnsModel.DataTypesCollection,RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateItemSourceCommand,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
每次selectionChanged
事件发生时,更新你的DataGrid
的ItemSource
应该添加到你的mainWindow的ViewModel的UpdateItemSourceCommand
:
private RelayCommand _updateItemSourceCommand;
public RelayCommand UpdateItemSourceCommand
{
get
{
return _updateItemSourceCommand
?? (_updateItemSourceCommand = new RelayCommand(
() =>
{
//Update your DataGridCollection, you could also pass a parameter and use it.
//Update your DataGridCollection based on DataGridColumnsModel.HeaderPropertyCollection
}));
}
}
Ps:我使用的RelayCommand
类是GalaSoft.MvvmLight.Command
命名空间的一部分,你可以通过NuGet添加它,或者定义你自己的命令。
最后这里是完整的xaml代码:
Window x:Class="WpfApp1.MainWindow"
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:WpfApp1"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:GetPropertConverter x:Key="GetPropertConverter"/>
</Window.Resources>
<Grid>
<DataGrid x:Name="SampleGrid" ItemsSource="{Binding DataGridCollection}" AutoGenerateColumns="False">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel>
<TextBox >
以上是关于在DataGrid中绑定自定义标头控件的主要内容,如果未能解决你的问题,请参考以下文章
WPF 在datagrid模板列中添加用户控件,在后台如何快速的检索到该控件。急!!!