C# 是否可以在 mvvm 模式中获取 wpf 数据网格上的动态列?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15549193/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 17:08:55  来源:igfitidea点击:

Is it possible to get dynamic columns on wpf datagrid in mvvm pattern?

c#wpfmvvmdatagrid

提问by Vinoth Ezhilan M

Im developing a product in wpf (mvvm pattern). I have a scenario in which i have spent more than a week for no good result. Pls help me if you can... Here is the scenario, according to the user's customization(user ll select the columns) i have to display a set of data into datagrid. Currently im binding a ObservableCollection with set of properties to the itemsource of datagrid. This limits me to fixed column size. Note: The n number of columns name is listed out for user's selection. If its done in code behind it is easy by "datagrid.columns.add()". Can any one out there help me in this scenario.

我在 wpf(mvvm 模式)中开发了一个产品。我有一个场景,我花了一个多星期都没有好的结果。如果可以,请帮助我......这是场景,根据用户的自定义(用户将选择列),我必须将一组数据显示到数据网格中。当前,我将具有一组属性的 ObservableCollection 绑定到数据网格的 itemsource。这将我限制为固定的列大小。注:列名列n 列供用户选择。如果它在后面的代码中完成它很容易通过“datagrid.columns.add()”。在这种情况下,任何人都可以帮助我。

my xaml:

我的 xaml:

<my:DataGrid AutoGenerateColumns="False" Margin="357,121.723,82,41" Name="dataGrid3" c:DataGridExtension.Columns="{Binding ColumnCollection}" />

my command class:

我的命令类:

public static class DataGridExtension
{
    public static ObservableCollection<DataGridColumn> GetColumns(DependencyObject obj)
    {
        return (ObservableCollection<DataGridColumn>)obj.GetValue(ColumnsProperty);
    }

    public static void SetColumns(DependencyObject obj, ObservableCollection<DataGridColumn> value)
    {
        obj.SetValue(ColumnsProperty, value);
    }

    public static readonly DependencyProperty ColumnsProperty = 
        DependencyProperty.RegisterAttached("Columns", typeof(ObservableCollection<DataGridColumn>),typeof(DataGridExtension),
         new UIPropertyMetadata (new ObservableCollection<DataGridColumn>(), OnDataGridColumnsPropertyChanged));

    private static void OnDataGridColumnsPropertyChanged(DependencyObject d,
           DependencyPropertyChangedEventArgs e)
    {
        if (d.GetType() == typeof(DataGrid))
        {
            DataGrid myGrid = d as DataGrid;

            ObservableCollection<DataGridColumn> Columns = (ObservableCollection<DataGridColumn>)e.NewValue;

            if (Columns != null)
            {
                myGrid.Columns.Clear();

                if (Columns != null && Columns.Count > 0)
                {
                    foreach (DataGridColumn dataGridColumn in Columns)
                    {
                        myGrid.Columns.Add(dataGridColumn);
                    }
                }


                Columns.CollectionChanged += delegate(object sender, NotifyCollectionChangedEventArgs args)
                {
                    if (args.NewItems != null)
                    {
                        //foreach (DataGridColumn column in args.NewItems.Cast<DataGridColumn>())
                        //{
                        //    myGrid.Columns.Add(column);
                        //}
                    }

                    if (args.OldItems != null)
                    {

                        //foreach (DataGridColumn column in args.OldItems.Cast<DataGridColumn>())
                        //{
                        //    myGrid.Columns.Remove(column);
                        //}
                    }
                };

            }
        }
    }
}

and my property in viewmodel:

和我在视图模型中的财产:

private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>();
public ObservableCollection<DataGridColumn> ColumnCollection
{
    get
    {
        return this._columnCollection;
    }
    set
    {
        _columnCollection = value;
        base.OnPropertyChanged("ColumnCollection");
        //Error
        //base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection);
    }
}

回答by Vinoth Ezhilan M

Thanks for your effort guy's... finally i have found the solution....

感谢您的努力……终于我找到了解决方案……

here its..(full wpf mvvm)

这里是..(完整的 wpf mvvm)

In my command file:

在我的命令文件中:

