Windows 8中未应用Combobox背景

8

我对WPF主题感到有些困惑。我希望在Vista、Windows 7和Windows 8上看起来一样。因此,我已经相应地为这些组件设置了样式,但在Windows 8上运行时会出现问题。例如,我有一个组合框,我正在通过xaml更改它的默认背景,就像这样。

<Style TargetType="{x:Type ComboBox}" >
    <Setter Property="FontStyle" Value="Normal"/>
    <Setter Property="Height" Value="24" />
    <Setter Property="Background" Value="{StaticResource GradientButtonBackgroundBrush}"/>
</Style>

组合框的“背景”属性在Windows 8中没有效果,我只能得到一个扁平的矩形,在右侧有一个箭头(这是Windows 8默认的组合框,设计相当不好!)。

那么,我的问题是如何使所有版本的Windows上的组合框外观相同。我尝试在我的App.xaml中添加Windows Aero主题,如下所示,但它对组合框显示没有影响。以下是我添加Aero主题的方法:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/PresentationFramework.Aero;component/themes/aero.normalcolor.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

还有一个关于主题的疑问。我在Windows 7机器上构建wpf应用程序,默认情况下(我相信)设置了Aero主题。因此,在Windows 7机器上查看时,我的所有样式都基于Aero主题。如果我在XP上运行应用程序会发生什么?那么我是否需要像上面代码中列出的那样在App.xaml中添加资源字典(Aero主题)的条目?

我知道我的问题有点模糊,但请相信我,在不同的Windows版本上使用wpf的默认主题确实让我感到非常困惑。

编辑: 我仍然无法根据我的需求设置combobox的样式。combobox仍然显示为灰色矩形。

这是我做的。我从Microsoft的网站下载了Aero.NormalColor.xaml,并将其包含在应用程序的themes文件夹中。然后我在App.xaml中添加了以下内容。

    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/Themes/Aero.NormalColor.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

然后我编译了应用程序并在Windows 8上部署。仍然是之前显示的相同的下拉框。请注意,所有其他元素都按照主题正确地进行了样式化。我对Luna.Metallic.xaml做了同样的操作,每个元素都被样式化了,除了ComboBox

我认为当我加载特定主题时,它会使用ControlTemplate定义样式,然后WPF应该选择它。我很困惑为什么只有ComboBox即使给它Aero(或Luna)控件模板也不改变其外观。有任何想法吗?

编辑-2 Windows 8上下拉框外观的屏幕截图 enter image description here


我已经针对你的编辑更新了我的答案。请在相关答案或问题中添加评论。如果您在与您所陈述的内容无关的答案上发表评论,那么很难跟踪。 - Viv
5个回答

2

ComboBox可点击区域实际上是一个ToggleButton

如果你查看Windows-8中这个ToggleButton的Style,你会看到如下内容:

<ControlTemplate TargetType="{x:Type ToggleButton}">
  <Border x:Name="templateRoot"
          Background="{StaticResource ComboBox.Static.Background}"
          BorderBrush="{StaticResource ComboBox.Static.Border}"
          BorderThickness="{TemplateBinding BorderThickness}"
          SnapsToDevicePixels="true">
...

如您所见,Background使用的不是{TemplateBinding Background}而是{StaticResource ComboBox.Static.Background}。这就是为什么在Windows-8中设置ComboBoxBackground属性时看不到任何效果。
如果您想在不同的操作系统版本中保持Style(而无需回溯并不断检查新版本是否破坏了您的覆盖),简单的规则是自己创建它。
创建一个Style,将其设置为应用于TargetType且没有Key,以便自动应用。这样,在任何操作系统中都会使用您的Style而不是默认的底层样式。
这样可以确保您的代码在每个操作系统上都按预期运行。基于任何操作系统的默认样式,并根据需要进行调整。 附言
从可用性的角度来看,在运行于Windows-8上的应用程序中提供Windows-7 ComboBox并不好(除非您的整个应用程序看起来像Windows-7应用程序,这甚至更糟)。您希望用户习惯于您的应用程序样式,并忘记他在使用基于操作系统的默认样式的其他所有应用程序中习惯的内容。如果您有特定的原因,请继续,但请考虑其影响。
例如,您表示不喜欢Windows-8的样式,而我则相反。我实际上喜欢Windows-8干净简洁的外观。没有闪烁的渐变和让您分心的东西,这会影响到您放在他们面前的内容。这是一个永无止境的争论。只要警告最终用户期望和想法而不仅仅是您认为写程序时很好的东西即可。 更新: 首先,请在相关答案上发表评论。您的回答和评论更新之间没有关系。
好的,至于您的问题编辑,您尝试的方法在Windows-8中不起作用,因为PresentationFramework.Aero.dll不存在,它保存了Aero.NormalColor.xaml。在Windows-8中,您的选项是默认的PresentationFramework.Aero2.dll和我认为是Windows Server 2012使用的PresentationFramework.AeroLite.dll(不确定)。
尝试在Windows-8上编译您的程序,您会发现它甚至不想编译。
您将需要显式添加对PresentationFramework.AeroPresentationUI(我认为是.net3的一部分)的引用到您的项目中。
然后,您将不得不将您的Aero.NormalColor.xaml编辑为类似以下内容:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:theme="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
                    xmlns:ui="clr-namespace:System.Windows.Documents;assembly=PresentationUI">
