c# - 在 DataTemplate 中使用时,行为 DependencyProperty 不更新 ViewModel
问题描述
我有一个DependencyProperty
in aBehavior
我正在设置 in 的值OnAttached()
。
然后我DependencyProperty
用Mode
.OneWayToSource
OneWayToSource
由于某种原因,绑定的视图模型属性在 a 中完成时不会被绑定更新DataTemplate
(视图模型的设置器永远不会被调用)。在其他情况下,它似乎工作正常。
我没有收到任何绑定错误,也看不到任何异常的迹象,等等,我不知道我做错了什么。
WPF 设计器确实显示了一些错误,根据您查看的位置,声明为The member "TestPropertyValue" is not recognized or is not accessible
或。The property "TestPropertyValue was not found in type 'TestBehavior'
我不确定这些是否是“真正的”错误(正如我所观察到的那样,WPF 设计器似乎并不完全可靠地始终显示真正的问题),如果是,它们是否与此问题或其他问题完全相关.
如果这些设计器错误确实与此问题有关,我只能假设我必须DependencyProperty
错误地声明了。如果是这种情况,我无法看到错误在哪里。
我制作了一个复制问题的示例项目。下面的代码应该足够了,并且可以添加到任何名为 的新 WPF 项目中WpfBehaviorDependencyPropertyIssue001
。
主窗口.xaml
<Window x:Class="WpfBehaviorDependencyPropertyIssue001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="clr-namespace:WpfBehaviorDependencyPropertyIssue001.Behaviors"
xmlns:vm="clr-namespace:WpfBehaviorDependencyPropertyIssue001.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<StackPanel>
<Label Content="{Binding TestPropertyValue, ElementName=OuterTestA}" Background="Cyan">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="OuterTestA" TestPropertyValue="{Binding MainTestValueA, Mode=OneWayToSource}" />
</b:Interaction.Behaviors>
</Label>
<Label Content="{Binding MainTestValueA, Mode=OneWay}" Background="Orange" />
<Label Content="{Binding MainTestValueB, Mode=OneWay}" Background="MediumPurple" />
<DataGrid ItemsSource="{Binding Items}" RowDetailsVisibilityMode="Visible">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="OuterTestB" TestPropertyValue="{Binding MainTestValueB, Mode=OneWayToSource}" />
</b:Interaction.Behaviors>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding TestPropertyValue, ElementName=InnerTest}" Background="Cyan">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="InnerTest" TestPropertyValue="{Binding ItemTestViewModelValue, Mode=OneWayToSource}" />
</b:Interaction.Behaviors>
</Label>
<Label Content="{Binding ItemTestViewModelValue, Mode=OneWay}" Background="Lime" />
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</StackPanel>
</Window>
测试行为.cs
using Microsoft.Xaml.Behaviors;
using System.Windows;
namespace WpfBehaviorDependencyPropertyIssue001.Behaviors
{
public class TestBehavior : Behavior<UIElement>
{
public static DependencyProperty TestPropertyValueProperty { get; } = DependencyProperty.Register("TestPropertyValue", typeof(string), typeof(TestBehavior));
// Remember, these two are just for the XAML designer (or I guess if we manually invoked them for some reason).
public static string GetTestPropertyValue(DependencyObject dependencyObject) => (string)dependencyObject.GetValue(TestPropertyValueProperty);
public static void SetTestPropertyValue(DependencyObject dependencyObject, string value) => dependencyObject.SetValue(TestPropertyValueProperty, value);
protected override void OnAttached()
{
base.OnAttached();
SetValue(TestPropertyValueProperty, "Example");
}
}
}
ViewModelBase.cs
using System.ComponentModel;
namespace WpfBehaviorDependencyPropertyIssue001.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
主视图模型.cs
using System.Collections.ObjectModel;
namespace WpfBehaviorDependencyPropertyIssue001.ViewModels
{
public class MainViewModel : ViewModelBase
{
public ObservableCollection<ItemViewModel> Items
{
get => _Items;
set
{
_Items = value;
OnPropertyChanged(nameof(Items));
}
}
private ObservableCollection<ItemViewModel> _Items;
public MainViewModel()
{
Items = new ObservableCollection<ItemViewModel>()
{
new ItemViewModel() { ItemName="Item 1" }
};
}
public string MainTestValueA
{
get => _MainTestValueA;
set
{
System.Diagnostics.Debug.WriteLine($"Setting {nameof(MainTestValueA)} to {(value != null ? $"\"{value}\"" : "null")}");
_MainTestValueA = value;
OnPropertyChanged(nameof(MainTestValueA));
}
}
private string _MainTestValueA;
public string MainTestValueB
{
get => _MainTestValueB;
set
{
System.Diagnostics.Debug.WriteLine($"Setting {nameof(MainTestValueB)} to {(value != null ? $"\"{value}\"" : "null")}");
_MainTestValueB = value;
OnPropertyChanged(nameof(MainTestValueB));
}
}
private string _MainTestValueB;
}
}
项目视图模型.cs
namespace WpfBehaviorDependencyPropertyIssue001.ViewModels
{
public class ItemViewModel : ViewModelBase
{
public string ItemName
{
get => _ItemName;
set
{
_ItemName = value;
OnPropertyChanged(nameof(ItemName));
}
}
private string _ItemName;
public string ItemTestViewModelValue
{
get => _ItemTestViewModelValue;
set
{
System.Diagnostics.Debug.WriteLine($"Setting {nameof(ItemTestViewModelValue)} to {(value != null ? $"\"{value}\"" : "null")}");
_ItemTestViewModelValue = value;
OnPropertyChanged(nameof(ItemTestViewModelValue));
}
}
private string _ItemTestViewModelValue;
}
}
预期的调试输出消息(不包括标准 WPF 消息):
Setting MainTestValueA to null
Setting MainTestValueA to "Example"
Setting MainTestValueB to null
Setting MainTestValueB to "Example"
Setting ItemTestViewModelValue to null
Setting ItemTestViewModelValue to "Example"
实际调试输出消息(不包括标准 WPF 消息):
Setting MainTestValueA to null
Setting MainTestValueA to "Example"
Setting MainTestValueB to null
Setting MainTestValueB to "Example"
Setting ItemTestViewModelValue to null
解决方案
我已经设法解决了这个问题。
出于某种原因,看起来a 中的绑定需要一个UpdateSourceTrigger
of ,该绑定具有一个of 。这样做会导致正确更新视图模型属性。PropertyChanged
DataTemplate
Mode
OneWayToSource
我通过实验发现了这一点,但我不确定为什么这种行为不同于DataTemplate
.
如果我能找到这种行为的原因(记录与否),我将使用该信息更新此答案。
附加信息
为了让未来的读者更清楚,带有OneWayToSource
装订外的标签DataTemplate
按预期工作。用于此的 XAML(来自原始问题)如下所示:
<Label Content="{Binding TestPropertyValue, ElementName=OuterTestA}" Background="Cyan">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="OuterTestA" TestPropertyValue="{Binding MainTestValueA, Mode=OneWayToSource}" />
</b:Interaction.Behaviors>
</Label>
然而,TestBehavior
与内的OneWayToSource
绑定并没有奏效。用于此的 XAML(来自原始问题)如下所示:DataTemplate
<DataTemplate>
<StackPanel>
<Label Content="{Binding TestPropertyValue, ElementName=InnerTest}" Background="Cyan">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="InnerTest" TestPropertyValue="{Binding ItemTestViewModelValue, Mode=OneWayToSource}" />
</b:Interaction.Behaviors>
</Label>
<Label Content="{Binding ItemTestViewModelValue, Mode=OneWay}" Background="Lime" />
</StackPanel>
</DataTemplate>
添加UpdateSourceTrigger=PropertyChanged
到TestBehavior
绑定导致视图模型属性被正确更新。更新后的 XAML 如下所示:
<DataTemplate>
<StackPanel>
<Label Content="{Binding TestPropertyValue, ElementName=InnerTest}" Background="Cyan">
<b:Interaction.Behaviors>
<tb:TestBehavior x:Name="InnerTest" TestPropertyValue="{Binding ItemTestViewModelValue, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
</b:Interaction.Behaviors>
</Label>
<Label Content="{Binding ItemTestViewModelValue, Mode=OneWay}" Background="Lime" />
</StackPanel>
</DataTemplate>
推荐阅读
- android - 何时在 Gradle 插件中应用自定义 Android 扩展?
- laravel - Laravel插入数据库,里面有循环
- r - 如何使用 dplyr 在 3 天的时间间隔内找到中位数?
- javascript - 如何验证文本框中的 Javascript 注入
- r - R:获取每个条目在 data.table 中的先前条目
- angular - Angular - 显示来自 TMDB api 的字段
- javascript - 点 ?正则表达式字符串中的字符文字
- android - 如何在 Android Q 上隐藏状态栏?
- python - 使用 O365 库获取未读 Outlook 邮件
- azure - Azure 表存储:为现有表填充新列的脚本