C# 如何提高不同模型的属性更改
我有以下问题:我的WPF应用程序使用DataGrid来显示任务列表。这些任务存储在SQL数据库中,我们使用实体框架。为了提高可读性,任务的简化模型位于问题的末尾。任务模型已分配类别对象。所有类别都存储在数据库中,在本问题的末尾还可以看到类别模型 该类别在DataGrid中显示为组合框,因此您可以选择:C# 如何提高不同模型的属性更改,c#,wpf,binding,datagrid,C#,Wpf,Binding,Datagrid,我有以下问题:我的WPF应用程序使用DataGrid来显示任务列表。这些任务存储在SQL数据库中,我们使用实体框架。为了提高可读性,任务的简化模型位于问题的末尾。任务模型已分配类别对象。所有类别都存储在数据库中,在本问题的末尾还可以看到类别模型 该类别在DataGrid中显示为组合框,因此您可以选择: <Window.Resources> <CollectionViewSource x:Key="categoryViewSource" d:DesignSourc
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource" d:DesignSource="{d:DesignInstance {x:Type Models:Category}, CreateList=True}"/>
<CollectionViewSource x:Key="taskViewSource" d:DesignSource="{d:DesignInstance {x:Type Models:Task}, CreateList=True}"/>
</Window.Resources>
<DataGrid DataContext="{StaticResource taskViewSource}" ItemsSource="{Binding}" EnableRowVirtualization="True">
<DataGrid.Columns>
[DataGridTemplateColumn => DataGrid.CellTemplate => DataTemplate and then:]
<ComboBox x:Name="categoryValues"
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource categoryViewSource},
Mode=OneWay}"
SelectedItem="{Binding Category,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
这个很好用。但是,当我编辑类别时,允许编辑类别名称,问题就开始了。ItemsSource会相应地更新,因此在下拉菜单中,该值将以新名称显示。但是,SelectedItem的绑定不会得到更新。这意味着,所有已选择已编辑类别的组合框仍显示旧值
我发现如果我在更改值时这样做:
public void ChangeCategoryName(string name, Category c)
{
c.Name = name;
foreach (var task in c.Tasks)
{
task.Category = null;
task.Category = c;
}
}
然后立即在组合框的SelectedItem中更新该值。我的猜测是,当我更改类别中的值时,不会为任务调用PropertyChanged。我试着强迫大家这样做:
public virtual void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// Force Tasks to update
this.Tasks?.ForEach(t =>
{
t.OnPropertyChanged("Category");
});
}
那没用。有人知道怎么解决这个问题吗
这是我的模型的代码
namespace Application.Models
{
public class Task : INotifyPropertyChanged
{
[Key]
public int TaskId { get; set; }
[...]
// Category is defined in anothre table
public int? CategoryId { get; set; }
[ForeignKey("CategoryId")]
// The Category object will automatically be loaded if access is needed
public virtual Category Category { get; set; }
// PropertyChanged Event
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Category : INotifyPropertyChanged
{
[Key]
public int CategoryId { get; set; }
public string Name { get; set; }
// List of Task objects that are associated to this Category object
public virtual List<Task> Tasks { get; set; }
// PorpertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return Name;
}
}
}
我发现了问题所在。请记住:使用自己的模板时,始终确保正确更新更改。在我的例子中,ComboBoxItem中的绑定依赖于DataContext:
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding}" />
</StackPanel>
</ControlTemplate>
因此,为了订阅PropertyChanged触发器,我显式添加了绑定路径:
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ControlTemplate>
我觉得自己很愚蠢,但我4周前就开始使用WPF了,以前从未使用过,突然间我成了WPF程序员。请原谅我的无礼
编辑
删除了一些不必要的绑定说明。多亏了埃德 我不确定你实际上改变了什么价值,应该产生什么影响,以及你的实体与另一个实体的关系。你能试着澄清你的问题吗?可能至少会显示一个屏幕截图,这样就可以清楚地知道,如果不想显示完整的XAML,哪些控件与之绑定。此外,由于此.Tasks似乎是一个集合,如果希望正确跟踪更改,您可能会对某个集合感兴趣。我猜,当我更改类别中的值时,不会为任务调用PropertyChanged。-正当你没有做任何可能导致这种情况发生的事情,所以它不会发生。如果类别需要在其属性更改时通知UI,则类别必须在每个属性上无例外地引发PropertyChanged,并且其任务必须是ObservableCollection。通常,OnPropertyChanged应该受到保护。通常,任何viewmodel都不应为不同的viewmodel引发PropertyChanged这些是viewmodel,而不是模型。如果您正试图这样做,请返回并确定导致您正试图解决的问题的错误设计决策。@poke我稍后将编辑该问题。我认为XAML已经足够了,我不想把它炸了。基本上,我想更改类别对象的名称,并在所有具有该类别对象的任务对象中引发PropertyChanged事件。@EdPlunkett好的,我现在解决了这个问题。部分要感谢你。我开始修改我所有的代码,以构建一个可工作的最小样本。我非常感谢你的时间和热情。我的问题可能非常愚蠢,我道歉。我还是新手,顺便说一句,你需要的是{Binding Name}-在这种特殊情况下,所有的UpdateSourceTrigger等标志都不是ops。
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding}" />
</StackPanel>
</ControlTemplate>
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ControlTemplate>