2016-04-04 32 views
1

私の目標は、WPFで一連のカスケードコンボボックスを使用することです。私はMVVMモデルを使用しようとしていますが、まだ学習しています。MVVMを使用したDataGridのカスケードコンボボックス

プロジェクトの背景情報。私は従業員の時間を編集しようとしています。

私は、DataGridで選択した従業員のTimesのリストを持っています。 DataGridの各行はTimeオブジェクトです。時間は、InTime、OutTime、Date、Hours ...などのいくつかのフィールドで構成されます。時間にも部門と仕事があります。

現在、Department ComboBoxが有線で動作していますが、Departmentフィールドで選択した内容に基づいてJobコンボボックスを構築する方法がわかりません。ここで

は私のViewModelが選択されているどのように私は何に基づいて、そのアイテムを投入するために、私の仕事のコンボボックスを実装するのです

<DataGrid Grid.Row="1" Margin="15,0,15,15" Visibility="Visible" FontSize="14" HorizontalGridLinesBrush="{StaticResource Nelson2}" VerticalGridLinesBrush="{StaticResource Nelson2}" ItemsSource="{Binding Times}" SelectionMode="Single" CellEditEnding="DataGrid_CellEditEnding" RowEditEnding="DataGrid_RowEditEnding" AutoGenerateColumns="False"> 
    <DataGridTemplateColumn Header="Department Code"> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Path= Department.Display}"/> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate> 
      <DataGridTemplateColumn.CellEditingTemplate> 
       <DataTemplate> 
         <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource Findancestor, AncestorType={x:Type UserControl}}, Path=DataContext.Departments}" DisplayMemberPath="Display" SelectedValuePath="DepartmentCode" SelectedValue="{Binding Department.DepartmentCode}" /> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellEditingTemplate> 
    </DataGridTemplateColumn> 
</DataGrid> 

だから私のデータグリッドにバインドされ

public ObservableCollection<Time> Times { get; set; } 
public ObservableCollection<Department> Departments { get; set; } 

public TimeSheetsViewModel() 
{ 
    Times = new ObservableCollection<Time>(); 
    Departments = new ObservableCollection<Departments>(); 
    GetDepartments(); 
} 

private void GetDepartments() 
{ 
    /* 
    This section contains code to connect to my SQL Database and fills a DataTable dt 
    */ 

    if (Departments != null) 
     Departments.Clear(); 


    for (int i = 0; i < dt.Rows.Count; i++) 
    { 
     Department d = new Department() { Display = dt.Rows[i]["DISPLAY"].ToString(), DepartmentCode = dt.Rows[i]["DEPARTMENT_CODE"].ToString(), CompanyCode = dt.Rows[i]["COMPANY_CODE"].ToString() }; 
      Departments.Add(d); 
    } 
} 

この

に設定されている方法ですその列の部門のために?

私はこのコードを同じビューモデルに入れたいと思っています。

ありがとうございました。

EDIT(04/05/16):私は、そのオブジェクトのフィールドに異なるものをバインドすることコンバーターを使用できるように

どのように変換して、オブジェクトを返すことができます。

これは私のコンバータ

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
{ 
    string departmentCode = values[0].ToString(); 
    ObservableCollection<Department> Departments = values[1] as ObservableCollection<Department>; 

    return Departments.FirstOrDefault(Department => Department.DepartmentCode == departmentCode); 
} 

であり、これは私のバインディング

コンバータは部門オブジェクトを返します
<TextBlock > 
    <TextBlock.Text> 
     <MultiBinding Converter="{StaticResource DeptCodeToDeptConverter}" > 
      <Binding Path="DepartmentCode" /> 
      <Binding Path="DataContext.Departments" RelativeSource="{RelativeSource Findancestor, AncestorType={x:Type UserControl}}"/> 
     </MultiBinding> 
    </TextBlock.Text> 
</TextBlock> 

が、何であれば、私はTextBlockののテキストはDepartment.Nameになりたいかと言います位置。別のコントロールで使用したいフィールドのそれぞれを返すために新しいコンバータを作成する必要がありますか?または、私はこの方法を使って何をしたいのかを達成する方法がありますか?

答えて

0

私はこれを行うには、2つの方法のいずれかを選択したい:

1.マルチバインディングコンバーターを使用します。あなたの最初のコンボボックス 'ItemsSourceは物事のコレクションにバインドされます。 2番目のアイテムは、1番目のSelectedItemでマルチバインディングコンバーターを使用し、2番目のコンボボックスで使用可能なアイテムセットを使用して、2番目のItemsSourceのコレクションを返します。

public class DepartmentJobComboboValueConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     Department department = values[0] as Department; 
     ObservableCollection<string> jobCodes = values[1] as ObservableCollection<string>; 

     //do our logic to filter the job codes by department 
     return jobCodes.Where(jobCode => jobCode.StartsWith(department.DepartmentCode)).ToList(); 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

あなたが最初の値としてselectedItemsのをバインドすることができ、あなたの第二の値としてコレクションの辞書:

最初のコンボボックスは、それがアイテムを選択されている変更

、更新する結合

<DataGrid ItemsSource="{Binding Times}" 
       SelectionMode="Single" 
       AutoGenerateColumns="False"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Header="Department"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <TextBlock Text="{Binding Path= Department.DepartmentCode}"/> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
       <DataGridTemplateColumn.CellEditingTemplate> 
        <DataTemplate> 
         <ComboBox ItemsSource="{Binding RelativeSource={RelativeSource Findancestor, AncestorType={x:Type UserControl}}, Path=DataContext.Departments, UpdateSourceTrigger=PropertyChanged}" 
            DisplayMemberPath="DepartmentCode" 
            SelectedValuePath="DepartmentCode" 
            SelectedValue="{Binding Department.DepartmentCode}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellEditingTemplate> 
      </DataGridTemplateColumn> 
      <DataGridTemplateColumn Header="Job code"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <TextBlock Text="{Binding Path=Job}"/> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
       <DataGridTemplateColumn.CellEditingTemplate> 
        <DataTemplate> 
         <ComboBox SelectedValue="{Binding Job}"> 
          <ComboBox.ItemsSource> 
           <MultiBinding Converter="{StaticResource DepartmentJobComboboValueConverter}"> 
            <Binding Path="Department" /> 
            <Binding Path="DataContext.JobCodes" 
              RelativeSource="{RelativeSource Findancestor, AncestorType={x:Type UserControl}}"/> 
           </MultiBinding> 
          </ComboBox.ItemsSource> 
         </ComboBox> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellEditingTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 

:静的リソースとして多結合コンバータを

これは、これを生産する

public class TimeSheetsViewModel 
{ 
    public ObservableCollection<Time> Times { get; set; } 
    public ObservableCollection<Department> Departments { get; set; } 
    public ObservableCollection<string> JobCodes { get; set; } 

    public TimeSheetsViewModel() 
    { 
     Times = new ObservableCollection<Time>(); 
     Departments = new ObservableCollection<Department>(); 
     GetDepartments(); 
     JobCodes = new ObservableCollection<string>(); 
     GetJobCodes(); 
    } 

    private void GetJobCodes() 
    { 
     JobCodes = new ObservableCollection<string> { "01-A", "01-B", "02-A", "02-B", "03-A", "03-B" }; 
    } 

    private void GetDepartments() 
    { 
     Departments = new ObservableCollection<Department> { 
      new Department("01"), 
      new Department("02"), 
      new Department("03") 
     }; 
    } 
} 

public class Department 
{ 
    public String DepartmentCode { get; set; } 
    public Department(string departmentCode) { DepartmentCode = departmentCode; } 
} 

public class Time 
{ 
    //time in etc etc 
    public Department Department { get; set; } 
    public string Job { get; set; } 
} 

enter image description here

これはおそらく、あなたが既に持っているものに少なくとも変更である

は、ここでビューモデルです。別のビューモデルルートを使用したい場合は、ViewModelビヘイビアの領域にある "Display"プロパティが既に存在します(モデルではないため、データではありません)。

同様に、ユーザーがジョブコードをクリア/ナリングするなどのように、ユーザーが部門コードを変更したときにアクションを実行することもできます(そうしないと、ジョブコードを設定してから部門コードを変更して構成が無効になります)。

2.中間プロパティを使用してこれを行うこともできますすべてに直接バインドする必要はありません。次のようにViewModelでプロパティを作成できます。

public class TimesViewModel: INotifyPropertyChanged 
{ 
    //notifying property that is bound to ItemsSource in the first Combobox 
    public ObservableCollection<Department> Departments{ get... } 

    //total list of job codes 
    public List<string> JobCodes{ get...} 

    //This is the Department that's bound to SelectedItem in the first ComboBox 
    public Department Department 
    { 
     get 
     { 
      return department; 
     } 
     set 
     { 
      //standard notify like all your other bound properties 
      if (department!= value) 
      { 
       department= value; 
       //when this changes, our selection has changed, so update the second list's ItemsSource 
       DepartmentOnlyJobCodes = JobCodes.Where(jobCode => jobCode.StartsWith(Department.DepartmentCode)).ToList(); 
       //we can also do more complex operations for example, lets clear the JobCode! 
       JobCode = ""; 
       NotifyPropertyChanged("SelectedKey"); 
      } 
     } 
    } 

    //an "intermediatary" Property that's bound to the second Combobox, changes with the first's selection 
    public ObservableCollection<string> DepartmentOnlyJobCodes{ get ... } 

    public string JobCode {get...} 
} 

これらの両方に同じ結果がありますが、最終的には2番目のComboBoxesを何とか保存したリストにバインドします。ロジックはアプリケーションによって変わる可能性があります。例のために辞書を使用しました。

編集:応答あなたが親パネル内のデータコンテキストにバインドして、子要素のプロパティにアクセスすることができ

を編集するには:

<StackPanel> 
    <StackPanel.DataContext> 
     <MultiBinding Converter="{StaticResource DeptCodeToDeptConverter}" > 
      <Binding Path="DepartmentCode" /> 
      <Binding Path="DataContext.Departments" RelativeSource="{RelativeSource Findancestor, AncestorType={x:Type UserControl}}"/> 
     </MultiBinding> 
    </StackPanel.DataContext> 
    <TextBlock Text="{Binding DepartmentCode>"/> 
    <TextBlock Text="{Binding DepartmentName>"/> 
</StackPanel> 

それとも第三れる多を追加することができますあなたの財産を渡し、必要なものを返すためにリフレクションを使用する:

<TextBlock > 
    <TextBlock.Text> 
     <MultiBinding Converter="{StaticResource DeptCodeToDeptConverter}" > 
      <Binding Path="DepartmentCode" /> 
      <Binding Path="DataContext.Departments" RelativeSource="{RelativeSource Findancestor, AncestorType={x:Type UserControl}}"/> 
      <Binding> 
       <Binding.Source> 
        <sys:String>DepartmentCode</sys:String> 
       </Binding.Source> 
      </Binding> 
     </MultiBinding> 
    </TextBlock.Text> 
</TextBlock> 

このタグは、コンバータ論理:

if (values.Length > 2 && values[2] != null) 
{ 
    return typeof(Department).GetProperty(values[2] as string).GetValue(department, null); 
} 
else 
{ 
    //no property string passed - assume they just want the Department object 
    return department; 
} 
+0

ありがとう@ジョー、私はここで少し混乱しています。この物語ではかなり新しい。私のDataGridの各行は、それ自身のビューモデルのインスタンスにバインドする必要がありますか? –

+0

申し訳ありませんが、各行にビューモデルがあるはずです。それは私がそれについて行く方法です。それが単純なデータ表であれば、それはそれを保証するとは思えませんが、そこにはもっと複雑な機能がはっきりとあります。あなたはおそらく行ビューモデルなしでもマルチバインディングコンバータを使用することができます。何行目を期待していますか? – Joe

+0

これを実装した方法を見ると、マルチビューのバインディングはTimeビューのモデルを使用しなくてもうまく動作します。私は明日(9時間ほど)より詳細な例をノックアップします。どのコレクションのゴスを2番目のコンボボックスに定義しているのですか(最初の私が推測した部門コードを使用して) – Joe

関連する問題