2

我有几个不同的应用程序,这会有所帮助,但为了一个具体的例子,让我们假设我有一个 WinForms 应用程序。

此应用程序利用ElementHost对象内的 WPF 控件。我现在想为所有 WPF 按钮 ( System.Windows.Controls.Button) 定义一个隐式样式,以便每个ElementHost都不需要在资源字典中合并,也不Button需要每个都显式指定样式。

我在哪里定义所说的风格?

我尝试Themes\Generic.xaml在项目根目录中创建资源字典并指定

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None,
    ResourceDictionaryLocation.SourceAssembly )]

AssemblyInfo.cs. 这不起作用,我的印象是,那里的样式仅适用于在同一程序集中定义的自定义控件,而Button在外部程序集中定义。

示例将隐式样式代码放在<Application.Resources>; 但是,该节点是 in-in App.xaml,其中(该项目尚未作为 WPF 应用程序开始其生命)不存在。是否可以添加App.xaml或是否有其他地方可以放置以使<Application.Resources>它们被识别?

4

2 回答 2

2

WPF 博士有几个建议:
http ://drwpf.com/blog/2007/10/05/managing-application-resources-when-wpf-is-hosted/

下面引用一些相关部分:


在代码中创建应用程序实例并添加资源
下面是一个非常简单的函数,如果应用程序对象不存在,它将创建它,然后加载一些资源:

public static void EnsureApplicationResources()
{
    if (Application.Current == null)
    {
        // create the Application object
        new Application();

        // merge in your application resources
        Application.Current.Resources.MergedDictionaries.Add(
            Application.LoadComponent(
               new Uri("MyLibrary;component/Resources/MyResourceDictionary.xaml",
               UriKind.Relative)) as ResourceDictionary);
    }
}

现在,您只需确保在解析任何包含对应用程序级资源的静态资源引用的 XAML 文件之前调用此函数。为此,只需在基于标记的类的构造函数中添加对上述函数的调用,然后再调用InitializeComponent()

public Page1()
{
    EnsureApplicationResources();
    InitializeComponent();
}

在 XAML 中定义应用程序类并动态创建它
首先,我们不希望 MSBuild 为我们的应用程序类生成应用程序入口点。因此,我们需要将其声明为元素,而不是将文件声明为项目文件中的App.xaml元素:ApplicationDefinitionPage

<Page Include="App.xaml" />
<Compile Include="App.xaml.cs">
  <DependentUpon>App.xaml</DependentUpon>
  <SubType>Code</SubType>
</Compile>

接下来,我们需要确保我们的App.xaml标记被解析。通常,这是作为入口点函数的一部分完成的(我们刚刚删除了它)。相反,我们可以简单地为Application类定义一个构造函数并直接调用InitializeComponent

public App()
{
    InitializeComponent();
}

现在我们所有的资源和合并的字典都可以在其中声明App.xaml,我们加载应用程序实例的静态函数可以像这样简单:

public static void EnsureApplicationResources()
{
    if (Application.Current == null)
    {
        // create the Application object
        new App();
    }
}

在代码中管理资源字典的集合并在元素级别合并它们
在这种情况下,我们根本不利用Application对象。相反,我们在运行时动态加载每一个ResourceDictionary,并根据需要有选择地将其合并到页面或窗口或特定元素中。

public static class SharedResources
{
    public static readonly DependencyProperty MergedDictionariesProperty =
        DependencyProperty.RegisterAttached("MergedDictionaries",
            typeof(string), typeof(SharedResources),
            new FrameworkPropertyMetadata((string)null,
                new PropertyChangedCallback(OnMergedDictionariesChanged)));

    public static string GetMergedDictionaries(DependencyObject d)
    {
        return (string)d.GetValue(MergedDictionariesProperty);
    }

    public static void SetMergedDictionaries(DependencyObject d, string value)
    {
        d.SetValue(MergedDictionariesProperty, value);
    }

    private static void OnMergedDictionariesChanged(DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        if (!string.IsNullOrEmpty(e.NewValue as string))
        {
            foreach (string dictionaryName in (e.NewValue as string).Split(';'))
            {
                ResourceDictionary dictionary = GetResourceDictionary(dictionaryName);
                if (dictionary != null)
                {
                    if (d is FrameworkElement)
                    {
                        (d as FrameworkElement).Resources
                            .MergedDictionaries.Add(dictionary);
                    }
                    else if (d is FrameworkContentElement)
                    {
                        (d as FrameworkContentElement).Resources
                            .MergedDictionaries.Add(dictionary);
                    }
                }
            }
        }
    }

    private static ResourceDictionary GetResourceDictionary(string dictionaryName)
    {
        ResourceDictionary result = null;
        if (_sharedDictionaries.ContainsKey(dictionaryName))
        {
            result = _sharedDictionaries[dictionaryName].Target;
        }
        if (result == null)
        {
            string assemblyName = System.IO.Path.GetFileNameWithoutExtension(
                Assembly.GetExecutingAssembly().ManifestModule.Name);
            result = Application.LoadComponent(new Uri(assemblyName
                + ";component/Resources/" + dictionaryName + ".xaml",
                UriKind.Relative)) as ResourceDictionary;
            _sharedDictionaries[dictionaryName] = new WeakReference(result);
        }
        return result;
    }

    private static Dictionary<string, WeakReference> _sharedDictionaries
        = new Dictionary<string, WeakReference>();
}

这将允许我们将共享资源字典合并到任何框架元素的 Resources 集合中,只需执行以下操作:

<Grid dw:SharedResources.MergedDictionaries="ApplicationBrushes;ButtonStyles">
于 2012-12-19T13:25:23.273 回答
0

我能够让 Dr WPF App.xaml 方法工作在前面的答案中提到。我向我的 WinForms 应用程序从托管控件加载的 WPF 用户控件程序集添加了一个新页面。我从加载所有相同资源的其他 WPF 客户端应用程序复制了真正的 App.xaml.cs。我将它的内容复制到这个新的“页面”中,并根据需要更新命名空间和引用。我将 EnsureApplicationResources 静态方法添加到新的 App.xaml.cs App 类中。

它看起来像这样:

public static void EnsureApplicationResources() {
            if (Application.Current == null) {
                // create the Application object
                try {
                    new App() {
                        ShutdownMode = ShutdownMode.OnExplicitShutdown
                    };
                }
                catch (System.Exception ex) {
                    Debug.WriteLine(ex.ToString());
                }
            }
        }

诀窍是找出正确的位置来调用它来实例化应用程序。我的 WinForms 应用程序从多个位置启动 WPF 窗口,因此我第一次尝试从根窗口的构造函数中调用 Ensure... 方法没有奏效,因为实际上可能实例化了多个“根窗口”。它也没有可靠地处理 WPF 应用程序,因此它随后在尝试重新创建相同的应用程序时抛出异常,即使 Application.Current 显示为空。

相反,我将它添加到 WinForms 应用程序的启动例程中。我不在那个项目中使用启动对象。相反,我选择 Main() 子例程提供的显式控制。顺便说一下,WinForms 应用程序位于 VB.NET 中。

Public Module MyWinFormsApp

    Public Sub Main()
        MyWpfApp.App.EnsureApplicationResources()
        Application.Run(New frmWinFormsMain())
        System.Windows.Application.Current.Shutdown()
    End Sub
End Module

This approach works perfectly. The WPF App is created when the WinForms app starts up and shut down when it shuts down and is available with any App scoped resources to all of my hosted WPF controls including implicit styles, etc.

于 2016-12-20T18:28:59.967 回答