根据标题组合框选择更改列 [英] Changing column based on header ComboBox selection

查看:73
本文介绍了根据标题组合框选择更改列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,目前我有一个看起来像这样的DataGrid:
当前的DataGridView

So currently I have a DataGrid that looks like this: Current DataGridView

其目的是创建一个导入实用程序。以CSV格式接收的数据的结构并不总是相同。列更改顺序,有时它们仅提供部分数据。

The goal behind this is to create an import utility. The data received in CSV format isn't always structured the same way. The columns change order, sometimes they only provide partial data.

我希望用户能够选择每列的定向位置。我遇到了一些问题,希望有经验的人可以指导我。

I'd like for the user to be able to select where each column is directed. I'm running into a few problems and I was hoping someone more experienced could direct me.

首先,输入的数据类型仍然受到限制。因此,例如,如果第1列是整数,则不允许输入文本。我打算编写一个事件处理程序,以使标头ComboBox更改为更改BindingExpression。但实际上,这只是一个无类型的列。之后,将根据comboBox选择将其输入到实际表中。

First, the type of the data entered is still restricted. So for example, if column 1 is an integer, then it won't allow for text input. I was planning on writing an event handler for when the headers ComboBox changed to change the BindingExpression. but realistically this just needs to be a typeless column. Which would be entered into the actual table based on the comboBox selection afterwards.

我也不确定如何以这种方式生成ViewModel的ComboBox。

I'm also unsure how to identify/get at the ComboBox from the ViewModel when it's generated this way.

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++;
}


推荐答案

下面是基本接线。我们有一个名为 ColumnHeaderViewmodel 的类,它代表一列的标题。 MainViewModel 包含这些集合以及称为 Data DataTable 属性。 c $ c>。 DataTable 是在 DataGrid 中显示任意的,仅在运行时已知的CSV的一种相对可忍的方式。另一种方法是 ObservableCollection< ExpandoObject> ,其中包含一些额外的代码来生成列。如果需要,我们可以讨论一下。

Here's a rough sketch of the basic wiring. We've got a class called ColumnHeaderViewmodel that represents the header for one column. The MainViewModel has a collection of those as well as a DataTable property called Data. That DataTable is one relatively tolerable way to display arbitrary, known-only-at-runtime CSV in a DataGrid. Another way is an ObservableCollection<ExpandoObject>, with some extra code to generate the columns. We can go into that if need be.

在C#中创建WPF用户界面是个坏主意。拥有WPF经验的任何人都尽可能避免这种事情。

It's a bad idea to create WPF UI in C#. Anybody who's experienced with WPF avoids that kind of thing if he possibly can.

我们将CSV存储在单独的结构中,也许是 List< List< String>> 之类的东西,并且每次列标题更改时,我们都会从该集合中重新填充 DataTable 。那是你的问题,我没有解决。请参见 TODO注释。

We'll store the CSV in a separate structure, perhaps List<List<String>> or something, and every time the column headers change, we'll repopulate the DataTable from that collection. That's your problem, I'm not addressing it. See the "TODO" comments.

此代码是一个完整的独立示例,可让您从标题组合框中重命名列。您可以将其他内容(例如数据类型或包含/排除标志)添加到 ColumnHeaderViewModel 和标题模板中。

This code is a complete self-contained example that lets you rename columns from header comboboxes. You can add other stuff (e.g. datatype, or an include/exclude flag) to ColumnHeaderViewModel and to the header template.

首先,我们将说明如何将组合框放在DataGrid列标题中。不要用C#来做。

First, we'll illustrate how to put comboboxes in DataGrid column headers. Don't do it in C#.

<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>

第二,在上述XAML的代码隐藏文件中(也许是MainWindow,也许是UserControl), '将ColumnHeaderViewModels连接起来;它们位于 ViewModel.Columns

Second, in the codebehind file for the above XAML (maybe your MainWindow, maybe a UserControl), we'll wire up the ColumnHeaderViewModels; they're in ViewModel.Columns:

    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);
    }

MainViewModel:

MainViewModel:

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

ColumnHeaderViewModel

ColumnHeaderViewModel

public class ValueChangingEventArgs<T> : EventArgs
{
    public ValueChangingEventArgs(T oldValue, T newValue)
    {
        OldValue = oldValue;
        NewValue = newValue;
    }

    public T OldValue { get; private set; }
    public T NewValue { get; private set; }
}

public class ColumnHeaderViewModel : ViewModelBase
{
    public ColumnHeaderViewModel(List<String> names, string name)
    {
        ColumnNames = names;
        ColumnName = name;
    }

    public List<String> ColumnNames { get; private set; }

    public event EventHandler<ValueChangingEventArgs<String>> NameChanging;


    #region ColumnName Property
    private String _columnName = default(String);
    public String ColumnName
    {
        get { return _columnName; }
        set
        {
            if (value != _columnName)
            {
                var oldName = ColumnName;
                _columnName = value;
                OnPropertyChanged();
                NameChanging?.Invoke(this, new ValueChangingEventArgs<string>(oldName, ColumnName));
            }
        }
    }
    #endregion ColumnName Property

    //  Rename without raising NameChanging
    public void Rename(string newName)
    {
        _columnName = newName;
        OnPropertyChanged(nameof(ColumnName));
    }
}

这篇关于根据标题组合框选择更改列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