根据标题ComboBox选择

时间:2018-04-17 18:28:45

标签: c# wpf mvvm datagrid

所以目前我有一个如下所示的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++;
}

1 个答案:

答案 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();
            }
        }
    }
}