public class DataGridColumnsBehavior
    {
        public static readonly DependencyProperty BindableColumnsProperty =
            DependencyProperty.RegisterAttached("BindableColumns",
                                                typeof(ObservableCollection<DataGridColumn>),
                                                typeof(DataGridColumnsBehavior),
                                                new UIPropertyMetadata(null, BindableColumnsPropertyChanged));
        private static void BindableColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = source as DataGrid;
            ObservableCollection<DataGridColumn> columns = e.NewValue as ObservableCollection<DataGridColumn>;
            dataGrid.Columns.Clear();
            if (columns == null)
            {
                return;
            }
            foreach (DataGridColumn column in columns)
            {
                dataGrid.Columns.Add(column);
            }
            columns.CollectionChanged += (sender, e2) =>
            {
                NotifyCollectionChangedEventArgs ne = e2 as NotifyCollectionChangedEventArgs;
                if (ne.Action == NotifyCollectionChangedAction.Reset)
                {
                    dataGrid.Columns.Clear();
                    if (ne.NewItems != null)
                    {
                        foreach (DataGridColumn column in ne.NewItems)
                        {
                            dataGrid.Columns.Add(column);
                        }
                    }
                }
                else if (ne.Action == NotifyCollectionChangedAction.Add)
                {
                    if (ne.NewItems != null)
                    {
                        foreach (DataGridColumn column in ne.NewItems)
                        {
                            dataGrid.Columns.Add(column);
                        }
                    }
                }
                else if (ne.Action == NotifyCollectionChangedAction.Move)
                {
                    dataGrid.Columns.Move(ne.OldStartingIndex, ne.NewStartingIndex);
                }
                else if (ne.Action == NotifyCollectionChangedAction.Remove)
                {
                    if (ne.OldItems != null)
                    {
                        foreach (DataGridColumn column in ne.OldItems)
                        {
                            dataGrid.Columns.Remove(column);
                        }
                    }
                }
                else if (ne.Action == NotifyCollectionChangedAction.Replace)
                {
                    dataGrid.Columns[ne.NewStartingIndex] = ne.NewItems[0] as DataGridColumn;
                }
            };
        }
        public static void SetBindableColumns(DependencyObject element, ObservableCollection<DataGridColumn> value)
        {
            element.SetValue(BindableColumnsProperty, value);
        }
        public static ObservableCollection<DataGridColumn> GetBindableColumns(DependencyObject element)
        {
            return (ObservableCollection<DataGridColumn>)element.GetValue(BindableColumnsProperty);
        }
    }

in my xaml:

在我的 xaml 中:

<my:DataGrid AutoGenerateColumns="False" Margin="357,121.723,82,41" Name="dataGrid3" ItemsSource="{Binding Path=Datatable}" c:DataGridColumnsBehavior.BindableColumns="{Binding ColumnCollection}" />

and finaly in my viewmodel:

最后在我的视图模型中:

private ObservableCollection<DataGridColumn> _columnCollection = new ObservableCollection<DataGridColumn>();
        public ObservableCollection<DataGridColumn> ColumnCollection
        {
            get
            {
                return this._columnCollection;
            }
            set
            {
                _columnCollection = value;
                base.OnPropertyChanged("ColumnCollection");
                //Error
                //base.OnPropertyChanged<ObservableCollection<DataGridColumn>>(() => this.ColumnCollection);
            }
        }
        private DataTable _datatable = new DataTable();
        public DataTable Datatable
        {
            get
            {
                return _datatable;
            }
            set
            {
                if (_datatable != value)
                {
                    _datatable = value;
                }
                base.OnPropertyChanged("Datatable");
            }
        }

and in my constructor:

在我的构造函数中:

public MainViewModel()
        {
Datatable.Columns.Add("Name",typeof(string));
            Datatable.Columns.Add("Color", typeof(string));
            Datatable.Columns.Add("Phone", typeof(string));
            Datatable.Rows.Add("Vinoth", "#00FF00", "456345654");
            Datatable.Rows.Add("lkjasdgl", "Blue", "45654");
            Datatable.Rows.Add("Vinoth", "#FF0000", "456456");
System.Windows.Data.Binding bindings = new System.Windows.Data.Binding("Name");
            System.Windows.Data.Binding bindings1 = new System.Windows.Data.Binding("Phone");
            System.Windows.Data.Binding bindings2 = new System.Windows.Data.Binding("Color");
            DataGridTextColumn s = new DataGridTextColumn();
            s.Header = "Name";
            s.Binding = bindings;
            DataGridTextColumn s1 = new DataGridTextColumn();
            s1.Header = "Phone";
            s1.Binding = bindings1;
            DataGridTextColumn s2 = new DataGridTextColumn();
            s2.Header = "Color";
            s2.Binding = bindings2;

            FrameworkElementFactory textblock = new FrameworkElementFactory(typeof(TextBlock));
            textblock.Name = "text";
            System.Windows.Data.Binding prodID = new System.Windows.Data.Binding("Name");
            System.Windows.Data.Binding color = new System.Windows.Data.Binding("Color");
            textblock.SetBinding(TextBlock.TextProperty, prodID);
            textblock.SetValue(TextBlock.TextWrappingProperty, TextWrapping.Wrap);
            //textblock.SetValue(TextBlock.BackgroundProperty, color);
            textblock.SetValue(TextBlock.NameProperty, "textblock");
            //FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
            //border.SetValue(Border.NameProperty, "border");
            //border.AppendChild(textblock);
            DataTrigger t = new DataTrigger();
            t.Binding = new System.Windows.Data.Binding { Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter ="Phone" };
            t.Value = 1;
            t.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightGreen, textblock.Name));
            t.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name));
            DataTrigger t1 = new DataTrigger();
            t1.Binding = new System.Windows.Data.Binding { Path = new PropertyPath("Name"), Converter = new EnableConverter(), ConverterParameter = "Phone" };
            t1.Value = 2;
            t1.Setters.Add(new Setter(TextBlock.BackgroundProperty, Brushes.LightYellow, textblock.Name));
            t1.Setters.Add(new Setter(TextBlock.ToolTipProperty, bindings, textblock.Name));

            DataTemplate d = new DataTemplate();
            d.VisualTree = textblock;
            d.Triggers.Add(t);
            d.Triggers.Add(t1);

            DataGridTemplateColumn s3 = new DataGridTemplateColumn();
            s3.Header = "Name 1";
            s3.CellTemplate = d;
            s3.Width = 140;

            ColumnCollection.Add(s); 
            ColumnCollection.Add(s1);
            ColumnCollection.Add(s2);
            ColumnCollection.Add(s3);
    }

