所以目前我有一个如下所示的DataGrid: Current DataGridView
这背后的目标是创建一个导入实用程序。以CSV格式接收的数据并不总是以相同的方式构建。列更改顺序,有时它们仅提供部分数据。
我希望用户能够选择每列的定向位置。我遇到了一些问题,我希望有经验的人可以指导我。
首先,输入的数据类型仍受限制。因此,例如,如果第1列是整数,那么它将不允许文本输入。我正计划在标题ComboBox更改时更改BindingExpression时编写事件处理程序。但实际上这只需要一个无类型的列。然后根据组合框选择将其输入到实际表中。
我也不确定如何通过ViewModel以这种方式识别/获取ComboBox。
XAML
<DataGrid x:Name="ImportTable"
ItemsSource="{Binding displayTable}"
AutoGeneratingColumn="OnAutoGeneratingColumn"
AutoGenerateColumns="True"
CanUserAddRows="True"
CanUserDeleteRows="True"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
MaxWidth="1300"
MaxHeight="600"
/>
xaml.cs
//i keeps track of the column index, temporary solution to preset columns
private int i;
private void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var cb = new ComboBox();
cb.ItemsSource = (DataContext as EnterValueDialogViewModel).displayTable.Columns;
cb.DisplayMemberPath = "ColumnName";
cb.SelectedValue = e.PropertyName.ToString();
cb.SelectedIndex = i;
e.Column.Header = cb;
i++;
}
答案 0 :(得分:0)
这是基本布线的草图。我们有一个名为ColumnHeaderViewmodel
的类,它代表一列的标题。 MainViewModel
包含这些属性以及名为DataTable
的{{1}}属性的集合。 Data
是一种相对容忍的方式,可以在DataTable
中显示任意的,在运行时已知的CSV。另一种方法是DataGrid
,还有一些额外的代码来生成列。如果需要,我们可以进入。
在C#中创建WPF UI是个坏主意。任何对WPF有经验的人都可以避免这种事情。
我们会将CSV存储在单独的结构中,可能是ObservableCollection<ExpandoObject>
或其他内容,每次列标题更改时,我们都会从该集合中重新填充List<List<String>>
。这是你的问题,我没有解决它。见&#34; TODO&#34;评论。
此代码是一个完整的自包含示例,可让您从标题组框中重命名列。您可以向DataTable
和标题模板添加其他内容(例如数据类型或包含/排除标志)。
首先,我们将说明如何将组合框放在DataGrid列标题中。不要用C#做。
ColumnHeaderViewModel
其次,在上面的XAML的代码隐藏文件中(可能是你的MainWindow,也许是UserControl),我们将连接ColumnHeaderViewModels;他们在<DataGrid
ItemsSource="{Binding Data}"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox
ItemsSource="{Binding ColumnNames}"
SelectedItem="{Binding ColumnName}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
</DataGrid>
:
ViewModel.Columns
MainViewModel:
public MainViewModel ViewModel => DataContext as MainViewModel;
private void DataGrid_AutoGeneratingColumn(object sender,
DataGridAutoGeneratingColumnEventArgs e)
{
e.Column.Header = ViewModel.Columns
.FirstOrDefault(c => c.ColumnName == e.PropertyName);
}
ColumnHeaderViewModel
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
// TODO:
// Load CSV
// TODO:
// These are fake. Get actual column names from CSV
ColumnNames = new List<string> { "Foo", "Bar", "Numeric" };
Columns = new List<ColumnHeaderViewModel>();
foreach (var name in ColumnNames)
{
Data.Columns.Add(new DataColumn(name));
var col = new ColumnHeaderViewModel(ColumnNames, name);
col.NameChanging += Column_NameChanging;
Columns.Add(col);
}
UpdateDataTableFromCSVRows();
}
private void Column_NameChanging(object sender, ValueChangingEventArgs<String> e)
{
var col = sender as ColumnHeaderViewModel;
// Swap names. DataTable throws an exception on column name collisions
var otherCol = Columns.FirstOrDefault(c => c != col && c.ColumnName == e.NewValue);
if (e.OldValue != null && otherCol != null)
{
// Use Rename() method so it won't raise NameChanged again.
// However, UpdateDataTableFromCSVRows() should be clever enough
// to do nothing in cases where the column order is unchanged.
otherCol.Rename(e.OldValue);
}
UpdateDataTableFromCSVRows();
}
protected void UpdateDataTableFromCSVRows()
{
// TODO:
// Update the DataTable from the CSV rows, based on the new
// column names.
}
public List<ColumnHeaderViewModel> Columns { get; private set; }
public List<String> ColumnNames { get; private set; }
private DataTable _data = default(DataTable);
public DataTable Data
{
get { return _data; }
private set
{
if (value != _data)
{
_data = value;
OnPropertyChanged();
}
}
}
}