2014-09-05 34 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