...

我们明确说明了Aero主题的组件。我不使用Windows-7,所以不确定是否只需要这些。但您可以尝试一下。

尝试在Windows-8中编译代码,以确保它在Windows-8上能够正常工作。


我按照您建议的步骤进行了操作,但组合框仍然呈灰色。请查看原问题中我的EDIT-2以获取屏幕截图。也许这会提示正在选择哪个XAML主题。由于我在该机器上没有安装Visual Studio 2010,因此在Windows 8机器上编译需要一些时间。 - Jatin

2

最终,在经过数小时的挫败后,我找到了一篇解释文章在这里。向下滚动查看长答案。它准确地解释了我一直面临的情况,特别是Aero样式未应用于我的组合框。该链接很好地解释了为什么我们需要为每个要进行样式设置的元素添加BasedOn属性,如果我们不想使用默认的操作系统样式。因此,为组合框添加此BasedOn属性使其对我起作用。

<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">

现在组合框使用的是Aero主题样式。正如@Viv所指出的那样,这可能还需要将PresentationFramework.Aero.dll和PresentationUI.dll复制到Windows 8计算机上,因为它们没有随操作系统提供。
谢谢, Nirvan

1

我对内置模板进行了一些修改。虽然不是最干净的解决方案,但可以避免必须自己创建模板的麻烦。基本上,背后的代码将内置模板边框的属性与组合框的属性绑定在一起。

<Style TargetType="ComboBox">
    <Setter Property="Border.Background" Value="White"/>
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded"/>
    <Style.Triggers>
        <Trigger Property="IsReadOnly" Value="True">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
        </Trigger>
        <Trigger Property="IsFocused" Value="True">
            <Setter Property="Background" Value="{StaticResource ResourceKey=FocusedControlBackcolorBrush}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
        </Trigger>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BorderBrush" Value="{StaticResource ResourceKey=FocusedControlBorderBrush}"/>
        </Trigger>
    </Style.Triggers>
</Style>


    private void ComboBox_Loaded(object sender, RoutedEventArgs e)
    {
        var comboBox = sender as ComboBox;
        var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
        var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
        if (border != null)
        {
            Binding b = new Binding("Background");
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
            BindingOperations.SetBinding(border, Control.BackgroundProperty, b);

            b = new Binding("BorderBrush");
            b.RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ComboBox), 1);
            BindingOperations.SetBinding(border, Control.BorderBrushProperty, b);
        }
    }

0
新的模板具有一个Grid->ToggleButton->Border,其中硬编码的背景颜色不尊重任何背景样式。根据@Matstar的答案,我制定了一种方法来同步背景颜色。
您仍然可以设置绑定到背景颜色,代码将在需要时拾取并重新应用此颜色:
<ComboBox ItemsSource="{Binding Values}" x:Name="Cbo2"
Background="{Binding Path=SelectedValueBgColor}" ... />

然后在 xaml.cs 文件中

cbo.Loaded += (sender, args) =>
{
    var comboBox = sender as ComboBox;
    if (comboBox != null)
    {
        var toggleButton = comboBox.Template?.FindName("toggleButton", comboBox) as ToggleButton;
        var border = toggleButton?.Template.FindName("templateRoot", toggleButton) as Border;
        if (border != null)
        {
            var existing = BindingOperations.GetBinding(comboBox, BackgroundProperty);
            BindingOperations.SetBinding(border, BackgroundProperty, existing);
        }
    }
};

