简体   繁体   中英

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

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.

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

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

here its..(full 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:

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

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

Behavior (add reference on 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");
         }
     }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM