如何在保留原本所有樣式/綁定和用戶設置值的情況下,設置和還原 wpf 依賴項屬性的值

如何在保留原本所有樣式/綁定和用戶設置值的情況下,設置和還原 wpf 依賴項屬性的值

wpf 備份某控制項的一些屬性,做一些神奇的操作,然後再還原這些屬性。

最后更新 2021/11/9 上午10:08
吕毅
预计阅读 4 分钟
分类
WPF
标签
.NET WPF

wpf 備份某控制項的一些屬性,做一些神奇的操作,然後再還原這些屬性。多麼司空見慣的操作呀!然而怎麼備份卻是值得研究的問題。直接賦值?那一定是因為你沒踩到一些坑。

本文內容

  • 場景和問題
  • 解決方法和原理
  • 延伸

場景和問題

現在,我們假想一個場景(為了編代碼方便):

  1. 有一個窗口,設置了一些樣式屬性
  2. 現在需要將這個窗口設置為全屏,這要求修改一些原來的屬性(wpf 自帶那設置有 bug,我會另寫一篇博客說明)
  3. 取消設置窗口全屏後,之前修改的那些屬性要“完美”還原

一般可能會這麼寫:

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 关注我的主页

知识共享许可协议本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接: https://blog.walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2025/1/26

wpf 藉助自定義 xml 文件實現國際化

本文詳細居間了在wpf程式中使用自定義xml文件實現國際化的方法,包括安裝必備nuget包、動態獲取語言列表、動態切換語言、在代碼和xaml界面中使用翻譯字符串等內容,同時提供了源碼連結,幫助開發者輕鬆實現wpf應用的國際化。

继续阅读