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