簡體   English   中英

使用 MVVM 在 DataGrid 中級聯組合框

[英]Cascading ComboBox in DataGrid with MVVM

我的目標是在 WPF 中擁有一組級聯組合框。 我正在嘗試使用 MVVM 模型,但仍在學習。

關於該項目的一些背景信息。 我正在嘗試為員工編輯時間。

所以我有一個 DataGrid 中所選員工的時間列表。 DataGrid 中的每一行都是一個 Time 對象。 時間由一些字段 InTime、OutTime、Date、Hours... 等組成。 時間也有部門和工作。

目前,我已將部門組合框連接起來並正常工作,但我不確定如何根據部門字段中選擇的內容構建作業組合框。

這是我的 ViewModel 的設置方式

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);
    }
}

這是我的 DataGrid 上的綁定

<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>

那么我如何實現我的工作組合框以根據為該行中的部門選擇的任何內容填充其項目?

我假設我想把這個代碼放在我的同一個視圖模型中。

任何幫助表示贊賞,謝謝!

編輯(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 或 Department.Location 該怎么辦。 我是否必須創建一個新的轉換器來返回我想在不同控件中使用的每個字段? 或者有沒有辦法使用這種方法實現我想要的?

我會選擇兩種方法之一來做到這一點:

1. 使用多綁定轉換器。 您的第一個組合框的 ItemsSource 將綁定到它的集合。 第二個可以在第一個的 SelectedItem 和第二個組合框的一些可用項目集集合上使用多綁定轉換器,以返回第二個 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; }
}

這產生了這個:

在此處輸入圖片說明

這可能是對您已有的更改最少的。 如果你想走單獨的視圖模型路線,這可能是有利的(你已經有一個“顯示”屬性,它在 ViewModel 行為領域,因為它不是數據,不應該在你的模型中。

同樣,您可能希望在用戶更改部門代碼時采取措施,例如清除/清空職務代碼(否則,他們可以設置職務代碼,然后更改部門代碼,並且配置無效)。 邏輯越來越復雜,可能更適合 TimesViewModel。

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...}
}

它們都有相同的結果,您最終會將您的第二個 ComboBox 綁定到您以某種方式存儲的列表。 邏輯可以根據您的應用程序而改變,我只是以字典為例。

編輯:響應編輯

您可以綁定到父面板中的數據上下文,並訪問子元素中的屬性:

<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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM