Хорошо, я отвечаю на это, чтобы показать людям, почему ответ CodeNaked является правильным, но со звездочкой, если хотите, и также предоставить обходной путь. Но при хорошем СО-гражданстве я все же помечаю его как ответившего, поскольку его ответ привел меня сюда.
Обновление: с тех пор я перенес принятый ответ сюда по двум причинам. Во-первых, я хочу, чтобы люди знали, что - это решение этой проблемы (большинство людей только читают принятый ответ и идут дальше), и во-вторых, учитывая, что у него повторение 25K, я не думаю, что он против, если я заберу это обратно! :)
Вот что я сделал. Чтобы проверить это, я создал этот подкласс ...
public class TestPanel : DockPanel
{
protected override Size MeasureOverride(Size constraint)
{
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
return base.ArrangeOverride(arrangeSize);
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
... которую я выложил вот так (обратите внимание, что они вложенные):
<l:TestPanel x:Name="MainTestPanel" Background="Yellow">
<Button Content="Test" Click="Button_Click" DockPanel.Dock="Top" HorizontalAlignment="Left" />
<l:TestPanel x:Name="InnerPanel" Background="Red" Margin="16" />
</l:TestPanel>
Когда я изменил размеры окна, я получил это ...
MeasureOverride called for MainTestPanel.
MeasureOverride called for InnerPanel.
ArrangeOverride called for MainTestPanel.
ArrangeOverride called for InnerPanel.
OnRender called for InnerPanel.
OnRender called for MainTestPanel.
но когда я позвонил InvalidateVisual
в MainTestPanel (в событии кнопки Click), я получил это вместо ...
ArrangeOverride called for MainTestPanel.
OnRender called for MainTestPanel.
Обратите внимание, как не было вызвано ни одно из переопределений измерений, а был вызван только ArrangeOverride для внешнего элемента управления.
Это не идеально, как если бы у вас был очень тяжелый расчет внутри ArrangeOverride
в вашем подклассе (что, к сожалению, мы делаем), который все еще выполняется (ре), но по крайней мере дети не попадают в ту же судьбу.
Однако, если вы знаете, что ни один из дочерних элементов управления не имеет свойства с установленным битом AffectsParentArrange (опять-таки, что мы и делаем), вы можете пойти еще лучше и использовать Nullable Size
в качестве флага для подавления логики ArrangeOverride из повторный вход, за исключением случаев, когда это необходимо, как ...
public class TestPanel : DockPanel
{
Size? arrangeResult;
protected override Size MeasureOverride(Size constraint)
{
arrangeResult = null;
System.Console.WriteLine("MeasureOverride called for " + this.Name + ".");
return base.MeasureOverride(constraint);
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
if(!arrangeResult.HasValue)
{
System.Console.WriteLine("ArrangeOverride called for " + this.Name + ".");
// Do your arrange work here
arrangeResult = base.ArrangeOverride(arrangeSize);
}
return arrangeResult.Value;
}
protected override void OnRender(System.Windows.Media.DrawingContext dc)
{
System.Console.WriteLine("OnRender called for " + this.Name + ".");
base.OnRender(dc);
}
}
Теперь, если что-то определенно не нуждается в повторном выполнении логики упорядочения (как это делает вызов MeasureOverride), вы получаете только OnRender, и, если вы хотите явно форсировать логику упорядочения, просто обнулите размер, вызовите InvalidateVisual и Боб дядя! :)
Надеюсь, это поможет!