2014-09-05 140 views
1

我在DataGrid中有两个DataGridComboBoxColumns(技术上DataGridTemplateColumns)。我正在使用MVVM。将DataGridComboBoxColumn值传递给ObjectDataProvider的MethodParameter

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

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

使用第一列的SelectedValue作为ObjectDataProvider的MethodParameter的问题是当我在DataGrid中插入第二行时。如果第二行在第一列中使用的值与第一行第一列中的值不同,它将清除第一行的第二列值(因为新的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会很好用。

回答

1

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

你最好利用你的虚拟机。

这里是模仿你的模式和状况的一个例子:

业务/ VM实体:

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想象另一种变体,但我希望这将是一个为您的使用情况不够好......

+1

这工作。谢谢@Pragmateek!我正在努力保持数据访问代码不在我的模型中。现在我明白如果我想这样做,我需要将每个模型包装在提供数据访问逻辑的VM中。现在,我只需根据您的建议将公共字符串[] ProductLineCategoryCodes属性插入到我的模型中。我稍后将重构(创建更多的虚拟机,然后将其展示给我的View的主虚拟机),以从我的模型中提取数据访问代码。谢谢你让我走出窘境! BTW,很好的配置文件。我会在两节课学习编程,但我正忙于逆向工程时间。 – 2014-09-08 14:57:19

+0

@DavidAlanCondit很好用。是的,虚拟机在那里可以调整你的模型,从视图中缓解消耗。您不必将所有内容都包含在虚拟机中,并且找到合适的平衡点是WPF设计的一些难点,但您做得越多,所需时间就越少。 – Pragmateek 2014-09-08 19:03:10

+0

同意。我做了一个小的重构:我有一个静态的“ItemsProvider”类,它为我的许多模型(产品,位置,分区,ProductLineCategories等)加载了列表属性 - 它将这些列表从数据库中填充。需要这些列表的模型查询ItemsProvider,而不是查询数据库本身。这可能不是完美的,但它直接是右边的一步。任何感兴趣的人都可以参与 – 2014-09-08 19:12:19