动态应用程序级资源在 ElementHost 中托管时不是动态的

发布于 2024-07-16 18:34:35 字数 1915 浏览 6 评论 0原文

我在 WinForms 容器中托管 WPF UserControl。 现在,我希望能够为 UserControl 设置主题/皮肤。 为此,我有几个定义“皮肤”的资源字典。 当我的应用程序启动时,我创建一个“new System.Windows.Application()”,以便 Application.Current 存在。 要更改主题,旧皮肤将被删除,新皮肤将在运行时合并到应用程序级资源字典中。 但是,这不会更改 UserControl 中任何动态引用的资源。 我在一个直接的 WPF 应用程序中尝试过这个,它工作得很好。 我是否错过了什么,或者根本不可能做到这一点? 顺便说一句,如果我在 UserControl 初始化之前将皮肤添加到应用程序资源中,它将可以工作,但之后我无法更改皮肤。

要以最基本的方式重新存储此内容:

创建一个新的 WinForms 应用程序。 将 WPF 用户控件添加到应用程序。 这很简单:

<UserControl ...>
   <Grid>
      <Button
         Background="{DynamicResource ButtonBG}"/>
   </Grid>
</UserControl>

创建两个 ResourceDictionaries、White.xaml 和 Black.xaml(或其他),它们具有 SolidColorBrush,其中 ButtonBG 键具有相应的颜色。 在 Form1.cs 中,添加两个 Button 和一个 ElementHost。 将 ElementHost 的子级设置为我们刚刚创建的 UserControl 的实例。 将按钮连接到交换皮肤的事件:

private void White_Click(object sender, EventArgs e)
{
   Application.Current.Resources.MergedDictionaries[0] = 
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\White.xaml", UriKind.Relative)));
}

private void Black_Click(object sender, EventArgs e)
{
   Application.Current.Resources.MergedDictionaries[0] = 
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\Black.xaml", UriKind.Relative)));
}

在 Program.cs 中,确保 Application.Current 存在并设置初始皮肤:

[STAThread]
static void Main()
{
   new System.Windows.Application();

   Application.Current.Resources.MergedDictionaries[0] =
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\White.xaml", UriKind.Relative)));

   ...
}

现在,当单击“白色”按钮时,我希望 UserControl 中的按钮变为白色,并且当单击黑色按钮我希望按钮变黑。 然而,这并没有发生。

有谁知道为什么? 有解决办法吗?

编辑:想法:也许,如果有一种方法可以在主题更改时强制重新评估 DynamicResources,那就行了。

谢谢, 尘土飞扬

I'm hosting a WPF UserControl in a WinForms container. Now, I want to be able to theme/skin the UserControl. To do this, I've got several resource dictionaries that define the "skins." When my app starts up I create a "new System.Windows.Application()" so that Application.Current exists. To change the theme the old skin is removed and a new skin is merged into the Application level resource dictionary at runtime. However, this does not change any of the dyanamically referenced resources in the UserControl. I tried this in a straight WPF application and it worked just fine. Am I missing something, or is it not possible to do this at all? By the way, if I add a skin into the application resources before the UserControl is initialized it will work but I cannot change the skin after that.

To repo this in the most basic way:

Create a new WinForms application. Add a WPF UserControl to the app. This is simple enough:

<UserControl ...>
   <Grid>
      <Button
         Background="{DynamicResource ButtonBG}"/>
   </Grid>
</UserControl>

Create two ResourceDictionaries, White.xaml and Black.xaml (or whatever) that have a SolidColorBrush with the key ButtonBG with respective color. In Form1.cs, add two Buttons and an ElementHost. Set the child of the ElementHost to an instance of the UserControl we just created. Wire up the buttons to events that swap the skin:

private void White_Click(object sender, EventArgs e)
{
   Application.Current.Resources.MergedDictionaries[0] = 
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\White.xaml", UriKind.Relative)));
}

private void Black_Click(object sender, EventArgs e)
{
   Application.Current.Resources.MergedDictionaries[0] = 
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\Black.xaml", UriKind.Relative)));
}

In Program.cs, ensure that Application.Current exists and set the initial skin:

[STAThread]
static void Main()
{
   new System.Windows.Application();

   Application.Current.Resources.MergedDictionaries[0] =
      (ResourceDictionary)Application.LoadComponent(
         new Uri(@"\WpfThemes;component\White.xaml", UriKind.Relative)));

   ...
}

Now, when the White button is clicked I would expect the button in the UserControl to turn white and when the Black button is clicked I would expect the button to turn black. This does not happen, however.

Does anyone know why? Is there a solution?

Edit: Idea: Perhaps, if there's a way to force re-evaluation of DynamicResources when the theme changes, that would work.

Thanks,
Dusty

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧城空念 2024-07-23 18:34:36

当我尝试做类似的事情时,WPF 博士拯救了我。 他展示了如何在 WinForms 中创建 Application 对象。 现在,您可以将所有内容引用为 StaticResource,就像在 WPF 应用程序中一样。

http://drwpf.com /blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/

Dr. WPF came to my rescue when I was trying to do something similar. He shows how to create the Application object in WinForms. Now you can reference everything as StaticResource just like in a WPF application.

http://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/

不醒的梦 2024-07-23 18:34:36

另一种解决方法是创建一个虚拟窗口并将 elementhost 的内容指定为 content。
如果您查看应用程序并检查它如何处理资源字典的更改,您会发现它只通知窗口。您

应该提醒的唯一一件事是永远不要显示窗口(->例外),并在处理时关闭它elementhost,以便应用程序可以正常关闭。

Another workaround would be to create a dummy window and specify the content of the elementhost as content.
If you look into the Application and check how it handles changes of resourcedictionaries, you see that it only notifies windows..

The only thing you should remind is to never show the window (-> exception), and to close it when disposing the elementhost, so the application can shutdown properly.

莫多说 2024-07-23 18:34:35

我认为这可能是WPF框架中一个被忽视的问题。

从我通过 Reflector 得知的情况来看,当 Application 资源字典发生灾难性更改(这种更改可能会产生广泛的影响,例如添加、删除或替换皮肤)时,会有代码循环遍历应用程序中的所有 Windows 并强制它们重新评估其 DynamicResources。 但是,我认为 WPF 中的顶级元素(例如 ElementHost)却没有得到相同的处理。 这导致了我正在经历的行为。

我解决此问题的方法是手动单独检查所有 ElementHost 并添加、删除或替换外观 ResourceDictionary 文件。 它并不完美,但它完成了工作。

I think this may be an overlooked issue in the WPF framework.

From what I can tell via Reflector, it appears that when the Application resource dictionary is catastrophically changed (a change that will likely have wide ranging effects like adding, removing, or replacing a skin), there is code that loops over all of the Windows in the application and forces them to re-evaluate their DynamicResources. However, other elements that I would consider top-level in WPF like ElementHosts do not get the same treatment. This leads to the behavior that I'm experiencing.

My workaround to this issue is to manually go through all of my ElementHosts individually and add, remove, or replace the skin ResourceDictionary file. It's not perfect, but it gets the job done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文