多绑定时随机DependencyProperty.UnsetValue
Random DependencyProperty.UnsetValue when multibinding
我遇到了一个奇怪的问题。
我在 observablecollection 中得到了一组 spaceObjects(行星)。这些对象在屏幕上移动,因此它们的 X 轴和 Y 轴一直在变化。这是它的 xaml 代码:
<DataTemplate DataType="{x:Type spaceObjects:SpaceObject}">
<Path Fill="{Binding Color, Converter={StaticResource ColorConverter}}"
Stroke="{Binding Border, Converter={StaticResource ColorConverter}}"
StrokeThickness="{Binding BorderThickness}" Tag="{Binding Name}">
<Path.Data>
<EllipseGeometry RadiusY="{Binding Radius}" RadiusX="{Binding Radius}">
<EllipseGeometry.Center>
<MultiBinding Converter="{StaticResource XYPositionConverter}">
<Binding Path="X"></Binding>
<Binding Path="Y"></Binding>
</MultiBinding>
</EllipseGeometry.Center>
</EllipseGeometry>
</Path.Data>
</Path>
</DataTemplate>
我把xaml代码缩短了很多,因为有很多冗余信息。只要知道它是一个包含多种对象的可观察集合(这就是我使用数据模板的原因),并且这个数据模板所在的 itemscontrol 也绑定到另一个集合。不过,我认为这与问题无关。
因此,在我们讨论错误代码之前,我将向您展示一些不会引发错误的代码。
此方法移动 spaceObjects 并执行一些与 UI 无关的其他操作。没问题。
private void MoveAndCheckLegacy(Object source, ElapsedEventArgs e)
{
Parallel.For(0, _spaceObjects.Count, i =>
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject) _spaceObjects[i];
spaceObject.Move();
CheckForCollisionLegacy(spaceObject);
}
else
{
var lineObject = (LineSpaceObject) _spaceObjects[i];
lineObject.UpdatePosition();
}
});
}
它只是循环遍历 observablecollection 中的 spaceObjects,更改它们的 X 和 Y。多重绑定注意到这一点并采取相应的行动。
现在我写了一个更大的方法,它也将元素插入到四叉树中,但是移动部分只是像以前一样的 for 循环的一部分(以前也是并行的,但我在调试这个问题时改变了它)。
var qtree = new QuadTree(new Rectangle(0, 0, 800, 600), "base");
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject)_spaceObjects[i];
spaceObject.Move();
// Printing X and Y and Name for debugging purposes
Trace.WriteLine($"{spaceObject.Name}: ({spaceObject.X}, {spaceObject.Y})");
}
else // Some stuff to do with other objects.
{
var lineObject = (LineSpaceObject)_spaceObjects[i];
lineObject.UpdatePosition();
}
}
// Insert spaceObjects into QuadTree
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var so = _spaceObjects[i] is SpaceObject;
if (so) qtree.Insert((SpaceObject)_spaceObjects[i]);
}
// Show Quadtree on UI
ShowQtree(qtree);
这可以正常运行一段时间,但随后我的转换器抛出 DependencyProperty.UnsetValue 错误。
所以我开始打印我的转换器在进行任何转换之前收到的值:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Trace.WriteLine($"({values[0]}. {values[1]})");
var i = (decimal) values[0];
var y = (decimal) values[1];
var a = (int) i;
var b = (int) y;
return new Point(a, b);
}
现在是奇怪的部分。我还打印了行星的 x、y 值(用于调试目的)。
如果可观察集合中只有一个 spaceObject,则输出如下。
Kobol: (127,5, 12,0)
(127,5. 18,0)
Kobol: (127,8, 11,4)
(127,5. 11,4) <<<<< right here
({DependencyProperty.UnsetValue}. 11,4)
似乎就在它尝试转换任何东西之前,转换器有正确的值!
我把 'right here' 放在你可以看到转换器内部值的地方。
然后下一刻好像又进了转换器,现在就没有值了。
我可以打赌,实际的行星确实有一个 X 和 Y 值(虽然不能检查这个,因为应用程序在调试时冻结 :( )。这个移动算法没有改变并且没有问题(并且仍然工作没有问题),在此方法之外。
有没有人有任何想法或建议?
如果不清楚请告诉我。我会根据需要删除和添加详细信息。
编辑**
在注释掉大部分涉及移动和四叉树内容的方法后,我让它像最不复杂的移动方法一样工作。难道是我的方法太大了?我忘了提到它在定时器线程内部运行....也许它太慢导致问题?
您的 XYPositionConverter
必须处理这个特殊值。 WPF
将在探测转换器时使用 DependencyProperty.UnsetValue。这通常发生在您的 DataTemplate
创建时,可能在视图之外。
因此,请确保在您的转换器中处理此问题:
public class XYPositionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null ||
values.Any(x => x == null || x == DependencyProperty.UnsetValue))
{
return DependencyProperty.UnsetValue;
}
// ...
return result;
}
// ...
}
我遇到了一个奇怪的问题。
我在 observablecollection 中得到了一组 spaceObjects(行星)。这些对象在屏幕上移动,因此它们的 X 轴和 Y 轴一直在变化。这是它的 xaml 代码:
<DataTemplate DataType="{x:Type spaceObjects:SpaceObject}">
<Path Fill="{Binding Color, Converter={StaticResource ColorConverter}}"
Stroke="{Binding Border, Converter={StaticResource ColorConverter}}"
StrokeThickness="{Binding BorderThickness}" Tag="{Binding Name}">
<Path.Data>
<EllipseGeometry RadiusY="{Binding Radius}" RadiusX="{Binding Radius}">
<EllipseGeometry.Center>
<MultiBinding Converter="{StaticResource XYPositionConverter}">
<Binding Path="X"></Binding>
<Binding Path="Y"></Binding>
</MultiBinding>
</EllipseGeometry.Center>
</EllipseGeometry>
</Path.Data>
</Path>
</DataTemplate>
我把xaml代码缩短了很多,因为有很多冗余信息。只要知道它是一个包含多种对象的可观察集合(这就是我使用数据模板的原因),并且这个数据模板所在的 itemscontrol 也绑定到另一个集合。不过,我认为这与问题无关。
因此,在我们讨论错误代码之前,我将向您展示一些不会引发错误的代码。 此方法移动 spaceObjects 并执行一些与 UI 无关的其他操作。没问题。
private void MoveAndCheckLegacy(Object source, ElapsedEventArgs e)
{
Parallel.For(0, _spaceObjects.Count, i =>
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject) _spaceObjects[i];
spaceObject.Move();
CheckForCollisionLegacy(spaceObject);
}
else
{
var lineObject = (LineSpaceObject) _spaceObjects[i];
lineObject.UpdatePosition();
}
});
}
它只是循环遍历 observablecollection 中的 spaceObjects,更改它们的 X 和 Y。多重绑定注意到这一点并采取相应的行动。
现在我写了一个更大的方法,它也将元素插入到四叉树中,但是移动部分只是像以前一样的 for 循环的一部分(以前也是并行的,但我在调试这个问题时改变了它)。
var qtree = new QuadTree(new Rectangle(0, 0, 800, 600), "base");
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var isSpaceObject = _spaceObjects[i] is SpaceObject;
if (isSpaceObject)
{
var spaceObject = (SpaceObject)_spaceObjects[i];
spaceObject.Move();
// Printing X and Y and Name for debugging purposes
Trace.WriteLine($"{spaceObject.Name}: ({spaceObject.X}, {spaceObject.Y})");
}
else // Some stuff to do with other objects.
{
var lineObject = (LineSpaceObject)_spaceObjects[i];
lineObject.UpdatePosition();
}
}
// Insert spaceObjects into QuadTree
for (int i = 0; i < _spaceObjects.Count - 1; i++)
{
var so = _spaceObjects[i] is SpaceObject;
if (so) qtree.Insert((SpaceObject)_spaceObjects[i]);
}
// Show Quadtree on UI
ShowQtree(qtree);
这可以正常运行一段时间,但随后我的转换器抛出 DependencyProperty.UnsetValue 错误。 所以我开始打印我的转换器在进行任何转换之前收到的值:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
Trace.WriteLine($"({values[0]}. {values[1]})");
var i = (decimal) values[0];
var y = (decimal) values[1];
var a = (int) i;
var b = (int) y;
return new Point(a, b);
}
现在是奇怪的部分。我还打印了行星的 x、y 值(用于调试目的)。 如果可观察集合中只有一个 spaceObject,则输出如下。
Kobol: (127,5, 12,0)
(127,5. 18,0)
Kobol: (127,8, 11,4)
(127,5. 11,4) <<<<< right here
({DependencyProperty.UnsetValue}. 11,4)
似乎就在它尝试转换任何东西之前,转换器有正确的值! 我把 'right here' 放在你可以看到转换器内部值的地方。 然后下一刻好像又进了转换器,现在就没有值了。 我可以打赌,实际的行星确实有一个 X 和 Y 值(虽然不能检查这个,因为应用程序在调试时冻结 :( )。这个移动算法没有改变并且没有问题(并且仍然工作没有问题),在此方法之外。
有没有人有任何想法或建议?
如果不清楚请告诉我。我会根据需要删除和添加详细信息。
编辑**
在注释掉大部分涉及移动和四叉树内容的方法后,我让它像最不复杂的移动方法一样工作。难道是我的方法太大了?我忘了提到它在定时器线程内部运行....也许它太慢导致问题?
您的 XYPositionConverter
必须处理这个特殊值。 WPF
将在探测转换器时使用 DependencyProperty.UnsetValue。这通常发生在您的 DataTemplate
创建时,可能在视图之外。
因此,请确保在您的转换器中处理此问题:
public class XYPositionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null ||
values.Any(x => x == null || x == DependencyProperty.UnsetValue))
{
return DependencyProperty.UnsetValue;
}
// ...
return result;
}
// ...
}