简体   繁体   中英

WPF datagrid binding with custom columns

Currently I am working on WPF application (with MVVM) in which I am displaying data in the DataGridView .

<DataGrid RowHeaderWidth="0" ItemsSource="{Binding PartsList,UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Item Name" IsReadOnly="True" Width="*" Binding="{Binding ProductName}"></DataGridTextColumn>
        <DataGridTextColumn Header="Model Name" IsReadOnly="True" Width="*" Binding="{Binding CarModelName}"></DataGridTextColumn>
        <DataGridTextColumn Header="Company Name" IsReadOnly="True" Width="*" Binding="{Binding CompanName}"></DataGridTextColumn>
        <DataGridTextColumn Header="Price" IsReadOnly="True" Width="*" Binding="{Binding Rate}">
    </DataGrid.Columns>
</DataGrid>

Here PartsList is an ObservableCollection of entity Part.

Now I want to add custom column to the DataGrid which shows discount and another column which shows net amount. How can I do this?

Please give a good idea to do this as I need to work with thousands of records so performance is very important for me.

Thank you in advance.

Try to add columns on the Loaded event of DataGrid :

private void DataGrid_Loaded_1(object sender, RoutedEventArgs e)
{
    dataGrid.Columns.Add((DataGridTextColumn)this.Resources["DiscountColumn"]);
    dataGrid.Columns.Add((DataGridTextColumn)this.Resources["NetAmountColumn"]);

    //Alternatively you can create columns in .cs like

    dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Dicount", Binding = new Binding("Discount") });
    dataGrid.Columns.Add(new DataGridTextColumn() { Header = "Net Amount", Binding = new Binding("NetAmount") });
}
<Window.Resources>
    <DataGridTextColumn x:Key="DiscountColumn" Header="Discount" IsReadOnly="True" Width="*" Binding="{Binding Discount}"/>
    <DataGridTextColumn x:Key="NetAmountColumn" Header="Net Amount" IsReadOnly="True" Width="*" Binding="{Binding NetAmount}"/>
</Window.Resources>

<DataGrid RowHeaderWidth="0" x:Name="dataGrid" Loaded="DataGrid_Loaded_1" />

This is an old post but I have done something similar using MVVM and WPF so thought I would through my two pennies worth in.

I cannot give any real indication of how it will perform however we haven't seen any real issues with displaying around a thousand objects in the ItemSource .

Apologies it is going to be a lengthy display of code but I will try to break it down so it is easy to follow.

Ultimately what you need to do is create an Attached Behavior .

Here is mine:

Behavior class

This does the core work in creating the actual columns and add them to your DataGrid based on the ColumnsSource that you bind to it.

public class DataGridColumnCollectionBehavior
{
    private object columnsSource;
    private DataGrid dataGrid;

    public DataGridColumnCollectionBehavior(DataGrid dataGrid)
    {
        this.dataGrid = dataGrid;
    }

    public object ColumnsSource
    {
        get { return this.columnsSource; }
        set
        {
            object oldValue = this.columnsSource;
            this.columnsSource = value;
            this.ColumnsSourceChanged(oldValue, this.columnsSource);
        }
    }

    public string DisplayMemberFormatMember { get; set; }

    public string DisplayMemberMember { get; set; }

    public string FontWeightBindingMember { get; set; }

    public string FontWeightMember { get; set; }

    public string HeaderTextMember { get; set; }

    public string IsEditableMember { get; set; }

    public string SortMember { get; set; }

    public string TextAlignmentMember { get; set; }

    public string TextColourMember { get; set; }

    public string WidthMember { get; set; }

    private void AddHandlers(ICollectionView collectionView)
    {
        collectionView.CollectionChanged += this.ColumnsSource_CollectionChanged;
    }

    private void ColumnsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        ICollectionView view = sender as ICollectionView;

        if (this.dataGrid == null)
        {
            return;
        }

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                for (int i = 0; i < e.NewItems.Count; i++)
                {
                    DataGridColumn column = CreateColumn(e.NewItems[i]);
                    dataGrid.Columns.Insert(e.NewStartingIndex + i, column);
                }
                break;
            case NotifyCollectionChangedAction.Move:
                List<DataGridColumn> columns = new List<DataGridColumn>();

                for (int i = 0; i < e.OldItems.Count; i++)
                {
                    DataGridColumn column = dataGrid.Columns[e.OldStartingIndex + i];
                    columns.Add(column);
                }

