为什么 ListBoxItem 在其宽度更改时不调用 MeasureOverride?

发布于 2024-12-07 23:57:56 字数 2182 浏览 1 评论 0原文

好的,出于说明目的,下面我创建了 ListBoxItem 的子类和 ListBox 的子类,它通过重写 IsItemItsOwnContainerOverrideGetContainerForItemOverride

现在,当窗口首次出现时,如预期的那样,在每个 ListBoxItem 上调用 MeasureOverride (带有 Infinity,Infinity),然后在每个 ListBoxItem 上调用 ArrangeOverride物品。

但是,在调整 ListBox 大小时,仅在 ListBoxItem 上调用 ArrangeOverride,而不是 MeasureOverride,即使width 属性设置为 AffectsMeasure

注意:我知道我可以通过将 ScrollViewer.Horizo​​ntalScrollbarVisibility 设置为“禁用”来解决此问题,在这种情况下,MeasureOverride 确实会按预期调用,因为滚动设置会强制项目匹配列表框的宽度,因此自然会重新触发。但是,我仍在尝试找出为什么默认情况下不调用 Measure,因为 Width 属性的元数据设置了 AffectsMeasure 标志并且宽度正在更改通过 ArrangeOverride 步骤。

该标志是否只是其容器的提示,如果控件放置在 ScrollViewer 中,它会被忽略吗?我的猜测是,除非您禁用滚动,否则控件将有无限的可用区域,因此一旦测量了它们,就无需再次重新测量它们。但是,禁用水平滚动并且您声明宽度不是无限的,因此再次调用 MeasureOverride 。但这只是一种猜测,尽管是合乎逻辑的。

这是可以使用的示例代码。创建一个新的 WPF 项目并将其粘贴到窗口的 CodeBehind 中并查看调试输出。接下来,设置 Horizo​​ntalScrollbarVisibility 标志,您将看到它确实被调用。

public partial class MainWindow : Window
{
    public MainWindow(){
        InitializeComponent();
        var items = new List<object>(){ "This is a really, really, really, really long sentence"};
        var lbx = new ListBoxEx { ItemsSource = items };
        this.Content = lbx;
    }

}

public class ListBoxEx : ListBox
{
    protected override bool IsItemItsOwnContainerOverride(object item){
        return (item is ListBoxItemEx);
    }

    protected override DependencyObject GetContainerForItemOverride(){
        return new ListBoxItemEx();
    }
}

public class ListBoxItemEx : ListBoxItem
{
    protected override Size MeasureOverride(Size availableSize){
        Console.WriteLine("MeasureOverride called with " + availableSize);
        return base.MeasureOverride(availableSize);
    }

    protected override Size ArrangeOverride(Size finalSize){
        Console.WriteLine("ArrangeOverride called with " + finalSize);
        return base.ArrangeOverride(finalSize);
    }

}

Ok, for illustrative purposes, below I created a subclass of ListBoxItem and a subclass of ListBox which uses it as its container by overriding both IsItemItsOwnContainerOverride and GetContainerForItemOverride.

Now when the window first appears, as expected, MeasureOverride is called on every ListBoxItem (with Infinity,Infinity) followed by ArrangeOverride being called on every item.

However, when resizing the ListBox, only ArrangeOverride is called on the ListBoxItem, not MeasureOverride even though the metadata for the width property is set to AffectsMeasure.

NotE: I know I can get around this by setting ScrollViewer.HorizontalScrollbarVisibility to 'Disabled' in which case MeasureOverride does get called as expected because that scroll setting forces the items to match the width of the listbox and thus naturally would re-fire. However, I'm still trying to figure out why Measure isn't called by default anyway because the metadata for the Width property has the AffectsMeasure flag set and the width is changing via the ArrangeOverride step.

Is that flag just a hint for its container and in the case of a control placed in a ScrollViewer it's ignored? My guess is that unless you disable the scrolling, the controls have an infinite area available to them, so once they are measured, there's no need to re-measure them again. Disable the horizontal scrolling however and you're stating the width isn't unlimited, hence the MeasureOverride is called again. But that's just a guess, albeit a logical one.

Here's example code to play with. Create a new WPF project and paste this in the window's CodeBehind and look at the debug output. Next, set the HorizontalScrollbarVisibility flag and you'll see that it does get called.

public partial class MainWindow : Window
{
    public MainWindow(){
        InitializeComponent();
        var items = new List<object>(){ "This is a really, really, really, really long sentence"};
        var lbx = new ListBoxEx { ItemsSource = items };
        this.Content = lbx;
    }

}

public class ListBoxEx : ListBox
{
    protected override bool IsItemItsOwnContainerOverride(object item){
        return (item is ListBoxItemEx);
    }

    protected override DependencyObject GetContainerForItemOverride(){
        return new ListBoxItemEx();
    }
}

public class ListBoxItemEx : ListBoxItem
{
    protected override Size MeasureOverride(Size availableSize){
        Console.WriteLine("MeasureOverride called with " + availableSize);
        return base.MeasureOverride(availableSize);
    }

    protected override Size ArrangeOverride(Size finalSize){
        Console.WriteLine("ArrangeOverride called with " + finalSize);
        return base.ArrangeOverride(finalSize);
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

萌︼了一个春 2024-12-14 23:57:56

好吧,我认为答案是它没有触发,因为测量通道影响的是面板,而不是项目,并且面板本身没有调整大小,因此没有理由重新测量其子项。

但是,当将 ScrollViewer.Horizo​​ntalScrollbarVisibility 设置为 Disabled 时,面板的宽度会跟踪 ListBox 的宽度,而不是其内容,因此需要重新测量子项,因此这就是他们处于这种情况的原因。

Ok, I think the answer is it's not firing because the Measure pass is affecting the panel, not the items, and the panel itself hasn't resized so there's no reason to re-measure its children.

However, when setting ScrollViewer.HorizontalScrollbarVisibility to Disabled, the width of the panel tracks the width of the ListBox, not its contents, therefore the children do need to be re-measured, hence that's why they were in that case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文