【问题标题】:Passing DataGridComboBoxColumn value to a ObjectDataProvider's MethodParameter将 DataGridComboBoxColumn 值传递给 ObjectDataProvider 的 MethodParameter
【发布时间】:2014-09-05 15:48:55
【问题描述】:

我在 DataGrid 中有两个 DataGridComboBoxColumns(技术上是 DataGridTemplateColumns)。我正在使用 MVVM。

第一列的 ItemsSource 绑定到一个静态资源——那里没有问题。

第二列的 ItemsSource 取决于第一列选择的值。第一列的值 (SelectedValue) 作为 MethodParameter 传递给 ObjectDataProvider。 ObjectDataProvider 是第二列的 ItemsSource。

使用第一列的 SelectedValue 作为 ObjectDataProvider 的 MethodParameter 的问题是当我在 DataGrid 中插入第二行时。如果第二行在第 1 列中使用的值与在第一行的第 1 列中使用的值不同,则会清除第一行的第 2 列值(因为新的 SelectedValue 更改了允许的可供选择的项目列表,由 ObjectDataProvider 提供)。

我真的很想将第一列的 Text 值作为它的 MethodParameter 传递给 ObjectDataProvider,但是当第一列的 Text 值已经绑定到更新我的模型时,我该怎么做呢?

这是我的问题 XAML 的摘录:

<!--
My ObjectDataProvider. It returns a collection of strings for the user to choose from via the second DataGridTemplateColumn.
The first DataGridTemplateColumn feeds ObjectDataProvider a MethodParameter. 

The method is simple. It looks like:
    public List<String> ProductLineCategoryList_CategoryCodes(string productLineCode)
    {
        // return a list of strings based from an object collection, filtered by the passed in argument.
    }
-->

<ObjectDataProvider x:Key="categoryCodes" ObjectType="{x:Type e:ItemsProvider}" MethodName="ProductLineCategoryList_CategoryCodes">
    <ObjectDataProvider.MethodParameters>
        <x:StaticExtension Member="sys:String.Empty"/>
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

<!-- 
This DataGridComboBoxColumn lets the user choose a ProductLineCode.
Its SelectedValue provides a string value for the ObjectDataProvider's MethodParameter.
The ObjectDataProvider is used as the ItemsSource for the DataGridComboBoxColumn
below this one.
The problem with using SelectedValue to feed ObjectDataProvider a MethodParameter, when
a second row is added to my DataGrid and the second row uses a different ProductLineCode than
the first row, it clears the first row's ProductLineCategoryCode value.
-->
<DataGridTemplateColumn 
    Header="Product Line"
    ClipboardContentBinding="{Binding ProductLineCode}">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox
                IsEditable="True"
                ItemsSource="{x:Static e:ItemsProvider.ProductLineCategoryList_ProductLineCodeList}"
                SelectedValue="{Binding Source={StaticResource categoryCodes}, 
                                Path=MethodParameters[0], BindsDirectlyToSource=True, 
                                UpdateSourceTrigger=PropertyChanged}"
                Text="{Binding ProductLineCode, UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>                                               
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ProductLineCode}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>              
</DataGridTemplateColumn>

<!-- 
This DataGridComboBoxColumn uses the ObjectDataProvider for its ItemsSource.
ItemsSource s/b limited by the selection made from the above DataGridComboBoxColumn.
-->
<DataGridTemplateColumn 
    Header="Product Line Cat"
    ClipboardContentBinding="{Binding ProductLineCategoryCode}">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox 
                IsEditable="True"
                ItemsSource="{Binding Source={StaticResource categoryCodes}}"
                Text="{Binding ProductLineCategoryCode, UpdateSourceTrigger=PropertyChanged}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>                                               
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ProductLineCategoryCode}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>              
</DataGridTemplateColumn>                                           

我参考了网络寻求帮助,但找不到适合我的解决方案。我只需要传入一个字符串,而不是一个对象(尽管我想我可以更改我的 ObjectDataProvider 的方法来接受一个对象和then this might work)。如果这不是 DataGrid,这个 MSDN solution 会很好用。