                for (int i = 0; i < e.NewItems.Count; i++)
                {
                    DataGridColumn column = columns[i];
                    dataGrid.Columns.Insert(e.NewStartingIndex + i, column);
                }
                break;
            case NotifyCollectionChangedAction.Remove:
                for (int i = 0; i < e.OldItems.Count; i++)
                {
                    dataGrid.Columns.RemoveAt(e.OldStartingIndex);
                }
                break;
            case NotifyCollectionChangedAction.Replace:
                for (int i = 0; i < e.NewItems.Count; i++)
                {
                    DataGridColumn column = CreateColumn(e.NewItems[i]);

                    dataGrid.Columns[e.NewStartingIndex + i] = column;
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                dataGrid.Columns.Clear();
                CreateColumns(sender as ICollectionView);
                break;
            default:
                break;
        }
    }

    private void ColumnsSourceChanged(object oldValue, object newValue)
    {
        if (this.dataGrid != null)
        {
            dataGrid.Columns.Clear();

            if (oldValue != null)
            {
                ICollectionView view = CollectionViewSource.GetDefaultView(oldValue);

                if (view != null)
                {
                    this.RemoveHandlers(view);
                }
            }

            if (newValue != null)
            {
                ICollectionView view = CollectionViewSource.GetDefaultView(newValue);

                if (view != null)
                {
                    this.AddHandlers(view);

                    this.CreateColumns(view);
                }
            }
        }
    }

    private DataGridColumn CreateColumn(object columnSource)
    {
        DataGridColumn column = new DataGridTemplateColumn();

        var textBlockFactory = new FrameworkElementFactory(typeof(TextBlock));
        ((DataGridTemplateColumn)column).CellTemplate = new DataTemplate { VisualTree = textBlockFactory };
        textBlockFactory.SetValue(TextBlock.MarginProperty, new Thickness(3));

        if (!string.IsNullOrWhiteSpace(this.FontWeightBindingMember))
        {
            string propertyName = GetPropertyValue(columnSource, this.FontWeightBindingMember) as string;

            textBlockFactory.SetBinding(TextBlock.FontWeightProperty, new Binding(propertyName));
        }
        else if (!string.IsNullOrWhiteSpace(this.FontWeightMember))
        {
            textBlockFactory.SetValue(TextBlock.FontWeightProperty, (FontWeight)GetPropertyValue(columnSource, this.FontWeightMember));
        }

        if (!string.IsNullOrWhiteSpace(this.SortMember))
        {
            column.SortMemberPath = GetPropertyValue(columnSource, this.SortMember) as string;
        }

        if (!string.IsNullOrEmpty(this.DisplayMemberMember))
        {
            string propertyName = GetPropertyValue(columnSource, this.DisplayMemberMember) as string;

            string format = null;

            if (!string.IsNullOrEmpty(this.DisplayMemberFormatMember))
            {
                format = GetPropertyValue(columnSource, this.DisplayMemberFormatMember) as string;
            }

            if (string.IsNullOrEmpty(format))
            {
                format = "{0}";
            }

            textBlockFactory.SetBinding(TextBlock.TextProperty, new Binding(propertyName) { StringFormat = format });

            // If there is no sort member defined default to the display member.
            if (string.IsNullOrWhiteSpace(column.SortMemberPath))
            {
                column.SortMemberPath = propertyName;
            }
        }

        if (!string.IsNullOrWhiteSpace(this.TextAlignmentMember))
        {
            textBlockFactory.SetValue(TextBlock.TextAlignmentProperty, GetPropertyValue(columnSource, this.TextAlignmentMember));
        }

        if (!string.IsNullOrEmpty(this.HeaderTextMember))
        {
            column.Header = GetPropertyValue(columnSource, this.HeaderTextMember);
        }

        if (!string.IsNullOrWhiteSpace(this.TextColourMember))
        {
            string propertyName = GetPropertyValue(columnSource, this.TextColourMember) as string;
            textBlockFactory.SetBinding(TextBlock.ForegroundProperty, new Binding(propertyName));
        }

        if (!string.IsNullOrEmpty(this.WidthMember))
        {
            double width = (double)GetPropertyValue(columnSource, this.WidthMember);
            column.Width = width;
        }

        return column;
    }

    private void CreateColumns(ICollectionView collectionView)
    {
        foreach (object item in collectionView)
        {
            DataGridColumn column = this.CreateColumn(item);

            this.dataGrid.Columns.Add(column);
        }
    }

    private object GetPropertyValue(object obj, string propertyName)
    {
        object returnVal = null;

        if (obj != null)
        {
            PropertyInfo prop = obj.GetType().GetProperty(propertyName);

            if (prop != null)
            {
                returnVal = prop.GetValue(obj, null);
            }
        }

        return returnVal;
    }

    private void RemoveHandlers(ICollectionView collectionView)
    {
        collectionView.CollectionChanged -= this.ColumnsSource_CollectionChanged;
    }
}

Accessor class

This is the class that you use within your XAML files in order to create the Binding s.

