2014-09-05 1 views
1

DataGrid에 두 개의 DataGridComboBoxColumns (기술적으로 DataGridTemplateColumns)가 있습니다. MVVM을 사용하고 있습니다.DataGridComboBoxColumn 값을 ObjectDataProvider의 MethodParameter에 전달

첫 번째 열의 ItemsSource는 정적 리소스에 바인딩됩니다. 아무 문제가 없습니다.

두 번째 열의 ItemsSource는 첫 번째 열에서 선택한 값에 따라 다릅니다. 첫 번째 열의 값 (SelectedValue)은 ObjectDataProvider에 MethodParameter로 전달됩니다. ObjectDataProvider는 두 번째 열의 ItemsSource입니다.

ObjectDataProvider의 MethodParameter로 첫 번째 열의 SelectedValue를 사용할 때의 문제점은 DataGrid에 두 번째 행을 삽입 할 때 발생합니다. 두 번째 행에서 첫 번째 행의 열 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) 만 객체가 아닌 문자열을 전달하면됩니다. 이 MSDN solution은 DataGrid가 아닌 경우 잘 작동합니다.

답변

1

IMHO 당신은 XAML에서 너무 많은 것을하려고합니다.

VM을 활용하는 것이 좋습니다.

비즈니스/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 속성을 제안에 따라 내 모델에 연결했습니다. 내 모델에서 데이터 액세스 코드를 추출하기 위해 나중에 리팩토링 할 것입니다. (뷰에 대한 기본 VM에 더 많은 VM을 추가하여 많이 만듭니다). 저를 진부한 곳에서 꺼내 주셔서 감사합니다! BTW, 훌륭한 프로필. 나는 2시에 프로그래밍을 배웠을 것이다. 그러나 나는 역 공학 시간을 바쁘게 보내고 있었다. –

+0

@DavidAlanCondit 다행입니다. 그렇습니다. VM은 뷰를 통해 소비를 용이하게하기 위해 모델을 조정합니다.VM에서 모든 것을 랩핑 할 필요는 없으며 올바른 균형을 찾는 일은 WPF 디자인의 어려운 부분 중 하나이지만, 소요 시간은 줄어 듭니다. – Pragmateek

+0

동의. 나는 작은 리팩토링을했다 : 나는 많은 모델 (제품, 위치, 부서, ProductLineCategories 등)에 대한 목록 속성으로로드되는 정적 "ItemsProvider"클래스를 가지고있다. 이 목록이 필요한 모델은 db 자체를 쿼리하는 대신 ItemsProvider를 쿼리합니다. 아마 완벽하지는 않지만 바로 오른쪽에있는 단계입니다. 관심있는 사람을위한 참고 사항. –

관련 문제