为什么 ListBoxItem 在其宽度更改时不调用 MeasureOverride?
好的,出于说明目的,下面我创建了 ListBoxItem
的子类和 ListBox
的子类,它通过重写 IsItemItsOwnContainerOverride
和GetContainerForItemOverride
。
现在,当窗口首次出现时,如预期的那样,在每个 ListBoxItem
上调用 MeasureOverride
(带有 Infinity,Infinity),然后在每个 ListBoxItem
上调用 ArrangeOverride
物品。
但是,在调整 ListBox
大小时,仅在 ListBoxItem
上调用 ArrangeOverride
,而不是 MeasureOverride
,即使width 属性设置为 AffectsMeasure
。
注意:我知道我可以通过将
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);
}
}
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我认为答案是它没有触发,因为测量通道影响的是面板,而不是项目,并且面板本身没有调整大小,因此没有理由重新测量其子项。
但是,当将
ScrollViewer.HorizontalScrollbarVisibility
设置为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
toDisabled
, 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.