【问题讨论】:

    标签: c# wpf mvvm wpfdatagrid


    【解决方案1】:

    恕我直言,您试图在 XAML 中做太多事情。

    你最好利用你的虚拟机。

    这是一个模仿您的模型和情况的示例:

    业务/虚拟机实体:

    public class Product : INotifyPropertyChanged
    {
        private static readonly IDictionary<string, string[]> catalog = new Dictionary<string, string[]>
        {
            { "Fruit", new[]{ "Apple", "Banana", "Cherry" } },
            { "Vegatable", new[]{ "Amaranth", "Broccolini", "Celery" } }
        };
    
        public static IDictionary<string, string[]> Catalog { get { return catalog; } }
    
        private string productLineCategoryCode;
        public string ProductLineCategoryCode
        {
            get { return productLineCategoryCode; }
            set
            {
                if (value != productLineCategoryCode)
                {
                    productLineCategoryCode = value;
                    PropertyChanged(this, new PropertyChangedEventArgs("ProductLineCategoryCode"));
                    PropertyChanged(this, new PropertyChangedEventArgs("ProductLineCodes"));
                }
            }
        }
    
        public IEnumerable<string> ProductLineCodes
        {
            get
            {
                return Catalog[ProductLineCategoryCode];
            }
        }
    
        private string productLineCode;
        public string ProductLineCode
        {
            get { return productLineCode; }
            set
            {
                if (value != productLineCode)
                {
                    productLineCode = value;
                    PropertyChanged(this, new PropertyChangedEventArgs("ProductLineCode"));
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
    

    ProductLineCodes 是给定类别的可用代码列表,您只需绑定到它。

    因此,当用户更改类别时,我们会通知它和可用代码列表都已更改。

    观点:

    <DataGrid ItemsSource="{Binding Products}" CanUserAddRows="True" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Product Line Cat"
                                    ClipboardContentBinding="{Binding ProductLineCategoryCode}">
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <ComboBox IsEditable="True"
                                    ItemsSource="{Binding Path=(local:Product.Catalog).Keys}"
                                    SelectedValue="{Binding ProductLineCategoryCode, UpdateSourceTrigger=PropertyChanged}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding ProductLineCategoryCode}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn Header="Product Line" ClipboardContentBinding="{Binding ProductLineCode}">
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <ComboBox IsEditable="True"
                                    ItemsSource="{Binding ProductLineCodes}"
                                    SelectedValue="{Binding ProductLineCode,UpdateSourceTrigger=PropertyChanged}">                                
                        </ComboBox>
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding ProductLineCode}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
    

    我可以想象另一个使用 IValueConverter 的变体,但我希望这个变体足以满足您的用例...

    【讨论】:

    • 这行得通。谢谢,@Pragmateek!我正在努力将数据访问代码排除在我的模型之外。现在我明白了,如果我想这样做,我需要将每个模型包装在一个提供数据访问逻辑的 VM 中。现在,我只是根据您的建议将公共字符串 [] ProductLineCategoryCodes 属性插入到我的模型中。稍后我将重构(创建更多 VM,然后将其暴露给我的 View 的主 VM)以从我的模型中提取数据访问代码。谢谢你让我摆脱困境!顺便说一句,伟大的个人资料。我会在两岁时学习编程,但我正忙于逆向工程。
    • @DavidAlanCondit 很高兴它有效。是的,VM 可以调整您的模型以减轻视图中的消耗。您不必将所有内容都包装在 VM 中,找到适当的平衡是 WPF 设计的一些困难部分,但是您做得越多,花费的时间就越少。
    • 同意。我做了一个小的重构:我有一个静态的“ItemsProvider”类,它加载了我的许多模型(产品、位置、部门、ProductLineCategories 等)的列表属性——它用数据库中的值填充这些列表。需要这些列表的模型会查询 ItemsProvider,而不是查询 db 本身。它可能并不完美,但它直接是正确的一步。仅供有兴趣的人参考。
    • 您所做的不仅仅是朝着正确方向迈出的一步。 :) 您刚刚完成了 repository 模式 的基本实现,所以很好。查看此模式以帮助您将数据层与应用程序的其余部分分离。
    猜你喜欢
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 2014-03-09
    • 1970-01-01
    相关资源
    最近更新 更多