2013-04-25 38 views
4

不幸的是,我不得不在我的应用程序中覆盖其中一个TabControl的控件模板,因为我需要对外观和感觉进行一些轻微修改,否则无法完成。基于当前主题更改控制模板

<TabControl Name="specialTabControl" Template="{StaticResource SpecialTabControlTemplate} />". 

一切都很好,直到我切换操作系统的主题。现在我的TabControl看起来完全垃圾,因为它使用了错误主题的控件模板(主题我基于我的控件模板,用Blend提取)。

所以我想需要找到一种方法来提供特殊TabControl需要根据当前的OS主题可以选择4个控制模板(LUNA,航空,XP,经典)。

因此,如何能提供,并根据当前的主题应用不同自定义控件模板specialTabControl,这样当用户切换操作系统的主题,那specialTabControl将切换到我已经提供了该主题控件模板?

请注意,我还有其他TabControl在没有覆盖控制模板的应用程序中,并且应始终具有该主题的标准控制模板。

+0

每当有人推荐分配一个新的ControlTemplate来解决某些问题时,我完全停止阅读,因为这个问题总是立刻流入我的脑海。 – springy76 2016-05-04 08:11:57

+0

您也可以为各种控件编写模板,以便您的应用程序及其所有控件在所有主题中看起来都一样 - 无论将来会有什么新主题。这就是例如我猜是微软Office。 – Lumo 2017-09-08 10:09:01

回答

0

我想你需要在WPF应用程序中看到主题。组件可包含以下行:

[组件:ThemeInfo(ResourceDictionaryLocation SourceAssembly, ResourceDictionaryLocation.SourceAssembly)]

这意味着应用程序的系统的主题位于您组件(在文件夹/主题) 。主题名称必须符合系统主题...例如:

The Aero theme (Windows Vista and Windows 7): themes\Aero.NormalColor.xaml 
The default Windows XP theme: themes\Luna.NormalColor.xaml 
The olive green Windows XP theme: themes\Luna.Homestead.xaml 
The Windows Classic theme: themes\Classic.xaml 

当用户改变系统的皮肤,WPF应用程序会自动下载你的主题。因此,您可以为每个系统主题设置控制模板。更多信息可以在:

“Adam Nathan。WPF 4 Unleashed”。第14章

主题化WPF应用程序: http://blogs.infosupport.com/theming-wpf-applications/

我希望这有助于。

*编辑*

我发现这是指真正改变主题,一个有趣的例子:

http://northhorizon.net/2010/how-to-actually-change-the-system-theme-in-wpf/

您可以设置明确的风格,这将不改变皮肤反应在系统中:

<Style x:Key="ExplicitGreenButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> 
    <Setter Property="Background" Value="Green" /> 
    <Setter Property="Foreground" Value="White" /> 
</Style> 

因此,隐式样式将响应到改变体系皮肤:

<Style x:Key="ImplicitGreenButtonStyle" TargetType="Button"> 
    <Setter Property="Background" Value="Green" /> 
    <Setter Property="Foreground" Value="White" /> 
</Style> 

而且,例如在包含在ThemeHelper一个有用的代码,其中某些主题的功能。

*编辑#2 *

如果我理解正确的,你首先需要得到系统的主题名称。这个动作可以通过几种方式完成。

首先是从库“了UxTheme.dll”像GetCurrentThemeName()使用Win32函数:

[DllImport("uxtheme.dll", CharSet = CharSet.Auto)] 
public static extern int GetCurrentThemeName(StringBuilder pszThemeFileName, int dwMaxNameChars, StringBuilder pszColorBuff, int dwMaxColorChars, StringBuilder pszSizeBuff, int cchMaxSizeChars); 

StringBuilder stringThemeName = new StringBuilder(260); 
StringBuilder stringColorName = new StringBuilder(260); 
StringBuilder stringSizeName = new StringBuilder(260); 

Int32 s = GetCurrentThemeName(stringThemeName, 260, stringColorName, 260, stringSizeName, 260); 

但是我有这个功能无法正常收到了一些系统主题名称,例如作为“经典”。所以我尝试了一种不同的方式。

