zoukankan      html  css  js  c++  java
  • WPF SplitButton 的杂七杂八

    原文:

    http://www.codeproject.com/Articles/20612/A-WPF-SplitButton

    SplitButton.cs

    using System;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Markup;
    
    namespace Wpf.Controls
    {
        /// <summary>
        /// Implemetation of a Split Button
        /// </summary>
        [TemplatePart(Name = "PART_DropDown", Type = typeof (Button))]
        [ContentProperty("Items")] //不用显示写<Items/>标签
        [DefaultProperty("Items")]
        public class SplitButton : Button
        {
            // AddOwner Dependency properties
            public static readonly DependencyProperty HorizontalOffsetProperty;
            public static readonly DependencyProperty IsContextMenuOpenProperty;
            public static readonly DependencyProperty ModeProperty;
            public static readonly DependencyProperty PlacementProperty;
            public static readonly DependencyProperty PlacementRectangleProperty;
            public static readonly DependencyProperty VerticalOffsetProperty;
    
            /// <summary>
            /// Static Constructor
            /// </summary>
            static SplitButton()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof (SplitButton), new FrameworkPropertyMetadata(typeof (SplitButton)));
                IsContextMenuOpenProperty = DependencyProperty.Register("IsContextMenuOpen", typeof(bool), typeof(SplitButton), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnIsContextMenuOpenChanged)));
                ModeProperty = DependencyProperty.Register("Mode", typeof(SplitButtonMode), typeof(SplitButton), new FrameworkPropertyMetadata(SplitButtonMode.Split));
    
                // AddOwner properties from the ContextMenuService class, we need callbacks from these properties
                // to update the Buttons ContextMenu properties
                PlacementProperty = ContextMenuService.PlacementProperty.AddOwner(typeof (SplitButton), new FrameworkPropertyMetadata(PlacementMode.Bottom, new PropertyChangedCallback(OnPlacementChanged)));
                PlacementRectangleProperty = ContextMenuService.PlacementRectangleProperty.AddOwner(typeof (SplitButton), new FrameworkPropertyMetadata(Rect.Empty, new PropertyChangedCallback(OnPlacementRectangleChanged)));
                HorizontalOffsetProperty = ContextMenuService.HorizontalOffsetProperty.AddOwner(typeof (SplitButton), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnHorizontalOffsetChanged)));
                VerticalOffsetProperty = ContextMenuService.VerticalOffsetProperty.AddOwner(typeof (SplitButton), new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnVerticalOffsetChanged)));
            }
    
    
            /*
             * Overrides
             * 
            */
            /// <summary>
            /// OnApplyTemplate override, set up the click event for the dropdown if present in the template
            /// </summary>
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
    
                // set up the click event handler for the dropdown button
                ButtonBase dropDown = this.Template.FindName("PART_DropDown", this) as ButtonBase;
                if (dropDown != null)
                    dropDown.Click += Dropdown_Click;
            }
    
            /// <summary>
            ///     Handles the Base Buttons OnClick event
            /// </summary>
            protected override void OnClick()
            {
                switch (Mode)
                {
                    case SplitButtonMode.Dropdown:
                        OnDropdown();
                        break;
                    
                    default:
                        base.OnClick(); // forward on the Click event to the user
                        break;
                }
            }
    
            /*
             * Properties
             * 
            */
    
    
            /// <summary>
            /// The Split Button's Items property maps to the base classes ContextMenu.Items property
            /// </summary>
            public ItemCollection Items
            {
                get
                {
                    EnsureContextMenuIsValid();
                    return this.ContextMenu.Items;
                }
            }
    
            /*
             * DependencyProperty CLR wrappers
             * 
            */
    
            /// <summary>
            /// Gets or sets the IsContextMenuOpen property. 
            /// </summary>
            public bool IsContextMenuOpen
            {
                get { return (bool) GetValue(IsContextMenuOpenProperty); }
                set { SetValue(IsContextMenuOpenProperty, value); }
            }
    
    
            /// <summary>
            /// Placement of the Context menu
            /// </summary>
            public PlacementMode Placement
            {
                get { return (PlacementMode) GetValue(PlacementProperty); }
                set { SetValue(PlacementProperty, value); }
            }
    
    
            /// <summary>
            /// PlacementRectangle of the Context menu
            /// </summary>
            public Rect PlacementRectangle
            {
                get { return (Rect) GetValue(PlacementRectangleProperty); }
                set { SetValue(PlacementRectangleProperty, value); }
            }
    
    
            /// <summary>
            /// HorizontalOffset of the Context menu
            /// </summary>
            public double HorizontalOffset
            {
                get { return (double) GetValue(HorizontalOffsetProperty); }
                set { SetValue(HorizontalOffsetProperty, value); }
            }
    
    
            /// <summary>
            /// VerticalOffset of the Context menu
            /// </summary>
            public double VerticalOffset
            {
                get { return (double) GetValue(VerticalOffsetProperty); }
                set { SetValue(VerticalOffsetProperty, value); }
            }
    
            /// <summary>
            /// Defines the Mode of operation of the Button
            /// </summary>
            /// <remarks>
            ///     The SplitButton two Modes are
            ///     Split (default),    - the button has two parts, a normal button and a dropdown which exposes the ContextMenu
            ///     Dropdown            - the button acts like a combobox, clicking anywhere on the button opens the Context Menu
            /// </remarks>
            public SplitButtonMode Mode
            {
                get { return (SplitButtonMode) GetValue(ModeProperty); }
                set { SetValue(ModeProperty, value); }
            }
    
            /*
             * DependencyPropertyChanged callbacks
             * 
            */
    
            private static void OnIsContextMenuOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                SplitButton s = (SplitButton) d;
                s.EnsureContextMenuIsValid();
    
                if (!s.ContextMenu.HasItems)
                    return;
    
                bool value = (bool) e.NewValue;
    
                if (value && !s.ContextMenu.IsOpen)
                    s.ContextMenu.IsOpen = true;
                else if (!value && s.ContextMenu.IsOpen)
                    s.ContextMenu.IsOpen = false;
            }
    
    
            /// <summary>
            /// Placement Property changed callback, pass the value through to the buttons context menu
            /// </summary>
            private static void OnPlacementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                SplitButton s = d as SplitButton;
                if (s == null) return;
    
                s.EnsureContextMenuIsValid();
                s.ContextMenu.Placement = (PlacementMode) e.NewValue;
            }
    
            /// <summary>
            /// PlacementRectangle Property changed callback, pass the value through to the buttons context menu
            /// </summary>
            private static void OnPlacementRectangleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                SplitButton s = d as SplitButton;
                if (s == null) return;
    
                s.EnsureContextMenuIsValid();
                s.ContextMenu.PlacementRectangle = (Rect) e.NewValue;
            }
    
            /// <summary>
            /// HorizontalOffset Property changed callback, pass the value through to the buttons context menu
            /// </summary>
            private static void OnHorizontalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                SplitButton s = d as SplitButton;
                if (s == null) return;
    
                s.EnsureContextMenuIsValid();
                s.ContextMenu.HorizontalOffset = (double) e.NewValue;
            }
    
            /// <summary>
            /// VerticalOffset Property changed callback, pass the value through to the buttons context menu
            /// </summary>
            private static void OnVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                SplitButton s = d as SplitButton;
                if (s == null) return;
    
                s.EnsureContextMenuIsValid();
                s.ContextMenu.VerticalOffset = (double) e.NewValue;
            }
    
            /*
             * Helper Methods
             * 
            */
    
            /// <summary>
            /// Make sure the Context menu is not null
            /// </summary>
            private void EnsureContextMenuIsValid()
            {
                if (this.ContextMenu == null)
                {
                    this.ContextMenu = new ContextMenu();
                    this.ContextMenu.PlacementTarget = this;
                    this.ContextMenu.Placement = Placement;
    
                    this.ContextMenu.Opened += ((sender, routedEventArgs) => IsContextMenuOpen = true);
                    this.ContextMenu.Closed += ((sender, routedEventArgs) => IsContextMenuOpen = false);
                }
            }
    
            private void OnDropdown()
            {
                EnsureContextMenuIsValid();
                if (!this.ContextMenu.HasItems)
                    return;
    
                this.ContextMenu.IsOpen = !IsContextMenuOpen; // open it if closed, close it if open
            }
    
            /*
             * Events
             * 
            */
    
            /// <summary>
            /// Event Handler for the Drop Down Button's Click event
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void Dropdown_Click(object sender, RoutedEventArgs e)
            {
                OnDropdown();
                e.Handled = true;
            }
        }
    }
    View Code

    SplitButtonMode.cs

    namespace Wpf.Controls
    {
        public enum SplitButtonMode
        {
            Split, Dropdown, Button
        }
    }

    SplitButtonResources.cs

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    
    namespace Wpf.Controls
    {
        /// <summary>
        /// Class used for the ComponentResourceKey
        /// </summary>
        public class SplitButtonResources
        {
            public static ComponentResourceKey VistaSplitButtonStyleKey
            {
                get { return new ComponentResourceKey(typeof(SplitButtonResources), "vistaSplitButtonStyle"); }
            }
        }
    }

    自己做的style:

    Easy5SplitButtonSplitButtonColor.xaml

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:s="clr-namespace:Wpf.Controls;assembly=Wpf.SplitButton"
        xmlns:aero="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero">
    
        <Style x:Key="easy5SplitButton"
                   TargetType="s:SplitButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="s:SplitButton">
                        <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="16"/>
                            </Grid.ColumnDefinitions>
    
                            <Border x:Name="Bd" Padding="2,3"
                                    HorizontalAlignment="Stretch" 
                                    VerticalAlignment="Stretch"
                                    BorderBrush="Black"
                                    Background="Red">
                                <ContentPresenter>
    
                                </ContentPresenter>
                            </Border>
    
                            <Path x:Name="path"
                                Data="M0,0L3,3 6,0z" 
                                Margin="0,1,0,0" 
                                Grid.Column="1"
                                Stroke="{TemplateBinding Foreground}" 
                                Fill="{TemplateBinding Foreground}" 
                                HorizontalAlignment="Center" 
                                VerticalAlignment="Center"/>
    
                            <Button x:Name="PART_DropDown" 
                                        Grid.Column="1"
                                        Margin="0">
                                <Path Data="M0,0L3,3 6,0z" 
                                          Margin="0,1,0,0" 
                                          Grid.Column="1"
                                          Stroke="{TemplateBinding Foreground}" 
                                          Fill="{TemplateBinding Foreground}" 
                                          HorizontalAlignment="Center" 
                                          VerticalAlignment="Center"/>
                            </Button>
    
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ResourceDictionary>
    View Code

    说明的问题:

    1.用的CustomerControl自定义控件,而不是UserControl自定义控件。

    CustomerControl自定义控件,这种灵活了,不依赖xaml,如果依赖xaml,依照惯例,得标出 :

    CustomerControl自定义控件依赖样式中的控件,命名惯例是加上前缀PART_

    并在定义控件的时使用标签属性注明:

        [TemplatePart(Name = "PART_DropDown", Type = typeof (Button))]
        public class SplitButton : Button

    代码如下:
        /// <summary>
        /// Implemetation of a Split Button
        /// </summary>
        [TemplatePart(Name = "PART_DropDown", Type = typeof (Button))]
        public class SplitButton : Button
        {
    ...
    }

    xaml中的样式中必须提供名为:PART_DropDown的类型为Button的控件,否则,该自定义控件部分功能将肯能有所丢失。

    例如:

     1     <Style x:Key="easy5SplitButton"
     2                TargetType="s:SplitButton">
     3         <Setter Property="Template">
     4             <Setter.Value>
     5                 <ControlTemplate TargetType="s:SplitButton">
     6 。。。。。。。。。
     7 
     8                         <Button x:Name="PART_DropDown" 
     9                                     Grid.Column="1"
    10                                     Margin="0">
    11                             <Path Data="M0,0L3,3 6,0z" 
    12                                       Margin="0,1,0,0" 
    13                                       Grid.Column="1"
    14                                       Stroke="{TemplateBinding Foreground}" 
    15                                       Fill="{TemplateBinding Foreground}" 
    16                                       HorizontalAlignment="Center" 
    17                                       VerticalAlignment="Center"/>
    18                         </Button>
    19 
    20                     </Grid>
    21                 </ControlTemplate>
    22             </Setter.Value>
    23         </Setter>
    24     </Style>

    2.

        <Style x:Key="easy5SplitButton"
                   TargetType
    ="s:SplitButton">

    。。。。

                            <Border x:Name="Bd" Padding="2,3"
                                    HorizontalAlignment="Stretch"
                                    VerticalAlignment="Stretch"
                                    BorderBrush="Black"
                                    Background="Red">
                                <ContentPresenter>

                                </ContentPresenter>
                            </Border>

    样式    <Style x:Key="easy5SplitButton"
                   TargetType
    ="s:SplitButton">中的

     <ContentPresenter/>等价于:<ContentPresenter Content="{TemplateBinding Content}">

    (实验证明:Button类型中的ControlTemplate 包含

                                </ContentPresenter/>

    ContentPresenter的Content属性自动绑定到模板所在的父Button的Content,

    其他控件没实验过。

    3.当单击到SplitButton中的PART_DropDown按钮时,由于:

            /// <summary>
            /// OnApplyTemplate override, set up the click event for the dropdown if present in the template
            /// </summary>
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();

                // set up the click event handler for the dropdown button
                ButtonBase dropDown = this.Template.FindName("PART_DropDown", this) as ButtonBase;
                if (dropDown != null)
                    dropDown.Click += Dropdown_Click;
            }

    所以引发:

            private void Dropdown_Click(object sender, RoutedEventArgs e)
            {
                OnDropdown();
                e.Handled = true;
            }
            private void OnDropdown()
            {
                EnsureContextMenuIsValid();
                if (!this.ContextMenu.HasItems)
                    return;
    
                this.ContextMenu.IsOpen = !IsContextMenuOpen; // open it if closed, close it if open
            }

    如果没有,即dropDown == null

    1             // set up the click event handler for the dropdown button
    2             ButtonBase dropDown = this.Template.FindName("PART_DropDown", this) as ButtonBase;
    3             if (dropDown != null)
    4                 dropDown.Click += Dropdown_Click;

    会由于路由冒泡到PART_DropDown按钮的父控件SplitButton,被捕捉从而引发Click事件。

    但是不会弹出下拉菜单,功能会缺失,即上面已经提到的:

    CustomerControl自定义控件,这种灵活了,不依赖xaml,如果依赖xaml,得标出:

        /// <summary>
        /// Implemetation of a Split Button
        /// </summary>
        [TemplatePart(Name = "PART_DropDown", Type = typeof (Button))]
        public class SplitButton : Button
        {
     1     <Style x:Key="easy5SplitButton"
     2                TargetType="s:SplitButton">
     3         <Setter Property="Template">
     4             <Setter.Value>
     5                 <ControlTemplate TargetType="s:SplitButton">
     6 。。。。。。。。。
     7 
     8                         <Button x:Name="PART_DropDown" 
     9                                     Grid.Column="1"
    10                                     Margin="0">
    11                             <Path Data="M0,0L3,3 6,0z" 
    12                                       Margin="0,1,0,0" 
    13                                       Grid.Column="1"
    14                                       Stroke="{TemplateBinding Foreground}" 
    15                                       Fill="{TemplateBinding Foreground}" 
    16                                       HorizontalAlignment="Center" 
    17                                       VerticalAlignment="Center"/>
    18                         </Button>
    19 
    20                     </Grid>
    21                 </ControlTemplate>
    22             </Setter.Value>
    23         </Setter>
    24     </Style>

    xaml中的样式中必须提供名为:PART_DropDown的类型为Button的控件,否则,该自定义控件部分功能将肯能有所丢失。

  • 相关阅读:
    为什么已经设置了更多的远程连接授权,同一账户登陆的时候还会被踢掉?
    如何添加并设置远程桌面(RD)授权服务器
    如何在Windows Server 2008 上添加RD (远程桌面)会话主机配置的远程桌面授权服务器
    DB2 Enterprise Server Edition(DB2 ESE)9.1在Windows Server 2008 下出现无法新建数据库的情况,及解决办法
    在非SQL客户端使用命令行方式定期连接SQL Server 服务器并模拟用户查询操作,同时输出信息内容
    相机模型2
    Ubuntu下实用的录屏软件--Kazam
    2d lidar 与相机
    linux 串口查询设置修改
    Eigen中 Isometry3d、matrix的Identity()
  • 原文地址:https://www.cnblogs.com/easy5weikai/p/3143423.html
Copyright © 2011-2022 走看看