确保您的背景绑定永远不会返回null。如果没有有意义的值,请返回透明、白色或其他默认值。如果绑定到返回null的背景,则组合框将停止工作。
请确保在事件发生时,通知背景绑定需要刷新的属性已更改。

0

回复@Viv

@Viv,我觉得你的回答很清晰,但是你的观点/建议对我来说有些难以理解,我会简要说明原因。

你的建议

如果你想在不同的操作系统版本中保持样式的一致性(而不必返回并不断检查新版本是否破坏了你的覆盖),简单的规则是自己创建。

这似乎是不切实际的。主题的核心概念是为应用程序中的所有元素和所有平台创建一致的外观。因此,如果我使用框架提供的特定主题,无论如何,我都应该能够在当前主要平台上达到令人满意的一致性水平。最重要的是,并非每个人都有从头开始创建所有样式和模板的专业知识。主题的概念应该是:

“框架提供者提供适用于正常情况的主题,如果有人希望推出自己的主题,他可以这样做”

相反,这里的概念似乎是:

“框架提供者提供主题,不能保证它的一致性和不会出现问题。因此,总是推出自己的主题”

你的引用

如您所见,上面使用的背景不是{TemplateBinding Background},而是{StaticResource ComboBox.Static.Background}。因此,当您在Windows-8中为该ComboBox设置Background属性时,您将看不到任何效果。
我不知道谁想出了这种模板!可能是他们疯了,或者我太蠢了,无法理解其中的优势。想象一下,我正在使用一些蓝色主题的应用程序,明天,在Windows 9上,有人将{StaticResource ComboBox.Static.Background}定义为红色。那么,你猜怎么着,我现在有一个花哨的窗口屏幕,所有元素都以“蓝色”为主题,只有组合框显示为“红色”。我的意思是,这里主题的整个概念都被打破了!
你的侧记
例如,你说Windows-8风格不是你喜欢的,而我则相反。我实际上喜欢Windows-8的简洁外观。
我的组合框看起来像一个禁用的按钮,我甚至不能改变它的背景!按钮看起来不像可点击的!当然,正如你所说,这是个人选择。

我的结论 如果有人对我在Windows 8上如何使组合框工作有任何建议,我会等待一段时间。你的答案揭示了真相,如果我没有得到其他替代方案,我很高兴将其标记为正确。

Nirvan


在我看来,预设主题并不是为了在不同的操作系统中保持相同的外观,而是为了在操作系统环境下保持不同控件的外观相同。是的,在Windows-7中出现的ComboBox可能在Windows-9中是红色的,而使用Windows-9的用户实际上可能更喜欢这种颜色,而不是看到一个应用程序使用蓝色,只因为他们认为这是正确的选择。当您在Windows-8上使用带有按钮的ComboBox并运行它时,您的主题是一致的。它们与默认样式之间没有明显的差异。这就是默认主题给您的东西。 - Viv
对于从零开始创建样式的担忧。再次强调,我从不从头创建样式。要么使用Blend并让它将元素的样式作为副本创建,要么搜索控件的基础样式,在其中进行复制粘贴,然后进行编辑和修改。试图重新发明轮子是毫无意义的。您可以获取默认主题的完整源代码。从那里进行编辑和微调,而不是从头开始。 - Viv
主题不会再次破坏。我想你对主题的看法和我不同。如我在上面的评论中所解释的, 默认主题标准化了控件的外观,而不是跨操作系统版本。一个简单的例子是采用跨操作系统UI框架(如Qt或其他类似框架)。它们有控件和主题。如果保持默认设置,Windows上的按钮将呈现相同,并且与Fedora上的按钮不同。现在你肯定不希望你的Windows按钮看起来像Unix吧。我认为这就是我对主题的理解,即针对控件进行标准化。 - Viv
针对您最后的评论,这不仅仅是将样式应用于组合框,而是需要覆盖控件模板以更改显示。我认为控件模板与行为更密切相关,而不是像背景这样更改一些次要属性。这就是破坏主题的原因。 - Jatin
1
是的,我明白了。非常感谢您的帮助。如果您没有花时间查看控件模板,我将永远无法发现这个问题。 - Jatin
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接