如何解决在 DataTemplate 中使用时,行为 DependencyProperty 不会更新 ViewModel 附加信息
我在 DependencyProperty
中有一个 Behavior
,我正在为 OnAttached()
设置值。
然后我使用 DependencyProperty
的 Mode
将视图模型属性绑定到此 OneWayToSource
。
由于某种原因,OneWayToSource
绑定在 DataTemplate
内完成时不会更新绑定的视图模型属性(视图模型的 setter 永远不会被调用)。在其他情况下,它似乎工作正常。
我没有收到任何绑定错误,也看不到任何异常等迹象,我不知道我做错了什么。
WPF 设计器确实会显示一些错误,声称是 The member "TestPropertyValue" is not recognized or is not accessible
或 The property "TestPropertyValue was not found in type 'TestBehavior'
,具体取决于您查看的位置。我不确定这些是否是“真正的”错误(正如我观察到的,WPF 设计器在始终显示真正的问题方面似乎并不完全可靠),如果是,它们是否与此问题或其他问题完全相关.
如果这些设计器错误确实与这个问题有关,我只能假设我一定是错误地声明了 DependencyProperty
。如果是这种情况,我将无法看到错误在哪里。
我制作了一个复制该问题的示例项目。以下代码应该足够了,可以添加到任何名为 WpfBehaviorDependencyPropertyIssue001
的新 WPF 项目中。
主窗口.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>
TestBehavior.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));
}
}
}
MainViewModel.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;
}
}
ItemViewModel.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
解决方法
我完全测试了你的代码,它运行良好。
您的调试运行良好,因为在创建 MainViewModel
的实例时会立即调用所有成员。
MainTestValueA
以 null 值调用,然后调用 OnPropertyChanged
并使用 bind
属性调用标签控件的 TestPropertyValue
和 {{ 1}} 方法初始化 OnAttached
并将其打印在输出上。
example
的相同步骤
对 MainTestValueB
重复相同的步骤,但因为它位于 ItemTestViewModelValue
内,DataGridView
不允许从 View 访问。
当然,这是我的结论。
,我已经设法解决了这个问题。
出于某种原因,UpdateSourceTrigger
中的 PropertyChanged
为 DataTemplate
的绑定似乎需要 Mode
为 OneWayToSource
。
这样做会导致正确更新视图模型属性。
我通过实验发现了这一点,我不确定为什么这种行为与在 DataTemplate
之外完成的绑定不同,尽管这种行为可能记录在某处。
如果我能找到这种行为的原因(记录与否),我将使用该信息更新此答案。
附加信息
为了让未来的读者更清楚,带有 OneWayToSource
绑定 outside 的标签按预期工作。用于此的 XAML(来自原始问题)如下所示:
DataTemplate
但是, <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
绑定会导致视图模型属性正确更新。更新后的 XAML 如下所示:
TestBehavior
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。