为什么ListBoxItem的宽度更改时未调用MeasureOverride? [英] Why is a ListBoxItem not calling MeasureOverride when its width is changed?
问题描述
ListBoxItem
的子类和ListBox
的子类,通过覆盖IsItemItsOwnContainerOverride
和GetContainerForItemOverride
来将其用作容器.
现在,如预期的那样,当窗口首次出现时,在每个ListBoxItem
(带有Infinity,Infinity)上调用MeasureOverride
,然后在每个项目上调用ArrangeOverride
.
但是,当调整ListBox
的大小时,即使width属性的元数据设置为AffectsMeasure
,在ListBoxItem
上也仅调用ArrangeOverride
,而不在MeasureOverride
上调用.
注意:我知道我可以通过将
ScrollViewer.HorizontalScrollbarVisibility
设置为禁用"来解决此问题,在这种情况下,确实会按预期方式调用MeasureOverride
,因为滚动设置会强制项目与列表框的宽度匹配,因此自然会重新开火.但是,我仍然试图弄清楚为什么默认情况下不会调用Measure,因为Width
属性的元数据设置了AffectsMeasure
标志,并且宽度通过ArrangeOverride
步骤更改.
该标志只是针对其容器的提示吗?如果控件放置在ScrollViewer
中,它会被忽略吗?我的猜测是,除非您禁用滚动功能,否则控件将具有无限可用的区域,因此一旦对其进行了测量,就无需再次对其进行重新测量.但是,禁用水平滚动,您会说宽度不是无限的,因此再次调用MeasureOverride
.但这只是一个猜测,尽管是合乎逻辑的.
这里是示例代码.创建一个新的WPF项目,并将其粘贴到窗口的CodeBehind中,然后查看调试输出.接下来,设置HorizontalScrollbarVisibility标志,您将看到它确实被调用.
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 caseMeasureOverride
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 theWidth
property has theAffectsMeasure
flag set and the width is changing via theArrangeOverride
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屋!