【问题标题】:Split button for WPF that works in Win7 and Win8 themes适用于 Win7 和 Win8 主题的 WPF 拆分按钮
【发布时间】:2012-12-01 18:05:02
【问题描述】:

有人知道在 Win7 主题和 Win8 主题中看起来像常规按钮的 WPF 拆分按钮吗?我正在使用一个在 Win7 上看起来不错,但在 Win8 上却像大拇指一样突出的一个:

我试过WPF Splitbutton project on codeplexBanana Splitbuttonsplitbutton in the Extended WPF Toolkit

有没有什么东西可以在 Win7 中提供 Win7 主题按钮,在 Win8 中提供 Win8 主题按钮?

我需要控件具有可绑定的 Command 属性并在按下向下箭头时显示上下文菜单。

【问题讨论】:

  • 如果只是美学问题,您可以使用您发现的任何已满足您要求的拆分按钮,只需更改样式模板以使其看起来不同。这就是你所追求的吗?
  • 如果在 Win7/Win8 主题中使用时可以正确更改样式,那将起作用。
  • 你想为win7设置不同的风格,为win8设置另一种不同的风格吗?如果正确构建,样式模板在它们之间应该没问题。也许您所追求的一些视觉示例可能有助于澄清这里的总体要求。
  • @ChrisW。编辑以显示问题。我追求的是在 Win7 和 Win8 中使用时按钮的标准外观。

标签: wpf xaml wpf-controls


【解决方案1】:

我采用了 Sten 的方法:将一个按钮嵌套在另一个按钮中。我使用了一个用户控件来使其可重用,将所有元素放在控件模板中,这样它就可以在按钮中放置任意内容。

<UserControl 
  x:Class="VidCoder.Controls.SplitButton"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  mc:Ignorable="d"
  Loaded="SplitButton_OnLoaded">
  <UserControl.Template>
    <ControlTemplate TargetType="{x:Type UserControl}">
      <Button 
        HorizontalAlignment="Left" VerticalAlignment="Top" Name="mainButton" ContextMenuService.Placement="Bottom" 
        Width="{TemplateBinding Width}" Height="{TemplateBinding Height}">
        <Button.Content>
          <StackPanel Orientation="Horizontal" UseLayoutRounding="True">
            <ContentPresenter Margin="{TemplateBinding Padding}" />
            <Rectangle Width="1" Fill="#111111" Margin="0,2" />
            <Button Click="OnArrowClick">
              <Button.Template>
                <ControlTemplate TargetType="Button">
                  <Grid Background="Transparent" Name="buttonGrid">
                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                  </Grid>
                </ControlTemplate>
              </Button.Template>
              <Button.Content>
                <Path Data="M 0,0 L 8,0 L 4,4 Z" Fill="{TemplateBinding Foreground}" Margin="4 0 3 0" VerticalAlignment="Center"/>
              </Button.Content>
            </Button>
          </StackPanel>
        </Button.Content>
        <Button.ContextMenu>
          <ContextMenu Name="buttonMenu" ItemsSource="{Binding Path=MenuItemsSource, RelativeSource={RelativeSource TemplatedParent}}" />
        </Button.ContextMenu>
      </Button>
    </ControlTemplate>
  </UserControl.Template>
</UserControl>

代码隐藏公开了菜单项集合和 Command 属性:

public partial class SplitButton : UserControl
{
    private Button button;

    private ObservableCollection<object> menuItemsSource = new ObservableCollection<object>();

    public Collection<object> MenuItemsSource { get { return this.menuItemsSource; } }

    public SplitButton()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
        "Command",
        typeof (ICommand),
        typeof (SplitButton),
        new UIPropertyMetadata(null, OnCommandChanged));

    public ICommand Command
    {
        get
        {
            return (ICommand) GetValue(CommandProperty);
        }

        set
        {
            SetValue(CommandProperty, value);
        }
    }

    private static void OnCommandChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
    {
        if (eventArgs.NewValue != eventArgs.OldValue)
        {
            var splitButton = dependencyObject as SplitButton;

            if (splitButton.button != null)
            {
                splitButton.button.Command = eventArgs.NewValue as ICommand;
            }
        }
    }

    private void OnArrowClick(object sender, RoutedEventArgs e)
    {
        var buttonMenu = ContextMenuService.GetContextMenu(this.button);

        if (this.menuItemsSource.Count > 0 && buttonMenu != null)
        {
            buttonMenu.IsOpen = !buttonMenu.IsOpen;
            buttonMenu.PlacementTarget = this.button;
            buttonMenu.Placement = PlacementMode.Bottom;
        }
    }

    private void SplitButton_OnLoaded(object sender, RoutedEventArgs e)
    {
        this.button = this.Template.FindName("mainButton", this) as Button;
        if (this.Command != null)
        {
            this.button.Command = this.Command;
        }
    }
}

