WPF MVVM 数据网格在另一个属性的更改时更新属性



我正在尝试创建一个数据网格来创建一个工作分配列表(AssignmentPlanItem 类(,该列表具有员工、分配和工作中心的组合框(所有单独的类和 AssignmentPlanItem 的外键。计划将直接填充到数据网格。我知道如果通过表单添加项目可能会更容易,但我认为这是一种活泼的方法,我不想更改它。

在这个问题上经过很多天,我已经让其他一切都工作了,但我还有一个 DefaultAssignmentId 作为 Employee 类的属性,我希望在选择员工时自动将 DefaultAssignment 提取到数据网格的分配字段。这是我的第一个 WPF 应用程序,因此我的代码可能只能通过某种神奇的机会工作,因此请随时提供一般提示。我觉得我已经尝试了绑定的所有可能组合,所以现在我必须寻求帮助,因为我在 Google 上找不到任何东西。

XAML:

<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<DataGrid x:Name="assignmentPlanItemsDataGrid" Margin="0,3,0,0" ItemsSource="{Binding DataGridRows, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding CurrentItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" IsSynchronizedWithCurrentItem="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Employee" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path = DataContext.EmployeeComboRows, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"                                         
SelectedItem="{Binding DataContext.SelectedEmployee,Mode=TwoWay, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedValue="{Binding EmployeeId, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Id"
IsEditable="True"
DisplayMemberPath="FullName">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Assignment" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.AssignmentComboRows, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedItem="{Binding DataContext.SelectedAssignment,Mode=TwoWay, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedValuePath="Id"
DisplayMemberPath="Description"
SelectedValue="{Binding AssignmentId, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
IsEditable="True"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>        
</Grid>

视图模型:

public class AssignmentPlanItemViewModel : ViewModelBase
{
DataContext context = new DataContext();
//the datagrid collection
private ObservableCollection<AssignmentPlanItem> _dataGridRows = new ObservableCollection<AssignmentPlanItem>();
//datagrid selected item
private AssignmentPlanItem _currentItem;
//combobox itemssource collections
public ObservableCollection<Employee> EmployeeComboRows { get; set; }
public ObservableCollection<Assignment> AssignmentComboRows { get; set; }
public ObservableCollection<WorkStation> WorkStationComboRows { get; set; }
//the source event for the current assignment plan
public Event CurrentEvent;
public AssignmentPlanItemViewModel()
{
//populate combobox collections
EmployeeComboRows = new ObservableCollection<Employee>(context.Employees);
AssignmentComboRows = new ObservableCollection<Assignment>(context.Assignments);
WorkStationComboRows = new ObservableCollection<WorkStation>(context.WorkStations);
//getting the current event (yes, non-MVVM, I know)
CurrentEvent = context.Events.Find(AssignmentPlanWindow.eventId);
var planItems = CurrentEvent.AssignmentPlans.Last().AssignmentPlanItems;
DataGridRows = new ObservableCollection<AssignmentPlanItem>(planItems);
}
public AssignmentPlanItem CurrentItem
{
get { return _currentItem; }
set
{
if (value != _currentItem)
{
_currentItem = value;
OnPropertyChanged("CurrentItem");
OnPropertyChanged("DataGridRows");
}
}
}
public ObservableCollection<AssignmentPlanItem> DataGridRows
{
get { return _dataGridRows; }
set
{
_dataGridRows = value;
OnPropertyChanged("DataGridRows");
}
}
private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get
{
return _selectedEmployee;
}
set
{
if (CurrentItem != null)
{
_selectedEmployee = value;
if (_selectedEmployee != null)
{
CurrentItem.EmployeeId = _selectedEmployee.Id;
var defaultAssigment = context.Assignments.Find((int)_selectedEmployee.DefaultAssignmentId);
CurrentItem.Assignment = defaultAssigment;
CurrentItem.AssignmentId = (int)_selectedEmployee.DefaultAssignmentId;
OnPropertyChanged("CurrentItem");
}
}
}
}
private Assignment _selectedAssignment;
public Assignment SelectedAssignment
{
get
{
return _selectedAssignment;
}
set
{
if (CurrentItem != null)
{
_selectedAssignment = value;
if (_selectedAssignment != null)
{
CurrentItem.AssignmentId = _selectedAssignment.Id;
CurrentItem.Assignment = _selectedAssignment;
OnPropertyChanged("CurrentItem");
}
}
}
}
}

因此,我使用 SelectedEmployee 和 SelectedAssignment 属性来尝试更改数据网格的选定项 (CurrentItem(。项目已更改,但更改不会更新到网格。当我保存网格,关闭并返回时,分配也发生了变化。

在我尝试的 XAML 分配组合框中

<SelectedValue="{Binding DataContext.CurrentItem.AssignmentId, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"/> 

它实际上更新了视图,但它将 datagrid 中所有行的所有分配字段更改为与 CurrentItem 中相同的值,即使我到处都有IsSynchronizedWithCurrentItem=False

我的模型类没有实现INotifyPropertyChanged和我从网上翻录的ViewModelBase

那么,谁能告诉我我做错了什么?

好的,我在ΩmegaMan的帮助下让它工作。解决方案是让 AssignmentPlanItem 从 ViewModelBase 继承(即实现 INotifyPropertyChanged(,并将 AssignmentId 属性从

public AssignmentId {get; set; }

private int _assignmentId;
public int AssignmentId
{
get { return _assignmentId; }
set
{
_assignmentId = value;
OnPropertyChanged("AssignmentId");
}
}

数据网格组合框必须具有以下设置(不太确定是否有多余的内容(:

<DataGrid x:Name="assignmentPlanItemsDataGrid" Margin="0,3,0,0" ItemsSource="{Binding DataGridRows, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" CanUserAddRows="False" SelectedItem="{Binding CurrentItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Employee" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path = DataContext.EmployeeComboRows, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"                                         
SelectedItem="{Binding DataContext.SelectedEmployee,Mode=OneWayToSource, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedValue="{Binding EmployeeId}"
SelectedValuePath="Id"
IsEditable="True"
DisplayMemberPath="FullName"
IsSynchronizedWithCurrentItem="False">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Assignment" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.AssignmentComboRows, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedItem="{Binding DataContext.SelectedAssignment,Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Findancestor, AncestorType={x:Type Window}}}"
SelectedValuePath="Id"
DisplayMemberPath="Description"
SelectedValue="{Binding AssignmentId}"
IsEditable="True"
IsSynchronizedWithCurrentItem="False"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

视图模型中的SelectedEmployee具有以下代码来更改分配:

private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get
{
return _selectedEmployee;
}
set
{
if (CurrentItem!= null)
{
_selectedEmployee = value;
OnPropertyChanged("SelectedEmployee");
if (SelectedEmployee != null)
{
CurrentItem.EmployeeId = SelectedEmployee.Id;
var defaultAssigment = Context.Assignments.Find((int)SelectedEmployee.DefaultAssignmentId);
CurrentItem.AssignmentId = (int)SelectedEmployee.DefaultAssignmentId;
CurrentItem.Assignment = defaultAssigment;
}
}
}

还有一个棘手的部分,即将组合框SelectedItem绑定模式设置为OneWayToSource。如果没有这个,列中的所有组合框都将获得当前项的分配。因此,据我微弱的理解,这意味着ComboBox绑定模式处理对视图模型和模型的更新,并且模型上的属性更改通知通过SelectedValue将其带回视图。我仍然不确定它是否有效或应该像这样工作,但无论如何它完全按照我想要的方式运行。

需要了解许多级别的INotifyPropertyChange

分配给列表类型结构时,通知仅适用于对列表的引用发生更改;也就是已创建新列表。通知事件不标记对列表中的内容或不包含的内容的任何更改,也不标记列表中可能具有属性更改的任何单个项。

可观察集合会在添加或删除其列表中的项时发送通知,但在列表属性中的单个项发生更改时不会发送通知。


如果希望项的属性在更改其属性后反映在数据网格中,则该对象实例必须遵守INotifyPropertyChanged并且该属性必须使用要广播的属性名称调用 PropertyChanged。

您最有可能拥有的是一个不遵守INotifyPropertyChanged的 DTO 对象,因此即使在Selected...引用中正确引用了当前实例,包含/显示特定属性值的控件也无法知道该属性已更改;因为它只监视该属性名称的更改事件。


在这种情况下,您需要做的是从工作类中创建一个Partial类,并将INotifyPropertyChanged添加到部分类中,并使用更改调用(PropertyChanged("FirstName('(或任何方法调用(为属性提供覆盖,这将需要显示它们的更改。


虽然这并不能说明您的直接情况,但这是我的博客文章,确实展示了如何有效地使用INotifyPropertyChanged。它在 VM 上,但可以将相同的方法应用于部分 DTO 对象。

Xaml:视图模型主页实例化和加载策略,便于绑定

相关内容

  • 没有找到相关文章

最新更新