4 changed files with 375 additions and 1 deletions
@ -0,0 +1,287 @@ |
|||||
|
using System; |
||||
|
using System.Windows; |
||||
|
using System.Windows.Controls; |
||||
|
using System.Windows.Input; |
||||
|
using System.ComponentModel; |
||||
|
using System.Windows.Controls.Primitives; |
||||
|
|
||||
|
namespace Microsoft.Windows.Controls |
||||
|
{ |
||||
|
public class DropDownButton : ContentControl, ICommandSource |
||||
|
{ |
||||
|
#region Members
|
||||
|
|
||||
|
ToggleButton _dropDownButton; |
||||
|
|
||||
|
#endregion //Members
|
||||
|
|
||||
|
#region Constructors
|
||||
|
|
||||
|
static DropDownButton() |
||||
|
{ |
||||
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton))); |
||||
|
} |
||||
|
|
||||
|
public DropDownButton() |
||||
|
{ |
||||
|
Keyboard.AddKeyDownHandler(this, OnKeyDown); |
||||
|
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler(this, OnMouseDownOutsideCapturedElement); |
||||
|
} |
||||
|
|
||||
|
#endregion //Constructors
|
||||
|
|
||||
|
#region Properties
|
||||
|
|
||||
|
#region DropDownContent
|
||||
|
|
||||
|
public static readonly DependencyProperty DropDownContentProperty = DependencyProperty.Register("DropDownContent", typeof(object), typeof(DropDownButton), new UIPropertyMetadata(null, OnDropDownContentChanged)); |
||||
|
public object DropDownContent |
||||
|
{ |
||||
|
get { return (object)GetValue(DropDownContentProperty); } |
||||
|
set { SetValue(DropDownContentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private static void OnDropDownContentChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
DropDownButton dropDownButton = o as DropDownButton; |
||||
|
if (dropDownButton != null) |
||||
|
dropDownButton.OnDropDownContentChanged((object)e.OldValue, (object)e.NewValue); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnDropDownContentChanged(object oldValue, object newValue) |
||||
|
{ |
||||
|
// TODO: Add your property changed side-effects. Descendants can override as well.
|
||||
|
} |
||||
|
|
||||
|
#endregion //DropDownContent
|
||||
|
|
||||
|
#region IsOpen
|
||||
|
|
||||
|
public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(DropDownButton), new UIPropertyMetadata(false, OnIsOpenChanged)); |
||||
|
public bool IsOpen |
||||
|
{ |
||||
|
get { return (bool)GetValue(IsOpenProperty); } |
||||
|
set { SetValue(IsOpenProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private static void OnIsOpenChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
DropDownButton dropDownButton = o as DropDownButton; |
||||
|
if (dropDownButton != null) |
||||
|
dropDownButton.OnIsOpenChanged((bool)e.OldValue, (bool)e.NewValue); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnIsOpenChanged(bool oldValue, bool newValue) |
||||
|
{ |
||||
|
if (newValue) |
||||
|
RaiseRoutedEvent(DropDownButton.OpenedEvent); |
||||
|
else |
||||
|
RaiseRoutedEvent(DropDownButton.ClosedEvent); |
||||
|
} |
||||
|
|
||||
|
#endregion //IsOpen
|
||||
|
|
||||
|
#endregion //Properties
|
||||
|
|
||||
|
#region Base Class Overrides
|
||||
|
|
||||
|
public override void OnApplyTemplate() |
||||
|
{ |
||||
|
base.OnApplyTemplate(); |
||||
|
|
||||
|
if (_dropDownButton != null) |
||||
|
_dropDownButton.Click -= DropDownButton_Click; |
||||
|
|
||||
|
_dropDownButton = GetTemplateChild("PART_DropDownButton") as ToggleButton; |
||||
|
|
||||
|
if (_dropDownButton != null) |
||||
|
_dropDownButton.Click += DropDownButton_Click; |
||||
|
} |
||||
|
|
||||
|
#endregion //Base Class Overrides
|
||||
|
|
||||
|
#region Events
|
||||
|
|
||||
|
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DropDownButton)); |
||||
|
public event RoutedEventHandler Click |
||||
|
{ |
||||
|
add { AddHandler(ClickEvent, value); } |
||||
|
remove { RemoveHandler(ClickEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public static readonly RoutedEvent OpenedEvent = EventManager.RegisterRoutedEvent("Opened", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DropDownButton)); |
||||
|
public event RoutedEventHandler Opened |
||||
|
{ |
||||
|
add { AddHandler(OpenedEvent, value); } |
||||
|
remove { RemoveHandler(OpenedEvent, value); } |
||||
|
} |
||||
|
|
||||
|
public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(DropDownButton)); |
||||
|
public event RoutedEventHandler Closed |
||||
|
{ |
||||
|
add { AddHandler(ClosedEvent, value); } |
||||
|
remove { RemoveHandler(ClosedEvent, value); } |
||||
|
} |
||||
|
|
||||
|
#endregion //Events
|
||||
|
|
||||
|
#region Event Handlers
|
||||
|
|
||||
|
private void OnKeyDown(object sender, KeyEventArgs e) |
||||
|
{ |
||||
|
switch (e.Key) |
||||
|
{ |
||||
|
case Key.Escape: |
||||
|
{ |
||||
|
CloseDropDown(); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnMouseDownOutsideCapturedElement(object sender, MouseButtonEventArgs e) |
||||
|
{ |
||||
|
CloseDropDown(); |
||||
|
} |
||||
|
|
||||
|
private void DropDownButton_Click(object sender, RoutedEventArgs e) |
||||
|
{ |
||||
|
OnClick(); |
||||
|
} |
||||
|
|
||||
|
void CanExecuteChanged(object sender, EventArgs e) |
||||
|
{ |
||||
|
if (Command != null) |
||||
|
{ |
||||
|
RoutedCommand command = Command as RoutedCommand; |
||||
|
|
||||
|
// If a RoutedCommand.
|
||||
|
if (command != null) |
||||
|
IsEnabled = command.CanExecute(CommandParameter, CommandTarget) ? true : false; |
||||
|
// If a not RoutedCommand.
|
||||
|
else |
||||
|
IsEnabled = Command.CanExecute(CommandParameter) ? true : false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endregion //Event Handlers
|
||||
|
|
||||
|
#region Methods
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Closes the drop down.
|
||||
|
/// </summary>
|
||||
|
private void CloseDropDown() |
||||
|
{ |
||||
|
if (IsOpen) |
||||
|
IsOpen = false; |
||||
|
ReleaseMouseCapture(); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnClick() |
||||
|
{ |
||||
|
RaiseRoutedEvent(DropDownButton.ClickEvent); |
||||
|
RaiseCommand(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises routed events.
|
||||
|
/// </summary>
|
||||
|
private void RaiseRoutedEvent(RoutedEvent routedEvent) |
||||
|
{ |
||||
|
RoutedEventArgs args = new RoutedEventArgs(routedEvent, this); |
||||
|
RaiseEvent(args); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Raises the command's Execute event.
|
||||
|
/// </summary>
|
||||
|
private void RaiseCommand() |
||||
|
{ |
||||
|
if (Command != null) |
||||
|
{ |
||||
|
RoutedCommand routedCommand = Command as RoutedCommand; |
||||
|
|
||||
|
if (routedCommand == null) |
||||
|
((ICommand)Command).Execute(CommandParameter); |
||||
|
else |
||||
|
routedCommand.Execute(CommandParameter, CommandTarget); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Unhooks a command from the Command property.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldCommand">The old command.</param>
|
||||
|
/// <param name="newCommand">The new command.</param>
|
||||
|
private void UnhookCommand(ICommand oldCommand, ICommand newCommand) |
||||
|
{ |
||||
|
EventHandler handler = CanExecuteChanged; |
||||
|
oldCommand.CanExecuteChanged -= handler; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Hooks up a command to the CanExecuteChnaged event handler.
|
||||
|
/// </summary>
|
||||
|
/// <param name="oldCommand">The old command.</param>
|
||||
|
/// <param name="newCommand">The new command.</param>
|
||||
|
private void HookUpCommand(ICommand oldCommand, ICommand newCommand) |
||||
|
{ |
||||
|
EventHandler handler = new EventHandler(CanExecuteChanged); |
||||
|
canExecuteChangedHandler = handler; |
||||
|
if (newCommand != null) |
||||
|
newCommand.CanExecuteChanged += canExecuteChangedHandler; |
||||
|
} |
||||
|
|
||||
|
#endregion //Methods
|
||||
|
|
||||
|
#region ICommandSource Members
|
||||
|
|
||||
|
// Keeps a copy of the CanExecuteChnaged handler so it doesn't get garbage collected.
|
||||
|
private EventHandler canExecuteChangedHandler; |
||||
|
|
||||
|
#region Command
|
||||
|
|
||||
|
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(DropDownButton), new PropertyMetadata((ICommand)null, OnCommandChanged)); |
||||
|
[TypeConverter(typeof(CommandConverter))] |
||||
|
public ICommand Command |
||||
|
{ |
||||
|
get { return (ICommand)GetValue(CommandProperty); } |
||||
|
set { SetValue(CommandProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
DropDownButton dropDownButton = d as DropDownButton; |
||||
|
if (dropDownButton != null) |
||||
|
dropDownButton.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue); |
||||
|
} |
||||
|
|
||||
|
protected virtual void OnCommandChanged(ICommand oldValue, ICommand newValue) |
||||
|
{ |
||||
|
// If old command is not null, then we need to remove the handlers.
|
||||
|
if (oldValue != null) |
||||
|
UnhookCommand(oldValue, newValue); |
||||
|
|
||||
|
HookUpCommand(oldValue, newValue); |
||||
|
} |
||||
|
|
||||
|
#endregion //Command
|
||||
|
|
||||
|
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(DropDownButton), new PropertyMetadata(null)); |
||||
|
public object CommandParameter |
||||
|
{ |
||||
|
get { return GetValue(CommandParameterProperty); } |
||||
|
set { SetValue(CommandParameterProperty, value); } |
||||
|
} |
||||
|
|
||||
|
public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(DropDownButton), new PropertyMetadata(null)); |
||||
|
public IInputElement CommandTarget |
||||
|
{ |
||||
|
get { return (IInputElement)GetValue(CommandTargetProperty); } |
||||
|
set { SetValue(CommandTargetProperty, value); } |
||||
|
} |
||||
|
|
||||
|
#endregion //ICommandSource Members
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,81 @@ |
|||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:chrome="clr-namespace:Microsoft.Windows.Controls.Chromes" |
||||
|
xmlns:coreConverters="clr-namespace:Microsoft.Windows.Controls.Core.Converters" |
||||
|
xmlns:local="clr-namespace:Microsoft.Windows.Controls" > |
||||
|
|
||||
|
<coreConverters:InverseBoolConverter x:Key="InverseBoolConverter" /> |
||||
|
|
||||
|
<LinearGradientBrush x:Key="PopupDarkBorderBrush" EndPoint="0.5,1" StartPoint="0.5,0"> |
||||
|
<GradientStop Color="#FFA3AEB9" Offset="0"/> |
||||
|
<GradientStop Color="#FF8399A9" Offset="0.375"/> |
||||
|
<GradientStop Color="#FF718597" Offset="0.375"/> |
||||
|
<GradientStop Color="#FF617584" Offset="1"/> |
||||
|
</LinearGradientBrush> |
||||
|
|
||||
|
<LinearGradientBrush x:Key="PopupBackgroundBrush" StartPoint="0,0" EndPoint="0,1"> |
||||
|
<LinearGradientBrush.GradientStops> |
||||
|
<GradientStopCollection> |
||||
|
<GradientStop Offset="0" Color="#FFffffff"/> |
||||
|
<GradientStop Offset="1" Color="#FFE8EBED"/> |
||||
|
</GradientStopCollection> |
||||
|
</LinearGradientBrush.GradientStops> |
||||
|
</LinearGradientBrush> |
||||
|
|
||||
|
<Style TargetType="{x:Type local:DropDownButton}"> |
||||
|
<Setter Property="BorderThickness" Value="1"/> |
||||
|
<Setter Property="IsTabStop" Value="False" /> |
||||
|
<Setter Property="HorizontalContentAlignment" Value="Center" /> |
||||
|
<Setter Property="VerticalContentAlignment" Value="Center" /> |
||||
|
<Setter Property="Padding" Value="3"/> |
||||
|
<Setter Property="Template"> |
||||
|
<Setter.Value> |
||||
|
<ControlTemplate TargetType="{x:Type local:DropDownButton}"> |
||||
|
<Grid x:Name="MainGrid" SnapsToDevicePixels="True"> |
||||
|
<ToggleButton x:Name="PART_DropDownButton" Grid.Column="1" IsTabStop="False" |
||||
|
IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" |
||||
|
IsHitTestVisible="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource InverseBoolConverter}}" > |
||||
|
<ToggleButton.Template> |
||||
|
<ControlTemplate TargetType="ToggleButton"> |
||||
|
<ContentPresenter /> |
||||
|
</ControlTemplate> |
||||
|
</ToggleButton.Template> |
||||
|
<Grid> |
||||
|
<chrome:ButtonChrome x:Name="ToggleButtonChrome" |
||||
|
RenderChecked="{TemplateBinding IsOpen}" |
||||
|
RenderEnabled="{TemplateBinding IsEnabled}" |
||||
|
RenderMouseOver="{Binding IsMouseOver, ElementName=PART_DropDownButton}" |
||||
|
RenderPressed="{Binding IsPressed, ElementName=PART_DropDownButton}" /> |
||||
|
<Grid> |
||||
|
<Grid.ColumnDefinitions> |
||||
|
<ColumnDefinition Width="*" /> |
||||
|
<ColumnDefinition Width="Auto" /> |
||||
|
</Grid.ColumnDefinitions> |
||||
|
<ContentPresenter Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="true" /> |
||||
|
<Grid x:Name="arrowGlyph" IsHitTestVisible="False" Margin="4,3,4,3" Grid.Column="1"> |
||||
|
<Path Width="7" Height="4" Data="M 0,1 C0,1 0,0 0,0 0,0 3,0 3,0 3,0 3,1 3,1 3,1 4,1 4,1 4,1 4,0 4,0 4,0 7,0 7,0 7,0 7,1 7,1 7,1 6,1 6,1 6,1 6,2 6,2 6,2 5,2 5,2 5,2 5,3 5,3 5,3 4,3 4,3 4,3 4,4 4,4 4,4 3,4 3,4 3,4 3,3 3,3 3,3 2,3 2,3 2,3 2,2 2,2 2,2 1,2 1,2 1,2 1,1 1,1 1,1 0,1 0,1 z" Fill="#FF000000"/> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</ToggleButton> |
||||
|
|
||||
|
<Popup x:Name="PART_Popup" |
||||
|
HorizontalOffset="1" |
||||
|
VerticalOffset="1" |
||||
|
AllowsTransparency="True" |
||||
|
StaysOpen="False" |
||||
|
Placement="Bottom" |
||||
|
Focusable="False" |
||||
|
IsOpen="{Binding IsChecked, ElementName=PART_DropDownButton}"> |
||||
|
<Border BorderThickness="1" Background="{StaticResource PopupBackgroundBrush}" BorderBrush="{StaticResource PopupDarkBorderBrush}" > |
||||
|
<ContentPresenter Content="{TemplateBinding DropDownContent}" /> |
||||
|
</Border> |
||||
|
</Popup> |
||||
|
|
||||
|
</Grid> |
||||
|
</ControlTemplate> |
||||
|
</Setter.Value> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
</ResourceDictionary> |
||||
Loading…
Reference in new issue