我正在尝试创建一个用于创建工作分配列表(AssignmentPlanItem类)的数据网格,该列表具有Employee,Assignment和Workcenter的组合框(AssignmentPlanItem的所有单独的类和外键。该计划直接填充到datagrid我知道如果通过表单完成添加项目可能会更容易,但是我认为这是一种快捷的方法,我不想更改它。
在此问题上工作了许多天后,我已经完成了所有其他工作,但是我还有一个DefaultAssignmentId作为Employee类的属性,并且我希望当选择了雇员时,DefaultAssignment可自动获取到datagrid的assignment字段中。 。这是我的第一个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>
ViewModel:
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}}}"/>
实际更新了视图,但即使我到处都有IsSynchronizedWithCurrentItem=False
,它仍将数据网格中所有行的所有赋值字段更改为与CurrentItem中相同的值。
我的模型类没有实现INotifyPropertyChanged
,并且ViewModelBase
是我从网络上窃取的。
那么,谁能告诉我我做错了什么?
答案 0 :(得分:0)
INotifyPropertyChange
有许多层次需要理解。
分配给列表类型结构时,仅当列表的引用更改时才发出通知;又名新列表已创建。 notify事件不会标记列表中的内容或不在列表中的任何更改,也不会标记列表中可能有属性更改的任何单个项目。
一个可观察的集合会在添加或删除其列表中的项目时发送通知,而不是在列表属性中的单个项目发生更改时发送通知。
如果您希望在更改属性后将其属性反映在数据网格中,则该对象实例必须遵守INotifyPropertyChanged
,并且该属性必须使用其属性名称调用PropertyChanged进行广播。
您最有可能拥有一个不遵守INotifyPropertyChanged
的DTO对象,因此即使在您的Selected...
引用中正确引用了当前实例,包含或显示特定属性值的控件无法知道属性已更改;因为它仅监视该属性名称的更改事件。
在这种情况下,您需要做的是在工作类的基础上创建一个Partial
类,并将INotifyPropertyChanged
添加到局部类中,并通过变更调用为属性提供覆盖(PropertyChanged("FirstName
)(或您的方法调用是什么)),将需要显示其更改。
尽管这并不代表您的直接情况,但这是我的博客文章,它确实显示了人们如何有效使用INotifyPropertyChanged
。它在VM上,但是可以将相同的方法应用于部分DTO对象。
Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding
答案 1 :(得分:0)
好的,我在ΩmegaMan的帮助下工作了。解决方案是让AssignmentPlanItem从ViewModelBase继承(即实现INotifyPropertyChanged),然后从以下位置更改AssignmentId属性:
public AssignmentId {get; set; }
到
private int _assignmentId;
public int AssignmentId
{
get { return _assignmentId; }
set
{
_assignmentId = value;
OnPropertyChanged("AssignmentId");
}
}
datagrid组合框必须具有以下设置(不确定是否还有多余的东西):
<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>
并且ViewModel中的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;
}
}
}
还有一个棘手的部分,即将ComboBox SelectedItem
绑定模式设置为OneWayToSource
。否则,该列中的所有组合框都将获得CurrentItem的分配。因此,据我所知,这意味着ComboBox绑定模式负责处理ViewModel和Model的更新,并且Model上的属性更改通知将其通过SelectedValue
带回到视图。我仍然不确定它是否可以工作或应该像这样工作,但是无论如何,它可以完全按照我想要的方式工作。