【问题标题】:Random DependencyProperty.UnsetValue when multibinding多重绑定时的随机 DependencyProperty.UnsetValue
【发布时间】:2020-10-27 15:55:01
【问题描述】:

我遇到了一个奇怪的问题。

我在一个 observablecollection 中获得了一个空间对象(行星)的集合。这些对象在屏幕上移动,因此它们的 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代码缩短了很多,因为有很多冗余信息。只要知道它是一个包含多种对象的 observablecollection(这就是我使用数据模板的原因),并且这个数据模板所在的 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 中的空间对象,改变它们的 X 和 Y。多重绑定注意到这一点并采取相应的行动。

现在我写了一个更大的方法,它也将元素插入到 QuadTree 中,但移动部分只是像以前一样的 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 值(用于调试目的)。 如果 observable 集合中只有一个 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)

似乎在它尝试转换任何东西之前,转换器具有正确的值! 我把“就在这里”放在你可以看到转换器内部值的地方。 然后下一刻它似乎再次进入转换器,现在它没有价值。 我敢打赌,实际的行星确实有一个 X 和 Y 值(虽然不能检查这个,因为应用程序在调试时冻结:()。这个移动算法没有改变并且没有问题(并且仍然工作没有问题),在这个方法之外。

有人有什么想法或建议吗?

如果不清楚,请告诉我。我会在需要的地方删除和添加详细信息。

编辑**

在注释掉大部分涉及移动和四叉树的方法后,我让它像最简单的移动方法一样工作。难道是我的方法太大了?我忘了说它是在 Timer 线程中运行的......也许它太慢而导致问题?

【问题讨论】:

    标签: c# xaml observablecollection ivalueconverter


    【解决方案1】:

    您的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;
        }
        // ...
    }
    

    【讨论】:

    • 所以这行得通,但我仍然不明白为什么返回 DependencyProperty.UnsetValue 是接收 DependencyProperty.UnsetValue 的答案...
    • 您可以尝试返回一些其他值,例如null。不过,在某些极端情况下,您会在“输出”窗口中收到烦人的绑定警告。
    猜你喜欢
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2020-10-06
    • 2017-06-19
    • 1970-01-01
    • 2012-08-04
    • 2021-03-27
    • 1970-01-01
    相关资源
    最近更新 更多