I'm developing a product in wpf (using the MVVM pattern). According to the user's customization (user ll select the columns) I have to display a set of data into a datagrid. Currently I'm binding an ObservableCollection
with set of properties to the ItemSource
of the 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;
var 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 +=
(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);
}
}
The Grid class in WPF represents a Grid control. The following code snippet creates a Grid control, sets its width, horizontal alignment, vertical alignment, show grid lines, and background color. Grid DynamicGrid = new Grid();
WPF DataGrid (SfDataGrid) allows you to sort the data against one or more columns either in ascending or descending order. The sorting can be performed by clicking a column header. You can enable/disable the sorting for all the columns in DataGrid by using DataGrid. AllowSorting property.
A Grid is a control for laying out other controls on the form (or page). A DataGrid is a control for displaying tabular data as read from a database for example.
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");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With