第二种方法是从注册表中获取主题名称。在路径“HKEY_CURRENT_USER \ Software \ Microsoft \ Windows \ CurrentVersion \ Themes \ LastTheme”包含当前的系统主题。所以我们需要通过窗口钩子拦截“更改系统主题事件”。

下面是几个按钮,这取决于系统主题为例,风格:

添加到窗口:

SourceInitialized="Window_SourceInitialized" 

样式:

<Style x:Key="DefaultStyle" BasedOn="{StaticResource {x:Type Button}}" TargetType="{x:Type Button}"> 
    <Setter Property="Background" Value="CadetBlue" /> 
</Style> 

<Style x:Key="LunaStyle" TargetType="{x:Type Button}"> 
    <Setter Property="Background" Value="Blue" /> 
    <Setter Property="Foreground" Value="White" /> 
</Style> 

<Style x:Key="ClassicStyle" TargetType="{x:Type Button}"> 
    <Setter Property="Background" Value="Gray" /> 
    <Setter Property="Foreground" Value="Black" /> 
</Style> 

主网:

<Grid> 
    <Button Style="{StaticResource DefaultStyle}" Content="Default button" Width="100" Height="30" VerticalAlignment="Top" HorizontalAlignment="Left" /> 
    <Button Name="ChangeButtonStyle" Content="Changes style" Width="100" Height="30" VerticalAlignment="Top" HorizontalAlignment="Right" /> 
    <TextBlock Name="CurrentTheme" FontSize="16" Text="Null" Width="150" Height="30" HorizontalAlignment="Center" VerticalAlignment="Top" /> 
</Grid> 

在代码:

拦截“系统更改主题活动”:

private IntPtr hwnd; 
private HwndSource hsource; 

private void Window_SourceInitialized(object sender, EventArgs e) 
{ 
    if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero) 
    { 
     throw new InvalidOperationException("Could not get window handle."); 
    } 

    hsource = HwndSource.FromHwnd(hwnd); 
    hsource.AddHook(WndProc); 
} 

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{ 
    switch (msg) 
    { 
     case 0x31A:   // Define this as WM_DWMCOMPOSITIONCHANGED for Windows 7 
     case 0x31E:   // Define this as WM_THEMECHANGED 

     // Action on the change system theme 
     GetThemeName(SubKey, Value); 

     return IntPtr.Zero; 

     default: 

     return IntPtr.Zero; 
    } 
} 

获取系统主题名称,设置样式:

public string SubKey = "Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\LastTheme"; 
public string Value = "ThemeFile"; 

private void GetThemeName(string OpenKey, string Value) 
{ 
    RegistryKey pRegKey = Registry.CurrentUser; 
    pRegKey = pRegKey.OpenSubKey(OpenKey); 
    Object val = pRegKey.GetValue(Value); 
    string NameThemeFile = val as string; 

    if (NameThemeFile.IndexOf("Luna") != -1) 
    { 
     ChangeButtonStyle.Style = this.FindResource("LunaStyle") as Style; 
     CurrentTheme.Text = "Luna"; 
    } 

    if (NameThemeFile.IndexOf("Classic") != -1) 
    { 
     ChangeButtonStyle.Style = this.FindResource("ClassicStyle") as Style; 
     CurrentTheme.Text = "Classic"; 
    } 
} 

这种方法是不是最好的,但将开始。

+0

但我怎么能为一个TabControl和一个自定义主题(基于当前操作系统主题切换)为另一个TabControl使用默认主题(来自WPF本身)? – bitbonk 2013-04-25 19:20:00

+0

感谢您提供更新的详细答案,但我认为您还没有完全理解我的问题。我不想主题整个应用程序,我不想有一个固定的主题/风格。我想为*** ***'TabControl'提供4个控制模板(luna,classic,aero,xp),并且每当操作系统主题发生变化时它都应该切换到该控制模板。在同一个应用程序中还有'TabControl'没有自定义控件模板,只是应该像使用当前操作系统主题风格的常规选项卡控件一样运行。 – bitbonk 2013-04-26 07:08:32

+0

我已更新我的问题,希望有更好的解释。 – bitbonk 2013-04-26 07:24:59