【问题标题】:Implicit WPF syles in non-WPF Application非 WPF 应用程序中的隐式 WPF 样式
【发布时间】:2016-12-21 02:28:59
【问题描述】:

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

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

我在哪里定义所说的样式?

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

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

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

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

【问题讨论】:

    标签: c# wpf styles


    【解决方案1】:

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

    下面引用的一些相关部分:


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

    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 为我们的 Application 类生成应用程序入口点。因此,我们不需要在项目文件中将App.xaml 文件声明为ApplicationDefinition 元素,而是将其声明为Page 元素:

    <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>();
    }
    

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

    <Grid dw:SharedResources.MergedDictionaries="ApplicationBrushes;ButtonStyles">
    

    【讨论】:

    • 事情已经发生了,我无法立即对此进行测试,但看起来很有希望。谢谢!!
    【解决方案2】:

    我能够让 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
    

    这种方法非常有效。 WPF 应用程序在 WinForms 应用程序启动时创建,并在它关闭时关闭,并且可用于我所有托管的 WPF 控件的任何应用程序范围的资源,包括隐式样式等。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-15
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-26
      • 1970-01-01
      相关资源
      最近更新 更多