
WPF 備份某控制項的一些屬性,做一些神奇的操作,然後再還原這些屬性。多麼司空見慣的操作呀!然而怎麼備份卻是值得研究的問題。直接指派?那一定是因為你沒踩到一些坑。
本文內容
- 場景和問題
- 解決方法和原理
- 延伸
場景和問題
現在,我們假想一個場景(為了寫程式方便):
- 有一個視窗,設定了一些樣式屬性
- 現在需要將這個視窗設為全螢幕,這要求修改一些原來的屬性(WPF 自帶那個設定有 bug,我會另寫一篇部落格說明)
- 取消設定視窗全螢幕後,之前修改的那些屬性要「完美」還原
一般可能會這麼寫:
private Window _window;
private WindowStyle _oldStyle;
private void OnEnterFullScreen()
{
_oldStyle = _window.WindowStyle;
_window.WindowStyle = WindowStyle.None;
}
private void OnExitFullScreen()
{
_window.WindowStyle = _oldStyle;
}
然而:
- 如果某人在
WindowStyle上設了個動態的樣式怎麼辦?——那當然是不再動態了呀(因為覆蓋了樣式值) - 如果某人在
WindowStyle上設定了繫結怎麼辦?——那當然也是不再生效了呀(因為繫結被你覆蓋了)
解決方法和原理
因為各大 WPF 入門書籍都說到了 WPF 相依性屬性的優先權機制,所以大家應該基本都知道這個。不了解的,可以立刻去這裡看看:相依性屬性值優先順序 - WPF Microsoft Docs。
是這樣的優先順序:強制 > 動畫 > 本機值 > 範本 > 隱含樣式 > 樣式觸發程序 > 範本觸發程序 > 樣式 > 預設樣式 > 屬性繼承 > 中繼資料預設值。
而我們透過在 XAML 或 C# 程式碼中直接指派,設定的是「本機值」。因此,如果設定了本機值,那麼更低優先權的樣式當然就全部失效了。
那麼繫結呢?繫結在相依性屬性優先權中並不存在。繫結實際上是透過「本機值」來實現的,將一個繫結運算式設定到「本機值」中,然後在需要值的時候,會 ProvideValue 提供值。所以,如果再設定了本機值,那麼繫結的設定就被覆蓋掉了。
但是,SetCurrentValue 就是幹這件事的!
SetCurrentValue 設計為在不改變相依性屬性任何既有值的情況下,設定屬性目前的值。
_window.SetCurrentValue(Window.WindowStyleProperty, WindowStyle.None);
那麼,只需要還原 SetCurrentValue 所做的修改,就還原了此相依性屬性一切設定的值:
_window.InvalidateProperty(Window.WindowStyleProperty);
注意不是 ClearValue,那會清除本機值。
然而還差一點,繫結如果在你應用 SetCurrentValue 期間有改變,那麼這次的指派並不會讓繫結立即生效,所以我們還需要手工再讓繫結重新更新值:
BindingOperations.GetBindingExpression(_window, Window.WindowStyleProperty)?.UpdateTarget();
那麼,綜合起來,本文一開始的程式碼將更新成如下形式:
private Window _window;
private void OnEnterFullScreen()
{
_window.SetCurrentValue(Window.WindowStyleProperty, WindowStyle.None);
}
private void OnExitFullScreen()
{
_window.InvalidateProperty(Window.WindowStyleProperty);
BindingOperations.GetBindingExpression(_window, Window.WindowStyleProperty)?.UpdateTarget();
}
延伸
將程式碼變得通用一點:
static void ApplyTempProperty(DependencyObject d, DependencyProperty dp, object tempValue)
{
d?.SetCurrentValue(dp, tempValue);
}
static void RestoreProperty(DependencyObject d, DependencyProperty dp)
{
d.InvalidateProperty(dp);
BindingOperations.GetBindingExpression(d, dp)?.UpdateTarget();
}
本文會經常更新,請閱讀原文: https://blog.walterlv.com/post/change-and-restore-wpf-dependency-value-without-disabling-the-declared-use-of-the-property.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
如果你想持續閱讀我的最新部落格,請點擊 RSS 訂閱,或者前往 CSDN 關注我的主頁。
知識共享授權條款本作品採用 創用CC 姓名標示-非商業性-相同方式分享 4.0 國際授權條款 進行授權。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含連結: https://blog.walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的授權發布。如有任何疑問,請 與我聯繫 (walter.lv@qq.com) 。