使用中:

<controls:SplitButton HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding TestCommand}">
    <controls:SplitButton.MenuItemsSource>
        <MenuItem Header="ham" Command="{Binding TestCommand2}" />
        <MenuItem Header="sandwiches" />
        <MenuItem Header="yum" />
    </controls:SplitButton.MenuItemsSource>
    <TextBlock Padding="4" Text="Testing" />
</controls:SplitButton>

【讨论】:

    【解决方案2】:

    您总是可以用两个标准按钮制作自己的按钮,这并不难 - 只需按钮内的一个按钮,自定义内部按钮的样式以缺少边框,具有下拉三角形并具有“透明”背景(与导致点击的 {x:null} 背景不同)

    大概是这样的:

    <Button Style="outerStyle">
      <Button.Content>
         <StackPanel Orientation="Horizontal">
            <Label>Click Me</Label>
            <Button Style="{innerStyleWithTransparentBackground}"> ▼ </Button>
         </StackPanel>
      </Button.Content>
    </Button>
    

    如果您使用大量拆分按钮(无论如何都要这样做),您可以将此构造放在带有几个路由事件的用户控件中,否则您可以将其嵌套在其他代码中。

    这不是最优雅的解决方案,但它可能是您能找到的最简单的解决方案之一。

    【讨论】:

    • 这有很多问题。第一个是当您单击嵌套按钮时,两个按钮的单击处理程序都会触发。其次是嵌套按钮不与主按钮的侧面齐平。第三是简单地设置背景颜色并不能摆脱按钮的轮廓/样式。
    • 第一个:不正确,如果您的处理程序声明事件“已处理”并且其背景不为空。空背景可能导致点击第二个:第三个按钮需要一些样式:正如您在伪造的示例中看到的那样,这些按钮设置了它们的样式,而不仅仅是背景
    • 好的,所以我们现在必须更改按钮控件模板并消除外部的所有内容填充?然后我想依靠任何使用该控件的控件将填充放回原处?至于子按钮,您还必须为此编写一个新的控件模板才能摆脱边缘,但随后您必须写回代码以进行悬停/按下着色等。我真的不喜欢这种方法。
    • 对于外部,您不必设置任何样式,这就是目标 - 重用系统默认按钮样式。对于内部,您必须制作一个非常简单的控件模板,例如 。如果您希望摆脱填充,您可以设置 RenderTransform 将其向右移动几个像素。您可以做一些更有趣的事情,例如更改鼠标悬停时的边框背景等。我确实说过这不是一个优雅的解决方案,但它绝对可行且比大多数其他解决方案更快
    • 我今天在这方面做了很多工作,并得到了一个 SplitButton 用户控件,它松散地基于这个建议。它可以正常工作,因为外部按钮没有很多填充。我选择这种方法是因为它是最灵活的主题:任何时候按钮由于主题而发生变化时都可以使用。我必须做一些其他聪明的事情才能让它发挥作用。一旦我把它清理干净,我会发布我所做的。
    【解决方案3】:

    “像大拇指一样突出”是 wpf 控件如何解决其样式的结果。默认情况下(意味着如果您没有通过 Style 属性提供 Style),splitbutton 将在资源树中向上查找,直到找到 TargetType 为 splitButton 且隐式样式 x:Key 为 "{x:Type SplitButton} ”。如果它无法为您的拆分按钮找到样式,它将在主题文件中查找您当前使用的任何 Windows 主题。在 win7 中,这通常是 Aero.NormalColor.xaml。我不确定win8,但我想Metro.NormalColor.xaml(如果我错了,请纠正我)。如果在主题字典中找不到样式,它将在 Generic.xaml 中查找。无论如何,您要做的是在资源树中有意义的级别为您的控件定义一个隐式样式(如果它是独立的,则可能在 Window 或 Page 如果 xaml 托管在浏览器中/>。这将是始终使用的样式(未明确定义样式时)。

    示例: http://msdn.microsoft.com/en-us/library/ms745683.aspx#styling_basics

    【讨论】:

    • 但我不想只选择一种风格并一直使用它。我希望它根据用户的主题而有所不同。无论用户使用什么操作系统或主题,我唯一的选择是让我的整个应用程序看起来总是一样吗?
    • 不,这绝对不是您唯一的选择。您实际上可以向 Windows 主题添加样式。请参阅以下链接中的“在主题级别定义资源”msdn.microsoft.com/en-us/library/ms745025.aspx
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-27
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-13
    • 1970-01-01
    相关资源
    最近更新 更多