带有自定义列的 WPF 数据网格绑定

Posted

技术标签:

【中文标题】带有自定义列的 WPF 数据网格绑定【英文标题】:WPF datagrid binding with custom columns 【发布时间】:2013-08-11 16:55:03 【问题描述】:

目前我正在开发 WPF 应用程序(使用 MVVM),其中我在 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>

这里的 PartsList 是实体 Part 的ObservableCollection

现在我想将自定义列添加到显示折扣的DataGrid 和显示净金额的另一列。我该怎么做?

请给出一个好主意,因为我需要处理数千条记录,所以性能对我来说非常重要。

提前谢谢你。

【问题讨论】:

您不能在上面的 XAML 中添加“折扣”和“净金额”DataGrid 列,并在您的零件视图模型类中包含这些属性,还是有其他“自定义”列要求? 不需要其他自定义列。我会处理您的解决方案。谢谢!! 或者您可以简单地添加列并使用转换器来计算值 看看我最近对计算数据网格列的回答:***.com/a/48566856/5265292 也许这就是您需要的,然后我们可以将其作为副本关闭;) 如果您不想更改项目类,您可以创建具有附加属性的包装器并将原始属性值传递给被包装的项目。编辑:*我只是意识到这个问题已经过时了,最近才修改了答案* 【参考方案1】:

假设您正在为您的模型使用实体框架;使用基于基类计算的属性获取器创建模型的部分类。确保您实现 INotifyPropertyChange,然后在新列中绑定 NetAmount 和 Discount 的新属性。

【讨论】:

【参考方案2】:

这是一篇旧帖子,但我使用 MVVMWPF 做了类似的事情,所以我想我会花掉我的两分钱。

我无法真正说明它的性能,但我们还没有看到在 ItemSource 中显示大约一千个对象有任何实际问题。

抱歉,这将是一段冗长的代码展示,但我会尝试将其分解,以便于理解。

您最终需要做的是创建一个Attached Behavior。

这是我的:

行为类

这完成了创建实际列的核心工作,并根据您绑定到它的ColumnsSource 将它们添加到您的DataGrid

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;
    

访问器类

这是您在 XAML 文件中用于创建 Bindings 的类。

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;
    

XAML 用法示例

现在我们开始实际使用它了。

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

列头类

这只是我描述列的“简单”类。

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; 

实例化列

这就是我创建它们的方式。

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

结论

现在我明白这可能不完全是MVVM,但最终我们必须做出牺牲才能完成工作。这将允许您在视图模型中动态创建列并显示不同的信息。

我的解决方案相当复杂,我不能完全相信。我确信我从现有的 *** 答案中得到了起点,但是我不知道现在在哪里。考虑到它的复杂性,它允许消费者确定许多不同的东西,比如文本颜色等。这对于其他解决方案来说很可能是矫枉过正,如果你不需要它们,你可以删除不必要的属性。

【讨论】:

【参考方案3】:

尝试在DataGridLoaded事件上添加列:

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

【讨论】:

感谢您的建议,但我正在使用 MVVM。那么你能指导我如何使用 MVVM 做到这一点吗? 您不能在 ViewModel 中添加列,因为您需要引用数据网格。 好的,我会尝试不同的方式。 @rupareliab 我很感激您可能已经解决了这个问题,或者已经放弃了,但是我提供了一个解决方案,让您无需在 ViewModel 中引用 DataGrid 就可以做到这一点

以上是关于带有自定义列的 WPF 数据网格绑定的主要内容,如果未能解决你的问题,请参考以下文章

自定义 WPF 数据绑定:如何添加自定义逻辑?

WPF 自定义 DatagridColumn 绑定问题

WPF 数据网格。无法突出显示自定义样式的单元格

将wpf datagrid导出为自定义Excel CSV文件

wpf 自定义控件的自定义属性的数据绑定问题

WPF Window 自定义事件创建和绑定到 ICommand