【问题标题】:How does a container know when a child has called InvalidateArrange?容器如何知道孩子何时调用了 InvalidateArrange?
【发布时间】:2012-10-19 20:32:00
【问题描述】:

我正在尝试学习在 WinRT XAML 应用程序中创建自定义面板的基础知识。我已经定义了一个附加的依赖属性,它按预期工作,除了我不知道如何获取子元素的属性回调来触发容器的排列或测量。

让孩子知道应该再次调用排列和测量的正确方法是什么?在我的 WPF 4 unleashed book 中,他们使用了 FrameworkPropertyMetadataOptions.AffectsParentArrange,但这似乎在 WinRT 中不可用。

public class SimpleCanvas : Panel
{
    #region Variables
    #region Left Property
    public static double GetLeft(UIElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        object value = element.GetValue(LeftProperty);
        Type valueType = value.GetType();
        return Convert.ToDouble(value);
    } 

    public static void SetLeft(UIElement element, double value)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        element.SetValue(LeftProperty, value);
    }

    public static readonly DependencyProperty LeftProperty =
        DependencyProperty.RegisterAttached("Left", typeof(double), typeof(SimpleCanvas),
        new PropertyMetadata(0, OnLeftPropertyChanged));

    public static void OnLeftPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = (UIElement)source;
        // This doesn't cause ArrangeOverride below to be called
        element.InvalidateArrange();
    }
    #endregion

    #region Top Property
    public static double GetTop(UIElement element)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        object value = element.GetValue(TopProperty);
        return (value == null) ? 0 : (double)value;
    }

    public static void SetTop(UIElement element, double value)
    {
        if (element == null)
        {
            throw new ArgumentNullException("element");
        }

        element.SetValue(TopProperty, value);
    }

    public static readonly DependencyProperty TopProperty =
        DependencyProperty.RegisterAttached("Top", typeof(double), typeof(SimpleCanvas),
        new PropertyMetadata(0, OnTopPropertyChanged));

    public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = (UIElement)source;
        // This doesn't cause ArrangeOverride below to be called
        element.InvalidateArrange();
    }
    #endregion
    #endregion

    public SimpleCanvas()
    {

    }

    #region Methods
    protected override Size MeasureOverride(Size availableSize)
    {
        foreach (UIElement child in this.Children)
        {
            child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        }

        return new Size(0, 0);
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in this.Children)
        {
            double x = 0;
            double y = 0;

            double left = GetLeft(child);
            double top = GetTop(child);

            if (!double.IsNaN(left))
            {
                x = left;
            }
            if (!double.IsNaN(top))
            {
                y = top;
            }

            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
        }

        return finalSize;
    }
    #endregion
}

【问题讨论】:

    标签: c# xaml windows-runtime


    【解决方案1】:

    我参加聚会迟到了,但我朝同一个方向走,遇到了同样的问题。这是我的解决方案。

    在您的回调中,您在将属性附加到的子元素上调用 InvalidateArrange:

    public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = (UIElement)source;
        // This doesn't cause ArrangeOverride below to be called
        element.InvalidateArrange();
    }
    

    但是你真的应该通过改变你的代码来使面板无效:

    public static void OnTopPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
    {
        UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
        if(panel != null)       
            panel.InvalidateArrange();
    }
    

    它应该可以工作(对我有用)。

    【讨论】:

    • 我不敢相信我花了两天的大部分时间试图弄清楚这一点......非常感谢!
    【解决方案2】:

    如果单独使用 InvalidateArrange 不起作用,您也可以尝试 InvalidateMeasure 或 UpdateLayout。

    【讨论】:

    • 将回调更改为 InvalidateArrange()、InvalidateMeasure() 和 UpdateLayout(),但容器中的 ArrangeOverride() 和 MeasureOverride() 中的断点均未命中。
    • 也许它会检查是否有任何布局属性发生了变化,如果没有——它不会调用它。您可能需要更改一些布局属性,但这可能会破坏现有的绑定。
    【解决方案3】:

    我在依赖父控件的FontSize 的子控件时遇到了这个问题。我通过遍历父母堆栈并使所有内容无效来解决了这个问题:

        static MyControl()
        {
            // replace base implementation of the dependent property 
            FontSizeProperty.OverrideMetadata(typeof(Scalar), 
                new FrameworkPropertyMetadata(SystemFonts.MessageFontSize, FrameworkPropertyMetadataOptions.Inherits, OnMeasureInvalidated));
        }
    
        private static void OnMeasureInvalidated(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            // recurse over parent stack
            while (true)
            {
                var parent = VisualTreeHelper.GetParent(sender) as UIElement;
                if (parent == null) return; // break on root element
                parent.InvalidateMeasure();
                sender = parent;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2020-11-25
      • 2020-05-05
      • 2013-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-08
      • 1970-01-01
      相关资源
      最近更新 更多