回答by StuS

I would like to extend the previous example (answer) the ability to subscribe and unsubscribe on event CollectionChanged.

我想扩展前面的示例(答案)订阅和取消订阅事件 CollectionChanged 的​​能力。

Behavior(add reference on System.Windows.Interactivity):

行为(在 System.Windows.Interactivity 上添加参考):

 public class ColumnsBindingBehaviour : Behavior<DataGrid>
{
    public ObservableCollection<DataGridColumn> Columns
    {
        get { return (ObservableCollection<DataGridColumn>) base.GetValue(ColumnsProperty); }
        set { base.SetValue(ColumnsProperty, value); }
    }

    public static readonly DependencyProperty ColumnsProperty = DependencyProperty.Register("Columns",
        typeof(ObservableCollection<DataGridColumn>), typeof(ColumnsBindingBehaviour),
            new PropertyMetadata(OnDataGridColumnsPropertyChanged));

    private static void OnDataGridColumnsPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        var context = source as ColumnsBindingBehaviour;

        var oldItems = e.OldValue as ObservableCollection<DataGridColumn>;

        if (oldItems != null)
        {
            foreach (var one in oldItems)
                context._datagridColumns.Remove(one);

            oldItems.CollectionChanged -= context.collectionChanged;
        }

        var newItems = e.NewValue as ObservableCollection<DataGridColumn>;

        if (newItems != null)
        {
            foreach (var one in newItems)
                context._datagridColumns.Add(one);

            newItems.CollectionChanged += context.collectionChanged;
        }
    }

    private ObservableCollection<DataGridColumn> _datagridColumns;

    protected override void OnAttached()
    {
        base.OnAttached();

        this._datagridColumns = AssociatedObject.Columns;
    }


    private void collectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                if (e.NewItems != null)
                    foreach (DataGridColumn one in e.NewItems)
                        _datagridColumns.Add(one);
                break;

            case NotifyCollectionChangedAction.Remove:
                if (e.OldItems != null)
                    foreach (DataGridColumn one in e.OldItems)
                        _datagridColumns.Remove(one);
                break;

            case NotifyCollectionChangedAction.Move:
                _datagridColumns.Move(e.OldStartingIndex, e.NewStartingIndex);
                break;

            case NotifyCollectionChangedAction.Reset:
                _datagridColumns.Clear();
                if (e.NewItems != null)
                    foreach (DataGridColumn one in e.NewItems)
                        _datagridColumns.Add(one);
                break;
        }
    }
}

View:

查看

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:loc="clr-namespace:WpfApplication1"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <DataGrid x:Name="_dataGrid">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Test" />
        </DataGrid.Columns>
        <i:Interaction.Behaviors>
            <loc:ColumnsBindingBehaviour Columns="{Binding DataGridColumns}"/>        
        </i:Interaction.Behaviors>
    </DataGrid>
</Grid>

ViewModel:

视图模型:

     private ObservableCollection<DataGridColumn> _dataGridColumns;
     public ObservableCollection<DataGridColumn> DataGridColumns
     {
         get
         {
             if (_dataGridColumns == null)
                 _dataGridColumns = new ObservableCollection<DataGridColumn>()
                 {
                     new DataGridTextColumn()
                     {
                         Header = "Column1"
                     }
                 };

             return _dataGridColumns;
         }
         set
         {
             _dataGridColumns = value;
             OnPropertyChanged("DataGridColumns");
         }
     }