我有以下问题:我的WPF应用程序使用DataGrid显示任务列表。这些任务存储在SQL数据库中,我们使用实体框架。缩短的任务模型位于此问题的最后,以提高可读性。任务模型已分配Category对象。所有类别都存储在数据库中,类别模型也可以在这个问题的最后看到。
类别在DataGrid中显示为ComboBox,因此您可以选择:
<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>
ViewSources在Code中设置,例如:
await db.Categories.LoadAsync();
categoryViewSource.Source = db.Categories.Local;
这很好用。但是,当我编辑类别(允许编辑类别名称)时,问题就开始了。 ItemsSource相应地更新(因此在下拉菜单中,值显示为新名称)。但是,SelectedItem的Binding不会更新。这意味着所有已经编辑过的类别的组合框仍然显示旧值。
我发现如果我在更改值时执行此操作:
public void ChangeCategoryName(string name, Category c)
{
c.Name = name;
foreach (var task in c.Tasks)
{
task.Category = null;
task.Category = c;
}
}
然后立即在ComboBox的SelectedItem中更新该值。我的 guess 是我更改Task
中的值时,不会为Category
调用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;
}
}
}
修改
我确定了问题的根源。首先,我在评论中添加了Ed建议的DisplayMemberPath="Name"
。它没有帮助。在最小化示例性应用程序直到找到源代码之后,样式I应用于ComboBox似乎是问题的根源。该样式旨在在DropDown中向ComboBox项添加“删除”和“编辑”按钮,但在不选择项时显示简单的TextBox。看看自己:
<!--Templates for different Item Styles in ComboBoxes-->
<!--This template is a simple TextBox-->
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" />
</StackPanel>
</ControlTemplate>
<!--This template contains a remove button and is only shown in the DropDown Menu-->
<ControlTemplate x:Key="ExtendedComboBoxItem" >
<DockPanel HorizontalAlignment="Stretch">
<TextBlock Text="{Binding UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}" />
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Style="{StaticResource ComboBoxRemoveButton}"
x:Name="EditItemFromComboBoxButton" Click="EditItemFromComboBoxButton_Click"
Visibility="{Binding Converter={StaticResource RemoveXFromNullValuesConverter}}">
<iconPacks:PackIconMaterial Kind="Pencil" VerticalAlignment="Center" HorizontalAlignment="Center" Height="8" Width="8"/>
</Button>
<Button Style="{StaticResource ComboBoxRemoveButton}"
x:Name="RemoveItemFromComboBoxButton" Click="RemoveItemFromComboBoxButton_Click"
Visibility="{Binding Converter={StaticResource RemoveXFromNullValuesConverter}}">
<iconPacks:PackIconMaterial Kind="Close" VerticalAlignment="Center" HorizontalAlignment="Center" Height="8" Width="8"/>
</Button>
</StackPanel>
</DockPanel>
</ControlTemplate>
<!--Style the ComboBox Items so they can align the Remov "X" button to the right-->
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}" x:Key="RemovableComboboxItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
<!--This is the DateTemplate for the ComboBox ItemTemplate-->
<DataTemplate x:Key="RemovableComboBoxItemTemplate">
<Control x:Name="RemoveableItemsComboBoxControl" Focusable="False" Template="{StaticResource ExtendedComboBoxItem}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
<Setter TargetName="RemoveableItemsComboBoxControl" Property="Template" Value="{StaticResource SimpleComboBoxItem}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<!--ComboBox Style for ComboBoxes with a X for removing the item-->
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}" x:Key="RemoveItemComboBox">
<Setter Property="ItemContainerStyle" Value="{StaticResource RemovableComboboxItem}"/>
<Setter Property="ItemTemplate" Value="{StaticResource RemovableComboBoxItemTemplate}"/>
</Style>
答案 0 :(得分:1)
我发现了什么问题。请记住:使用自己的模板时,请务必确保正确更新更改。在我的例子中,我的ComboBoxItem中的Binding依赖于DataContext:
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding}" />
</StackPanel>
</ControlTemplate>
因此,为了订阅PropertyChanged Trigger,我明确地添加了绑定路径:
<ControlTemplate x:Key="SimpleComboBoxItem">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</ControlTemplate>
我觉得很愚蠢,但是我在4周前开始使用WPF,之前从未使用过它,突然间我成了WPF程序员。请原谅我的无礼。
修改强>
删除了一些不必要的绑定指令。感谢Ed!