gpt4 book ai didi

c# - 使用 MVVM 在 DataGrid 中级联 ComboBox

转载 作者:行者123 更新时间:2023-11-30 16:03:24 28 4
gpt4 key购买 nike

我的目标是在 WPF 中拥有一组级联组合框。我正在尝试使用 MVVM 模型,但仍在学习中。

项目的一些背景信息。我正在尝试为员工编辑时间。

所以我在 DataGrid 中有一个所选员工的时间列表。 DataGrid 中的每一行都是一个时间对象。时间由一些字段 InTime、OutTime、Date、Hours 等组成。 A Time 也有 Department 和 Job。

目前,我已经连接好 Department ComboBox 并正常工作,但我不确定如何根据在 Department 字段中选择的内容构建 Job 组合框。

这是我的 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 上的绑定(bind)

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

那么我如何实现我的工作组合框,以根据为该行中的部门选择的内容填充其项目?

我假设我想将此代码放在我的同一 View 模型中。

感谢任何帮助,谢谢!

编辑(2016 年 4 月 5 日):

如何返回带有转换器的对象,以便我可以使用该转换器将不同的东西绑定(bind)到该对象的字段。

说这是我的转换器

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

这是我的绑定(bind)

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

该转换器将返回一个 Department 对象,但是如果我希望 TextBlock 的文本是 Department.Name 或 Department.Location 怎么办?我是否必须创建一个新的转换器来返回我想在不同控件中使用的每个字段?或者有没有办法使用这种方法实现我想要的?

最佳答案

我会选择以下两种方法之一:

<强>1。使用多重绑定(bind)转换器。您的第一个组合框的 ItemsSource 将绑定(bind)到它的事物集合。第二个可以在第一个的 SelectedItem 和第二个组合框的一些可用项目集集合上使用多绑定(bind)转换器,以返回第二个 ItemsSource 的集合。

当第一个组合框更改它的选定项时,绑定(bind)将更新:

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 绑定(bind)为第一个值,将集合字典绑定(bind)为第二个值:

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

将多重绑定(bind)转换器作为静态资源:

这是 View 模型:

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

这可能是对现有内容的最小更改。如果你想走单独的 View 模型路线,这可能是有利的(你已经有一个“显示”属性,它在 ViewModel 行为的领域,因为它不是数据,不应该在你的模型中。

同样,您可能希望在用户更改部门代码时采取措施,例如清除/清空职位代码(否则,他们可以设置职位代码,然后更改部门代码并进行无效配置)。逻辑变得越来越复杂,可能更适合 TimesViewModel。

<强>2。您也可以使用中间属性执行此操作您不必直接绑定(bind)到所有内容,您可以像这样在 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...}
}

它们都有相同的结果,您最终会将第二个 ComboBoxes 绑定(bind)到您以某种方式存储的列表。逻辑可能会根据您的应用程序而改变,我只是使用字典作为示例。

编辑:对编辑的回应

您可以绑定(bind)到父面板中的数据上下文,并访问子元素中的属性:

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

或者您可以添加第三个多重绑定(bind)来传递您的属性并使用反射返回您需要的内容:

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

关于c# - 使用 MVVM 在 DataGrid 中级联 ComboBox,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36412159/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com