为什么ListBoxItem的宽度更改时未调用MeasureOverride? [英] Why is a ListBoxItem not calling MeasureOverride when its width is changed?

查看:97
本文介绍了为什么ListBoxItem的宽度更改时未调用MeasureOverride?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好的,出于说明目的,下面我创建了ListBoxItem的子类和ListBox的子类,通过覆盖IsItemItsOwnContainerOverrideGetContainerForItemOverride来将其用作容器.

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

但是,当调整ListBox的大小时,即使width属性的元数据设置为AffectsMeasure,在ListBoxItem上也仅调用ArrangeOverride,而不在MeasureOverride上调用.

注意:我知道我可以通过将ScrollViewer.HorizontalScrollbarVisibility设置为禁用"来解决此问题,在这种情况下,确实会按预期方式调用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);
    }

}

解决方案

好吧,我认为答案是不触发,因为Measure传递会影响面板,而不是项目,面板本身没有调整大小,因此没有理由重新测量其孩子.

但是,将ScrollViewer.HorizontalScrollbarVisibility设置为Disabled时,面板的宽度跟踪ListBox的宽度,而不是其内容,因此确实需要重新测量子级,因此这就是为什么

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);
    }

}

解决方案

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.

这篇关于为什么ListBoxItem的宽度更改时未调用MeasureOverride?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