【发布时间】:2021-09-20 03:43:23
【问题描述】:
我在Behavior 中有一个DependencyProperty,我正在为OnAttached() 设置值。
然后我将视图模型属性绑定到这个DependencyProperty,其中Mode 是OneWayToSource。
由于某种原因,绑定的视图模型属性在DataTemplate 内完成时不会被OneWayToSource 绑定更新(永远不会调用视图模型的设置器)。在其他情况下,它似乎工作正常。
我没有收到任何绑定错误,也看不到任何异常等迹象,我不知道我做错了什么。
WPF 设计器确实显示了一些错误,声称 The member "TestPropertyValue" is not recognized or is not accessible 或 The property "TestPropertyValue was not found in type 'TestBehavior',具体取决于您查看的位置。我不确定这些是否是“真正的”错误(正如我所观察到的那样,WPF 设计器似乎并不完全可靠地始终显示真正的问题),如果是,它们是否与此问题或其他问题完全相关.
如果这些设计器错误确实与此问题有关,我只能假设我必须错误地声明了 DependencyProperty。如果是这样的话,我看不出错误在哪里。
我制作了一个复制问题的示例项目。以下代码应该足够了,并且可以添加到名称为 WpfBehaviorDependencyPropertyIssue001 的任何新 WPF 项目中。
MainWindow.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
【问题讨论】:
-
我认为附加的道具应该使用“RegisterAttached”注册。同样在 ctor 中,您将值“Example”设置为 TestBehaviour 对象本身的实例,而在静态 Get/Set 方法中,您从传入的依赖对象获取/设置值,它们是控件/标签你的xml。所以这些值被映射到不同的目标......
-
@lidqy
TestPropertyValue不是附加属性。它是TestBehavior的依赖属性。标签本身的绑定是为了证明问题。要关注的关键点是TestBehavior元素本身的绑定。此外,此示例中未使用行为的AssociatedObject,这意味着依赖属性值不与标签一起存储。 -
@lidqy 另外,这不是作为附加属性开始的原因是因为这最终将用于我 am 使用的更大项目中
AssociatedObject为Behavior。在重现我遇到的问题时,这些功能是多余的。最后,“Example”的值不是在构造函数中设置的,而是在行为的OnAttached()方法中设置的。
标签: c# wpf data-binding dependency-properties behavior