WPF有每當相應的內置機制,使所有Adorners
被重新測量,重新排列,並重新呈現AdornedElement
更改大小,位置或變換。這個機制要求你在編寫你的裝飾者時遵循一定的規則,但並不是所有的文件都應該清楚地記錄下來。
我會先回答你的標題問題爲什麼你的裝潢不符合重新渲染,然後說明解決它的最好方法。
爲什麼裝飾器不重新渲染
每當AdornerLayer接收它掃描它的每一個裝飾器,看看該AdornedElement
的大小,位置改變或變換LayoutChanged通知。如果是這樣,它將設置標誌來強制Adorner
進行測量,排列和再次渲染 - 大致相當於InvalidateMeasure(); InvaliateArrange(); InvalidateVisual();
。
在這種情況下通常會發生的情況是控制首先被測量,然後進行排列,然後進行渲染。事實上,WPF試圖使這是最常見的情況,因爲它是最有效的序列。然而,在重新測量之前,很多情況下控制可能會重新排列和/或重新排列。這是WPF中事件的合法順序(允許靈活的佈局技術),但它並不常見,所以通常不會進行測試。
一個正確實施Adorner
或其他UIElement
會小心調用InvalidateVisual()
任何時候渲染可能會受到影響,除非僅AffectsRender
依賴屬性發生了變化。
在你的情況下,你的裝飾大小顯然會影響渲染。大小屬性不是AffectsRender
依賴項屬性,因此在更改時需要手動調用InvalidateVisual()
。如果你不這樣做,WPF可能永遠不知道重新渲染你的裝飾者。
什麼在你的情況正在發生的事情大概是這樣的:
- 佈局完成和
LayoutChanged
事件觸發
AdornerLayer
發現你的AdornedElement
AdornerLayer
時間表大小改變你對重新測量裝飾器,重新佈局和重新渲染
- 某些原因
Arrange()
被調用,導致在重新測量之前重新佈局和重新渲染。這導致WPF認爲裝飾者不再需要重新佈局或重新渲染。
- 佈局引擎檢測到裝飾器需要測量,並呼籲
Measure
- 裝飾器的
MeasureOverride
重新計算所需的大小,但確實沒什麼好說的WPF裝飾器需要重新渲染
- 佈局引擎決定有什麼更多的工作要做,因此裝飾器永遠不會重新呈現
你能做些什麼來解決它
解決的辦法是,當然,通過調用InvalidateVisual()
以修復Adorner
的bug。只要控制重新測量,像這樣:
protected override Size MeasureOverride(Size constraint)
{
var result = base.MeasureOverride(constraint);
// ... add custom measure code here if desired ...
InvalidateVisual();
return result;
}
這樣做將使你的裝飾器始終如一地遵守WPF的所有規則,所以它會在所有情況下按預期工作。這也是最有效的解決方案,因爲InvalidateVisual()
什麼都不會做,除非在真正需要的情況下。
尺寸屬性不會影響渲染有多奇怪 - 以前沒有碰到過,但是看起來像是一個討厭的小問題,我很驚訝它不會更經常出現!你知道(或者你可以推測)爲什麼WPF設計者以這種方式構建它?無論如何,謝謝你提供了這樣一個清晰,詳細和翔實的答案。 – itowlson 2010-03-27 01:48:49
@itowlson:因爲在最常見的情況下(RenderSize僅在ArrangeCore中更新並且渲染不依賴於任何其他大小),您可能從來沒有碰到過它,因爲在不需要InvalidateVisual()調用的情況下始終觸發渲染。記錄一系列ArrangeOverride和OnRender調用,以更好地感受這種工作方式。我的猜測是,設計師不想在每次設置RenderSize時自動調度OnRender,因爲他們設想RenderSize會被更改並立即改回的場景。 – 2010-03-29 21:13:28
不渲染的另一個原因可能是您必須傳遞給Adorner基礎構造函數的'UIElement'不在adorner層中。例如如果使用'AdornerDecorator'來創建本地裝飾器圖層,然後錯誤地引用超出視覺/邏輯樹的'UIElement'。 – Dennis 2012-08-15 10:07:16