public static class DataGridColumnCollection
{
    public static readonly DependencyProperty ColumnCollectionBehaviourProperty =
        DependencyProperty.RegisterAttached("ColumnCollectionBehaviour", typeof(DataGridColumnCollectionBehaviour), typeof(DataGridColumnCollection), new UIPropertyMetadata(null));

    public static readonly DependencyProperty ColumnsSourceProperty =
        DependencyProperty.RegisterAttached("ColumnsSource", typeof(object), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.ColumnsSourcePropertyChanged));

    public static readonly DependencyProperty DisplayMemberFormatMemberProperty =
        DependencyProperty.RegisterAttached("DisplayMemberFormatMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.DisplayMemberFormatMemberChanged));

    public static readonly DependencyProperty DisplayMemberMemberProperty =
        DependencyProperty.RegisterAttached("DisplayMemberMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.DisplayMemberMemberChanged));

    public static readonly DependencyProperty FontWeightBindingMemberProperty =
        DependencyProperty.RegisterAttached("FontWeightBindingMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.FontWeightBindingMemberChanged));

    public static readonly DependencyProperty FontWeightMemberProperty =
        DependencyProperty.RegisterAttached("FontWeightMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.FontWeightMemberChanged));

    public static readonly DependencyProperty IsEditableMemberProperty =
        DependencyProperty.RegisterAttached("IsEditableMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.IsEditableMemberChanged));

    public static readonly DependencyProperty HeaderTextMemberProperty =
        DependencyProperty.RegisterAttached("HeaderTextMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.HeaderTextMemberChanged));

    public static readonly DependencyProperty SortMemberProperty =
        DependencyProperty.RegisterAttached("SortMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.SortMemberChanged));

    public static readonly DependencyProperty TextAlignmentMemberProperty =
        DependencyProperty.RegisterAttached("TextAlignmentMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.TextAlignmentMemberChanged));

    public static readonly DependencyProperty TextColourMemberProperty =
        DependencyProperty.RegisterAttached("TextColourMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.TextColourMemberChanged));

    public static readonly DependencyProperty WidthMemberProperty =
        DependencyProperty.RegisterAttached("WidthMember", typeof(string), typeof(DataGridColumnCollection), new UIPropertyMetadata(null, DataGridColumnCollection.WidthMemberChanged));

    public static DataGridColumnCollectionBehaviour GetColumnCollectionBehaviour(DependencyObject obj)
    {
        return (DataGridColumnCollectionBehaviour)obj.GetValue(ColumnCollectionBehaviourProperty);
    }

    public static void SetColumnCollectionBehaviour(DependencyObject obj, DataGridColumnCollectionBehaviour value)
    {
        obj.SetValue(ColumnCollectionBehaviourProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static object GetColumnsSource(DependencyObject obj)
    {
        return (object)obj.GetValue(ColumnsSourceProperty);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static void SetColumnsSource(DependencyObject obj, ObservableCollection<DataGridColumn> value)
    {
        obj.SetValue(ColumnsSourceProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetDisplayMemberFormatMember(DependencyObject obj)
    {
        return (string)obj.GetValue(DisplayMemberFormatMemberProperty);
    }

    public static void SetDisplayMemberFormatMember(DependencyObject obj, string value)
    {
        obj.SetValue(DisplayMemberFormatMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetDisplayMemberMember(DependencyObject obj)
    {
        return (string)obj.GetValue(DisplayMemberMemberProperty);
    }

    public static void SetDisplayMemberMember(DependencyObject obj, string value)
    {
        obj.SetValue(DisplayMemberMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetFontWeightBindingMember(DependencyObject obj)
    {
        return (string)obj.GetValue(FontWeightBindingMemberProperty);
    }

    public static void SetFontWeightBindingMember(DependencyObject obj, string value)
    {
        obj.SetValue(FontWeightBindingMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetFontWeightMember(DependencyObject obj)
    {
        return (string)obj.GetValue(FontWeightMemberProperty);
    }

    public static void SetFontWeightMember(DependencyObject obj, string value)
    {
        obj.SetValue(FontWeightMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetTextAlignmentMember(DependencyObject obj)
    {
        return (string)obj.GetValue(TextAlignmentMemberProperty);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static void SetTextAlignmentMember(DependencyObject obj, string value)
    {
        obj.SetValue(TextAlignmentMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetTextColourMember(DependencyObject obj)
    {
        return (string)obj.GetValue(TextColourMemberProperty);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static void SetTextColourMember(DependencyObject obj, string value)
    {
        obj.SetValue(TextColourMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static string GetHeaderTextMember(DependencyObject obj)
    {
        return (string)obj.GetValue(HeaderTextMemberProperty);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static void SetHeaderTextMember(DependencyObject obj, string value)
    {
        obj.SetValue(HeaderTextMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetWidthMember(DependencyObject obj)
    {
        return (string)obj.GetValue(WidthMemberProperty);
    }

    public static void SetWidthMember(DependencyObject obj, string value)
    {
        obj.SetValue(WidthMemberProperty, value);
    }

    [AttachedPropertyBrowsableForType(typeof(GridView))]
    public static string GetSortMember(DependencyObject obj)
    {
        return (string)obj.GetValue(SortMemberProperty);
    }

    public static void SetSortMember(DependencyObject obj, string value)
    {
        obj.SetValue(SortMemberProperty, value);
    }

    private static void ColumnsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).ColumnsSource = e.NewValue;
    }

    private static void DisplayMemberFormatMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).DisplayMemberFormatMember = e.NewValue as string;
    }

    private static void DisplayMemberMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).DisplayMemberMember = e.NewValue as string;
    }

    private static void FontWeightBindingMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).FontWeightBindingMember = e.NewValue as string;
    }

    private static void FontWeightMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).FontWeightMember = e.NewValue as string;
    }

    private static void IsEditableMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).IsEditableMember = e.NewValue as string;
    }

    private static void HeaderTextMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).HeaderTextMember = e.NewValue as string;
    }

    private static void SortMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).SortMember = e.NewValue as string;
    }

    private static void TextAlignmentMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).TextAlignmentMember = e.NewValue as string;
    }

    private static void TextColourMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).TextColourMember = e.NewValue as string;
    }

    private static void WidthMemberChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        DataGridColumnCollection.GetOrCreateBehaviour(sender).WidthMember = e.NewValue as string;
    }

    private static DataGridColumnCollectionBehaviour GetOrCreateBehaviour(DependencyObject source)
    {
        DataGridColumnCollectionBehaviour behaviour = DataGridColumnCollection.GetColumnCollectionBehaviour(source);

        if (behaviour == null)
        {
            behaviour = new DataGridColumnCollectionBehaviour(source as DataGrid);

            DataGridColumnCollection.SetColumnCollectionBehaviour(source, behaviour);
        }

        return behaviour;
    }
}

Example XAML usage

Now we actually get on to using it.

<DataGrid behaviors:DataGridColumnCollection.ColumnsSource="{Binding ColumnHeaders}"
          behaviors:DataGridColumnCollection.DisplayMemberFormatMember="Format"                         behaviors:DataGridColumnCollection.DisplayMemberMember="DisplayMember"
                      behaviors:DataGridColumnCollection.FontWeightBindingMember="FontWeightMember"
                      behaviors:DataGridColumnCollection.HeaderTextMember="Header"
                      behaviors:DataGridColumnCollection.SortMember="SortMember"
                      behaviors:DataGridColumnCollection.TextAlignmentMember="TextAlignment"
                      behaviors:DataGridColumnCollection.TextColourMember="TextColourMember"
                      behaviors:DataGridColumnCollection.WidthMember="Width"
                      ItemsSource="{Binding Items}">

Column Header class

This is just my 'simple' class that describes a column.

public class ColumnHeaderDescriptor
{
    public string DisplayMember { get; set; }

    public string FontWeightMember { get; set; }

    public string Format { get; set; }

    public string Header { get; set; }

    public string SortMember { get; set; }

    public TextAlignment TextAlignment { get; set; }

    public string TextColourMember { get; set; }

    public double Width { get; set; }
}

Instantiating the columns

And this is how I create them.

this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.Name, DisplayMember = "ItemName", Width = 250 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.ManufPartNumber, DisplayMember = "ManufPartNumber", Width = 150 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.LastUnitPrice, DisplayMember = "UnitPriceString", SortMember = "UnitPrice", Width = 90 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.AssetType, DisplayMember = "AssetType", Width = 100 });
this.ColumnHeaders.Add(new ColumnHeaderDescriptor { Header = Properties.Resources.Quantity, DisplayMember = "QuantityString", SortMember = "Quantity", Width = 80 });

Conclusion

Now I appreciate that this may not be fully MVVM but at the end of the day we have to make sacrifices in order to get the job done. This will allow you to dynamically create columns and show different pieces of information from within your View Model.

My solution is quite a complicated one and I can't take full credit. I am sure I got the starting point from an existing StackOverflow answer however I am at a loss to know where that is now. Given it's complexity by allowing the consumer to determine lots of different things like text colour, etc. this could well be overkill for others solutions and you could remove the unnecessary properties should you not need them.

Assuming you are using Entity Framework for your model; create a partial class of your model with property getters that calculate based on the base class. Ensure that you implement INotifyPropertyChange and then bind the new properties of NetAmount and Discount in new columns.

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