C# 带有自定义列的WPF datagrid绑定

C# 带有自定义列的WPF datagrid绑定,c#,.net,wpf,mvvm,wpfdatagrid,C#,.net,Wpf,Mvvm,Wpfdatagrid,目前,我正在开发WPF应用程序(使用MVVM),在该应用程序中,我在DataGridView中显示数据 <DataGrid RowHeaderWidth="0" ItemsSource="{Binding PartsList,UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Head

目前,我正在开发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是实体部分的
可观察集合

现在,我想在显示折扣的
DataGrid
中添加自定义列,并在另一列中显示净金额。我该怎么做

请给一个好主意,这样做,因为我需要与数以千计的记录,所以性能对我来说是非常重要的工作


提前感谢。

尝试在
DataGrid
Loaded
事件中添加列:

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

这是一篇老文章,但我用
MVVM
WPF
做过类似的事情,所以我想我会花掉我的两便士

我无法给出它将如何执行的任何实际指示,但是我们没有看到在
ItemSource
中显示大约一千个对象的任何实际问题

抱歉,这将是一个漫长的代码显示,但我会尝试打破它,所以很容易遵循

最终,您需要做的是创建一个

这是我的:

行为类

这是创建实际列的核心工作,并基于绑定到它的
columnsource
将它们添加到
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使用示例

现在我们实际开始使用它

<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}">
实例化列

这就是我创建它们的方式

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,但最终我们必须做出牺牲才能完成任务。这将允许您动态创建列,并显示视图模型中的不同信息


我的解决方案相当复杂,我不能完全相信。我确信我从一个现有的StackOverflow答案中得到了一个起点,但是我不知道现在的答案在哪里。考虑到它的复杂性,允许消费者确定大量不同的内容,如文本颜色等。对于其他解决方案来说,这可能是过分的,如果您不需要它们,您可以删除不必要的属性。

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

谢谢你的建议,但我正在使用MVVM。那么,您能告诉我如何使用MVVM实现这一点吗?您不能在ViewModel中添加列,因为您需要引用datagrid。好的,我将尝试另一种方式。@rupareliab我很感激您可能已经解决了这个问题,或者已经放弃了,但是我提供了一种解决方案,可以让您在不需要使用在ViewModel中引用
DataGrid
,您是否可以将“折扣”和“净额”DataGrid列添加到上面的XAML中,并将这些属性包含在零件视图模型类中,或者是否有其他“自定义”列要求?不需要其他自定义列。我将处理您的解决方案。谢谢!!或者你可以简单地添加列并使用转换器来计算值看看我最近关于计算数据网格列的回答:也许这就是你需要的,然后我们可以将其作为副本关闭;)如果不想更改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 });