非 WPF 应用程序中的隐式 WPF syles



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

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

我在哪里定义所述样式?

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

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

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

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

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

<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">

我能够让 Dr WPF App.xaml 方法在上一个答案中提到。我向我的 WPF 用户控件程序集添加了一个新页面,该页面由我的 WinForms 应用从托管控件加载。 我从加载所有相同资源的其他 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 显示为 null,它随后也会引发异常。

相反,我将其添加到 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 控件(包括隐式样式等)的任何应用范围的资源。

相关内容

  • 没有找到相关文章

最新更新