我有一个困扰我的绑定。根据值转换器,提供给我的控件的值是有效的且不为空。但最后的
DependencyProperty
总是收到空值。然而,我在输出窗口中没有收到任何绑定警告、错误或任何类型的异常。 (我确实启用了 WPF 日志记录)
我不知道有什么办法可以在
IValueConverter
阶段之后的任何阶段查看我向控件提供的内容。不知道从这里去哪里。
下面是
DependencyProperty
(类本身被缩写为只显示相关属性,因为它很大。我只是想了解这个绑定的问题。
所有其他属性都完美结合)
public class LineControl : Control
{
static LineControl()
{
DefaultStyleKeyProperty.OverrideMetadata(
typeof(LineControl), ew FrameworkPropertyMetadata(typeof(LineControl)));
}
public LineControl() { }
public static readonly DependencyProperty RegionsProperty = DependencyProperty.Register(
nameof(Regions), typeof(IEnumerable<ShapeRegion>), typeof(LineControl),
new FrameworkPropertyMetadata(Enumerable.Empty<ShapeRegion>(), OnRegionsChanged));
public IEnumerable<ShapeRegion> Regions
{
get => (IEnumerable<ShapeRegion>)GetValue(RegionsProperty);
set => SetValue(RegionsProperty, value);
}
private static void OnRegionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Sanity Check. I keep getting NULL for the new value here.
if (e.NewValue is not IEnumerable<ShapeRegion>)
Debug.WriteLine("NULL REGIONS"); // I only get this
else
Debug.WriteLine("VALID REGIONS"); // Not this.
}
}
视图模型
LineShape
是一个相当普通的类,具有绘制线条的属性。精简版
public class LineShape : ObservableObject
{
private Point _p1;
private Point _p2;
private readonly ObservableCollection<ShapeRegion> _regions = new();
public IEnumerable<ShapeRegion> Regions => _regions;
public Point P1 { get => _p1; set => SetProperty(ref _p1, value); }
public Point P2 { get => _p2; set => SetProperty(ref _p2, value); }
}
这是我绑定的方式
<DataTemplate DataType="{x:Type core:LineShape}">
<ctrl:LineControl P1="{Binding P1}" P2="{Binding P2}"
Regions="{Binding Regions}" />
</DataTemplate>
正如我所提到的,为了尝试诊断此问题,我尝试在绑定中插入一个不执行任何操作的
IValueConverter
。它记录所提供对象的类型,然后返回给定值不变。像这样
public class LogValueConverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object? v, Type tt, object p, CultureInfo c)
{
Debug.WriteLine($"Type = {(v?.GetType() ?? "NULL")}");
return v;
}
public object? ConvertBack(object? v, Type tt, object? p, CultureInfo c) => return v;
}
然后我像这样编辑我的
DataTemplate
Regions="{Binding Regions, Converter={core:LogValueConverter}}"
但每次,此转换器都会记录到输出窗口,表明我正在向绑定提供
ObservableCollection<ShapeRegion>
类型的有效对象。
Type=System.Collections.ObjectModel.ObservableCollection`1[Core.Shapes.ShapeRegion]
然而,我的下一个日志语句(在
LineControl.Regions
的属性更改回调中)每次都会告诉我控件收到了 null。我也用调试器中的断点验证了两者。
我一定错过了一些非常明显的东西,但我不知道它是什么。知道出了什么问题吗?
编辑添加:我无法在简单的示例中重现此问题。这正是我的问题:我过去做过数百次这样的绑定,没有任何问题。但这个尤其表现出我无法理解的行为。我在这里希望有人知道一种方法来更好地诊断单个绑定失败的原因。或者关于可能导致此问题的原因的建议可能会帮助我进一步调查。因为现在我没办法了。
我终于学会了如何让绑定提供非空值,但
DependencyProperty
仍然接收 null:*当绑定类型为 IEnumerable 并且类型 T 实际上是两种不同的类型时,可能会发生这种情况,因为命名空间冲突。编译器无法捕获它,WPF 也无法发出警告。
在我的例子中,属性的类型是
IEnumerable<ShapeRegion>
问题是两个独立的命名空间中有两个完全独立的类型,名为 ShapeRegion
Sdk.ShapeRegion
——一个结构体Core.Shapes.ShapeRegion
——一堂课视图模型属性正在提供
IEnumerable<Sdk.ShapeRegion>
,但需要控制 DependencyProperty
。
IEnumerable<Core.Shapes.ShapeRegion>
通常我会期望这样一个不正确的绑定会导致构建错误。或者至少是一个 WPF XAML 绑定警告。但我两者都没有。当我将自己的
IValueConverter.Convert
函数放入WPF代码中时,我才发现它。