diff --git a/src/MahApps.Metro/Controls/DropDownButton.cs b/src/MahApps.Metro/Controls/DropDownButton.cs
index 753da55556..8e26697912 100644
--- a/src/MahApps.Metro/Controls/DropDownButton.cs
+++ b/src/MahApps.Metro/Controls/DropDownButton.cs
@@ -9,11 +9,13 @@
namespace MahApps.Metro.Controls
{
- [ContentProperty("ItemsSource")]
+ [ContentProperty(nameof(ItemsSource))]
[TemplatePart(Name = "PART_Button", Type = typeof(Button))]
[TemplatePart(Name = "PART_ButtonContent", Type = typeof(ContentControl))]
[TemplatePart(Name = "PART_Menu", Type = typeof(ContextMenu))]
- public class DropDownButton : ItemsControl
+ [StyleTypedProperty(Property = nameof(ButtonStyle), StyleTargetType = typeof(Button))]
+ [StyleTypedProperty(Property = nameof(MenuStyle), StyleTargetType = typeof(ContextMenu))]
+ public class DropDownButton : ItemsControl, ICommandSource
{
public static readonly RoutedEvent ClickEvent
= EventManager.RegisterRoutedEvent(nameof(Click),
@@ -23,29 +25,30 @@ public static readonly RoutedEvent ClickEvent
public event RoutedEventHandler Click
{
- add { this.AddHandler(ClickEvent, value); }
- remove { this.RemoveHandler(ClickEvent, value); }
+ add => this.AddHandler(ClickEvent, value);
+ remove => this.RemoveHandler(ClickEvent, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty IsExpandedProperty
- = DependencyProperty.Register(
- nameof(IsExpanded),
- typeof(bool),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsExpandedPropertyChangedCallback));
+ = DependencyProperty.Register(nameof(IsExpanded),
+ typeof(bool),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsExpandedPropertyChangedCallback));
- private static void IsExpandedPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
+ private static void OnIsExpandedPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
- DropDownButton dropDownButton = (DropDownButton)dependencyObject;
- dropDownButton.SetContextMenuPlacementTarget(dropDownButton._contextMenu);
+ if (dependencyObject is DropDownButton dropDownButton)
+ {
+ dropDownButton.SetContextMenuPlacementTarget(dropDownButton.contextMenu);
+ }
}
protected virtual void SetContextMenuPlacementTarget(ContextMenu contextMenu)
{
- if (this._clickButton != null)
+ if (this.button != null)
{
- contextMenu.PlacementTarget = this._clickButton;
+ contextMenu.PlacementTarget = this.button;
}
}
@@ -54,49 +57,46 @@ protected virtual void SetContextMenuPlacementTarget(ContextMenu contextMenu)
///
public bool IsExpanded
{
- get { return (bool)this.GetValue(IsExpandedProperty); }
- set { this.SetValue(IsExpandedProperty, value); }
+ get => (bool)this.GetValue(IsExpandedProperty);
+ set => this.SetValue(IsExpandedProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ExtraTagProperty
- = DependencyProperty.Register(
- nameof(ExtraTag),
- typeof(object),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(ExtraTag),
+ typeof(object),
+ typeof(DropDownButton));
///
/// Gets or sets an extra tag.
///
public object ExtraTag
{
- get { return this.GetValue(ExtraTagProperty); }
- set { this.SetValue(ExtraTagProperty, value); }
+ get => this.GetValue(ExtraTagProperty);
+ set => this.SetValue(ExtraTagProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty OrientationProperty
- = DependencyProperty.Register(
- nameof(Orientation),
- typeof(Orientation),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(Orientation),
+ typeof(Orientation),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the orientation of children stacking.
///
public Orientation Orientation
{
- get { return (Orientation)this.GetValue(OrientationProperty); }
- set { this.SetValue(OrientationProperty, value); }
+ get => (Orientation)this.GetValue(OrientationProperty);
+ set => this.SetValue(OrientationProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty IconProperty
- = DependencyProperty.Register(
- nameof(Icon),
- typeof(object),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(Icon),
+ typeof(object),
+ typeof(DropDownButton));
///
/// Gets or sets the content for the icon part.
@@ -104,16 +104,15 @@ public static readonly DependencyProperty IconProperty
[Bindable(true)]
public object Icon
{
- get { return this.GetValue(IconProperty); }
- set { this.SetValue(IconProperty, value); }
+ get => this.GetValue(IconProperty);
+ set => this.SetValue(IconProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty IconTemplateProperty
- = DependencyProperty.Register(
- nameof(IconTemplate),
- typeof(DataTemplate),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(IconTemplate),
+ typeof(DataTemplate),
+ typeof(DropDownButton));
///
/// Gets or sets the DataTemplate for the icon part.
@@ -121,81 +120,84 @@ public static readonly DependencyProperty IconTemplateProperty
[Bindable(true)]
public DataTemplate IconTemplate
{
- get { return (DataTemplate)this.GetValue(IconTemplateProperty); }
- set { this.SetValue(IconTemplateProperty, value); }
+ get => (DataTemplate)this.GetValue(IconTemplateProperty);
+ set => this.SetValue(IconTemplateProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandProperty
- = DependencyProperty.Register(
- nameof(Command),
- typeof(ICommand),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(Command),
+ typeof(ICommand),
+ typeof(DropDownButton),
+ new PropertyMetadata(null, OnCommandPropertyChangedCallback));
+
+ private static void OnCommandPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ (dependencyObject as DropDownButton)?.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
+ }
///
/// Gets or sets the command to invoke when the content button is pressed.
///
public ICommand Command
{
- get { return (ICommand)this.GetValue(CommandProperty); }
- set { this.SetValue(CommandProperty, value); }
+ get => (ICommand)this.GetValue(CommandProperty);
+ set => this.SetValue(CommandProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandTargetProperty
- = DependencyProperty.Register(
- nameof(CommandTarget),
- typeof(IInputElement),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(CommandTarget),
+ typeof(IInputElement),
+ typeof(DropDownButton),
+ new PropertyMetadata(null));
///
/// Gets or sets the element on which to raise the specified command.
///
public IInputElement CommandTarget
{
- get { return (IInputElement)this.GetValue(CommandTargetProperty); }
- set { this.SetValue(CommandTargetProperty, value); }
+ get => (IInputElement)this.GetValue(CommandTargetProperty);
+ set => this.SetValue(CommandTargetProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandParameterProperty
- = DependencyProperty.Register(
- nameof(CommandParameter),
- typeof(object),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(CommandParameter),
+ typeof(object),
+ typeof(DropDownButton),
+ new PropertyMetadata(null));
///
/// Gets or sets the parameter to pass to the command property.
///
public object CommandParameter
{
- get { return (object)this.GetValue(CommandParameterProperty); }
- set { this.SetValue(CommandParameterProperty, value); }
+ get => (object)this.GetValue(CommandParameterProperty);
+ set => this.SetValue(CommandParameterProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ContentProperty
- = DependencyProperty.Register(
- nameof(Content),
- typeof(object),
- typeof(DropDownButton));
+ = DependencyProperty.Register(nameof(Content),
+ typeof(object),
+ typeof(DropDownButton));
///
/// Gets or sets the content of this control.
///
public object Content
{
- get { return (object)this.GetValue(ContentProperty); }
- set { this.SetValue(ContentProperty, value); }
+ get => (object)this.GetValue(ContentProperty);
+ set => this.SetValue(ContentProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ContentTemplateProperty
- = DependencyProperty.Register(
- nameof(ContentTemplate),
- typeof(DataTemplate),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata((DataTemplate)null));
+ = DependencyProperty.Register(nameof(ContentTemplate),
+ typeof(DataTemplate),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata((DataTemplate)null));
///
/// Gets or sets the data template used to display the content of the DropDownButton.
@@ -203,17 +205,16 @@ public static readonly DependencyProperty ContentTemplateProperty
[Bindable(true)]
public DataTemplate ContentTemplate
{
- get { return (DataTemplate)this.GetValue(ContentTemplateProperty); }
- set { this.SetValue(ContentTemplateProperty, value); }
+ get => (DataTemplate)this.GetValue(ContentTemplateProperty);
+ set => this.SetValue(ContentTemplateProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ContentTemplateSelectorProperty
- = DependencyProperty.Register(
- nameof(ContentTemplateSelector),
- typeof(DataTemplateSelector),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata((DataTemplateSelector)null));
+ = DependencyProperty.Register(nameof(ContentTemplateSelector),
+ typeof(DataTemplateSelector),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata((DataTemplateSelector)null));
///
/// Gets or sets a template selector that enables an application writer to provide custom template-selection logic.
@@ -224,17 +225,16 @@ public static readonly DependencyProperty ContentTemplateSelectorProperty
[Bindable(true)]
public DataTemplateSelector ContentTemplateSelector
{
- get { return (DataTemplateSelector)this.GetValue(ContentTemplateSelectorProperty); }
- set { this.SetValue(ContentTemplateSelectorProperty, value); }
+ get => (DataTemplateSelector)this.GetValue(ContentTemplateSelectorProperty);
+ set => this.SetValue(ContentTemplateSelectorProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ContentStringFormatProperty
- = DependencyProperty.Register(
- nameof(ContentStringFormat),
- typeof(string),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata((string)null));
+ = DependencyProperty.Register(nameof(ContentStringFormat),
+ typeof(string),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata((string)null));
///
/// Gets or sets a composite string that specifies how to format the content property if it is displayed as a string.
@@ -245,110 +245,104 @@ public static readonly DependencyProperty ContentStringFormatProperty
[Bindable(true)]
public string ContentStringFormat
{
- get { return (string)this.GetValue(ContentStringFormatProperty); }
- set { this.SetValue(ContentStringFormatProperty, value); }
+ get => (string)this.GetValue(ContentStringFormatProperty);
+ set => this.SetValue(ContentStringFormatProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ButtonStyleProperty
- = DependencyProperty.Register(
- nameof(ButtonStyle),
- typeof(Style),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(ButtonStyle),
+ typeof(Style),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the button content style.
///
public Style ButtonStyle
{
- get { return (Style)this.GetValue(ButtonStyleProperty); }
- set { this.SetValue(ButtonStyleProperty, value); }
+ get => (Style)this.GetValue(ButtonStyleProperty);
+ set => this.SetValue(ButtonStyleProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty MenuStyleProperty
- = DependencyProperty.Register(
- nameof(MenuStyle),
- typeof(Style),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(MenuStyle),
+ typeof(Style),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the "popup" menu style.
///
public Style MenuStyle
{
- get { return (Style)this.GetValue(MenuStyleProperty); }
- set { this.SetValue(MenuStyleProperty, value); }
+ get => (Style)this.GetValue(MenuStyleProperty);
+ set => this.SetValue(MenuStyleProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowBrush),
- typeof(Brush),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowBrush),
+ typeof(Brush),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush for the button arrow icon.
///
public Brush ArrowBrush
{
- get { return (Brush)this.GetValue(ArrowBrushProperty); }
- set { this.SetValue(ArrowBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowBrushProperty);
+ set => this.SetValue(ArrowBrushProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowMouseOverBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowMouseOverBrush),
- typeof(Brush),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowMouseOverBrush),
+ typeof(Brush),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush of the button arrow icon if the mouse is over the drop down button.
///
public Brush ArrowMouseOverBrush
{
- get { return (Brush)this.GetValue(ArrowMouseOverBrushProperty); }
- set { this.SetValue(ArrowMouseOverBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowMouseOverBrushProperty);
+ set => this.SetValue(ArrowMouseOverBrushProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowPressedBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowPressedBrush),
- typeof(Brush),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowPressedBrush),
+ typeof(Brush),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush of the button arrow icon if the arrow button is pressed.
///
public Brush ArrowPressedBrush
{
- get { return (Brush)this.GetValue(ArrowPressedBrushProperty); }
- set { this.SetValue(ArrowPressedBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowPressedBrushProperty);
+ set => this.SetValue(ArrowPressedBrushProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowVisibilityProperty
- = DependencyProperty.Register(
- nameof(ArrowVisibility),
- typeof(Visibility),
- typeof(DropDownButton),
- new FrameworkPropertyMetadata(Visibility.Visible, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(ArrowVisibility),
+ typeof(Visibility),
+ typeof(DropDownButton),
+ new FrameworkPropertyMetadata(Visibility.Visible, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the visibility of the button arrow icon.
///
public Visibility ArrowVisibility
{
- get { return (Visibility)this.GetValue(ArrowVisibilityProperty); }
- set { this.SetValue(ArrowVisibilityProperty, value); }
+ get => (Visibility)this.GetValue(ArrowVisibilityProperty);
+ set => this.SetValue(ArrowVisibilityProperty, value);
}
static DropDownButton()
@@ -356,9 +350,66 @@ static DropDownButton()
DefaultStyleKeyProperty.OverrideMetadata(typeof(DropDownButton), new FrameworkPropertyMetadata(typeof(DropDownButton)));
}
+ private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
+ {
+ if (oldCommand != null)
+ {
+ this.UnhookCommand(oldCommand);
+ }
+
+ if (newCommand != null)
+ {
+ this.HookCommand(newCommand);
+ }
+ }
+
+ private void UnhookCommand(ICommand command)
+ {
+ CanExecuteChangedEventManager.RemoveHandler(command, this.OnCanExecuteChanged);
+ this.UpdateCanExecute();
+ }
+
+ private void HookCommand(ICommand command)
+ {
+ CanExecuteChangedEventManager.AddHandler(command, this.OnCanExecuteChanged);
+ this.UpdateCanExecute();
+ }
+
+ private void OnCanExecuteChanged(object sender, EventArgs e)
+ {
+ this.UpdateCanExecute();
+ }
+
+ private void UpdateCanExecute()
+ {
+ this.CanExecute = this.Command == null || CommandHelpers.CanExecuteCommandSource(this);
+ }
+
+ ///
+ protected override bool IsEnabledCore => base.IsEnabledCore && this.CanExecute;
+
+ private bool canExecute = true;
+
+ private bool CanExecute
+ {
+ get => this.canExecute;
+ set
+ {
+ if (value == this.canExecute)
+ {
+ return;
+ }
+
+ this.canExecute = value;
+ this.CoerceValue(IsEnabledProperty);
+ }
+ }
+
private void ButtonClick(object sender, RoutedEventArgs e)
{
- if (this._contextMenu?.HasItems == true)
+ CommandHelpers.ExecuteCommandSource(this);
+
+ if (this.contextMenu?.HasItems == true)
{
this.SetCurrentValue(IsExpandedProperty, true);
}
@@ -371,27 +422,25 @@ public override void OnApplyTemplate()
{
base.OnApplyTemplate();
- if (this._clickButton != null)
+ if (this.button != null)
{
- this._clickButton.Click -= this.ButtonClick;
- this._clickButton.IsEnabledChanged -= this.ButtonIsEnabledChanged;
+ this.button.Click -= this.ButtonClick;
}
- this._clickButton = this.GetTemplateChild("PART_Button") as Button;
- if (this._clickButton != null)
+ this.button = this.GetTemplateChild("PART_Button") as Button;
+ if (this.button != null)
{
- this._clickButton.Click += this.ButtonClick;
- this._clickButton.IsEnabledChanged += this.ButtonIsEnabledChanged;
+ this.button.Click += this.ButtonClick;
}
- this._contextMenu = this.GetTemplateChild("PART_Menu") as ContextMenu;
+ this.contextMenu = this.GetTemplateChild("PART_Menu") as ContextMenu;
- if (this._contextMenu != null && this.Items != null && this.ItemsSource == null)
+ if (this.contextMenu != null && this.Items != null && this.ItemsSource == null)
{
foreach (var newItem in this.Items)
{
this.TryRemoveVisualFromOldTree(newItem);
- this._contextMenu.Items.Add(newItem);
+ this.contextMenu.Items.Add(newItem);
}
}
}
@@ -403,11 +452,6 @@ protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
e.Handled = true;
}
- private void ButtonIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- this.SetCurrentValue(IsEnabledProperty, e.NewValue);
- }
-
private void TryRemoveVisualFromOldTree(object item)
{
if (item is Visual visual)
@@ -426,7 +470,7 @@ private void TryRemoveVisualFromOldTree(object item)
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
- if (this._contextMenu == null || this.ItemsSource != null || this._contextMenu.ItemsSource != null)
+ if (this.contextMenu == null || this.ItemsSource != null || this.contextMenu.ItemsSource != null)
{
return;
}
@@ -439,7 +483,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
foreach (var newItem in e.NewItems)
{
this.TryRemoveVisualFromOldTree(newItem);
- this._contextMenu.Items.Add(newItem);
+ this.contextMenu.Items.Add(newItem);
}
}
@@ -449,7 +493,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
foreach (var oldItem in e.OldItems)
{
- this._contextMenu.Items.Remove(oldItem);
+ this.contextMenu.Items.Remove(oldItem);
}
}
@@ -460,7 +504,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
foreach (var oldItem in e.OldItems)
{
- this._contextMenu.Items.Remove(oldItem);
+ this.contextMenu.Items.Remove(oldItem);
}
}
@@ -469,7 +513,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
foreach (var newItem in e.NewItems)
{
this.TryRemoveVisualFromOldTree(newItem);
- this._contextMenu.Items.Add(newItem);
+ this.contextMenu.Items.Add(newItem);
}
}
@@ -477,11 +521,11 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
case NotifyCollectionChangedAction.Reset:
if (this.Items != null)
{
- this._contextMenu.Items.Clear();
+ this.contextMenu.Items.Clear();
foreach (var newItem in this.Items)
{
this.TryRemoveVisualFromOldTree(newItem);
- this._contextMenu.Items.Add(newItem);
+ this.contextMenu.Items.Add(newItem);
}
}
@@ -491,7 +535,7 @@ protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
}
}
- private Button _clickButton;
- private ContextMenu _contextMenu;
+ private Button button;
+ private ContextMenu contextMenu;
}
}
\ No newline at end of file
diff --git a/src/MahApps.Metro/Controls/SplitButton.cs b/src/MahApps.Metro/Controls/SplitButton.cs
index 94827c7b23..0990e555f7 100644
--- a/src/MahApps.Metro/Controls/SplitButton.cs
+++ b/src/MahApps.Metro/Controls/SplitButton.cs
@@ -1,4 +1,5 @@
-using System.ComponentModel;
+using System;
+using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
@@ -8,14 +9,17 @@
namespace MahApps.Metro.Controls
{
- [ContentProperty("ItemsSource")]
+ [ContentProperty(nameof(ItemsSource))]
[TemplatePart(Name = "PART_Container", Type = typeof(Grid))]
[TemplatePart(Name = "PART_Button", Type = typeof(Button))]
[TemplatePart(Name = "PART_ButtonContent", Type = typeof(ContentControl))]
[TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
[TemplatePart(Name = "PART_Expander", Type = typeof(Button))]
- public class SplitButton : ComboBox
+ [StyleTypedProperty(Property = nameof(ButtonStyle), StyleTargetType = typeof(Button))]
+ [StyleTypedProperty(Property = nameof(ButtonArrowStyle), StyleTargetType = typeof(Button))]
+ public class SplitButton : ComboBox, ICommandSource
{
+ /// Identifies the routed event.
public static readonly RoutedEvent ClickEvent
= EventManager.RegisterRoutedEvent(nameof(Click),
RoutingStrategy.Bubble,
@@ -24,49 +28,46 @@ public static readonly RoutedEvent ClickEvent
public event RoutedEventHandler Click
{
- add { this.AddHandler(ClickEvent, value); }
- remove { this.RemoveHandler(ClickEvent, value); }
+ add => this.AddHandler(ClickEvent, value);
+ remove => this.RemoveHandler(ClickEvent, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ExtraTagProperty
- = DependencyProperty.Register(
- nameof(ExtraTag),
- typeof(object),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(ExtraTag),
+ typeof(object),
+ typeof(SplitButton));
///
/// Gets or sets an extra tag.
///
public object ExtraTag
{
- get { return this.GetValue(ExtraTagProperty); }
- set { this.SetValue(ExtraTagProperty, value); }
+ get => this.GetValue(ExtraTagProperty);
+ set => this.SetValue(ExtraTagProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty OrientationProperty
- = DependencyProperty.Register(
- nameof(Orientation),
- typeof(Orientation),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(Orientation),
+ typeof(Orientation),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the orientation of children stacking.
///
public Orientation Orientation
{
- get { return (Orientation)this.GetValue(OrientationProperty); }
- set { this.SetValue(OrientationProperty, value); }
+ get => (Orientation)this.GetValue(OrientationProperty);
+ set => this.SetValue(OrientationProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty IconProperty
- = DependencyProperty.Register(
- nameof(Icon),
- typeof(object),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(Icon),
+ typeof(object),
+ typeof(SplitButton));
///
/// Gets or sets the content for the icon part.
@@ -74,16 +75,15 @@ public static readonly DependencyProperty IconProperty
[Bindable(true)]
public object Icon
{
- get { return this.GetValue(IconProperty); }
- set { this.SetValue(IconProperty, value); }
+ get => this.GetValue(IconProperty);
+ set => this.SetValue(IconProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty IconTemplateProperty
- = DependencyProperty.Register(
- nameof(IconTemplate),
- typeof(DataTemplate),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(IconTemplate),
+ typeof(DataTemplate),
+ typeof(SplitButton));
///
/// Gets or sets the DataTemplate for the icon part.
@@ -91,151 +91,206 @@ public static readonly DependencyProperty IconTemplateProperty
[Bindable(true)]
public DataTemplate IconTemplate
{
- get { return (DataTemplate)this.GetValue(IconTemplateProperty); }
- set { this.SetValue(IconTemplateProperty, value); }
+ get => (DataTemplate)this.GetValue(IconTemplateProperty);
+ set => this.SetValue(IconTemplateProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandProperty
- = DependencyProperty.Register(
- nameof(Command),
- typeof(ICommand),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(Command),
+ typeof(ICommand),
+ typeof(SplitButton),
+ new PropertyMetadata(null, OnCommandPropertyChangedCallback));
+
+ private static void OnCommandPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
+ {
+ (dependencyObject as SplitButton)?.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
+ }
///
/// Gets or sets the command to invoke when the content button is pressed.
///
public ICommand Command
{
- get { return (ICommand)this.GetValue(CommandProperty); }
- set { this.SetValue(CommandProperty, value); }
+ get => (ICommand)this.GetValue(CommandProperty);
+ set => this.SetValue(CommandProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandTargetProperty
- = DependencyProperty.Register(
- nameof(CommandTarget),
- typeof(IInputElement),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(CommandTarget),
+ typeof(IInputElement),
+ typeof(SplitButton),
+ new PropertyMetadata(null));
///
/// Gets or sets the element on which to raise the specified command.
///
public IInputElement CommandTarget
{
- get { return (IInputElement)this.GetValue(CommandTargetProperty); }
- set { this.SetValue(CommandTargetProperty, value); }
+ get => (IInputElement)this.GetValue(CommandTargetProperty);
+ set => this.SetValue(CommandTargetProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty CommandParameterProperty
- = DependencyProperty.Register(
- nameof(CommandParameter),
- typeof(object),
- typeof(SplitButton));
+ = DependencyProperty.Register(nameof(CommandParameter),
+ typeof(object),
+ typeof(SplitButton),
+ new PropertyMetadata(null));
///
/// Gets or sets the parameter to pass to the command property.
///
public object CommandParameter
{
- get { return this.GetValue(CommandParameterProperty); }
- set { this.SetValue(CommandParameterProperty, value); }
+ get => this.GetValue(CommandParameterProperty);
+ set => this.SetValue(CommandParameterProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ButtonStyleProperty
- = DependencyProperty.Register(
- nameof(ButtonStyle),
- typeof(Style),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(ButtonStyle),
+ typeof(Style),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the button content style.
///
public Style ButtonStyle
{
- get { return (Style)this.GetValue(ButtonStyleProperty); }
- set { this.SetValue(ButtonStyleProperty, value); }
+ get => (Style)this.GetValue(ButtonStyleProperty);
+ set => this.SetValue(ButtonStyleProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ButtonArrowStyleProperty
- = DependencyProperty.Register(
- nameof(ButtonArrowStyle),
- typeof(Style),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
+ = DependencyProperty.Register(nameof(ButtonArrowStyle),
+ typeof(Style),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(default(Style), FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure));
///
/// Gets or sets the button arrow style.
///
public Style ButtonArrowStyle
{
- get { return (Style)this.GetValue(ButtonArrowStyleProperty); }
- set { this.SetValue(ButtonArrowStyleProperty, value); }
+ get => (Style)this.GetValue(ButtonArrowStyleProperty);
+ set => this.SetValue(ButtonArrowStyleProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowBrush),
- typeof(Brush),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowBrush),
+ typeof(Brush),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush for the button arrow icon.
///
public Brush ArrowBrush
{
- get { return (Brush)this.GetValue(ArrowBrushProperty); }
- set { this.SetValue(ArrowBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowBrushProperty);
+ set => this.SetValue(ArrowBrushProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowMouseOverBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowMouseOverBrush),
- typeof(Brush),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowMouseOverBrush),
+ typeof(Brush),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush of the button arrow icon if the mouse is over the split button.
///
public Brush ArrowMouseOverBrush
{
- get { return (Brush)this.GetValue(ArrowMouseOverBrushProperty); }
- set { this.SetValue(ArrowMouseOverBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowMouseOverBrushProperty);
+ set => this.SetValue(ArrowMouseOverBrushProperty, value);
}
/// Identifies the dependency property.
public static readonly DependencyProperty ArrowPressedBrushProperty
- = DependencyProperty.Register(
- nameof(ArrowPressedBrush),
- typeof(Brush),
- typeof(SplitButton),
- new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
+ = DependencyProperty.Register(nameof(ArrowPressedBrush),
+ typeof(Brush),
+ typeof(SplitButton),
+ new FrameworkPropertyMetadata(default(Brush), FrameworkPropertyMetadataOptions.AffectsRender));
///
/// Gets or sets the foreground brush of the button arrow icon if the arrow button is pressed.
///
public Brush ArrowPressedBrush
{
- get { return (Brush)this.GetValue(ArrowPressedBrushProperty); }
- set { this.SetValue(ArrowPressedBrushProperty, value); }
+ get => (Brush)this.GetValue(ArrowPressedBrushProperty);
+ set => this.SetValue(ArrowPressedBrushProperty, value);
}
static SplitButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(typeof(SplitButton)));
- IsEditableProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(false, null, new CoerceValueCallback(CoerceIsEnabledProperty)));
+ IsEditableProperty.OverrideMetadata(typeof(SplitButton), new FrameworkPropertyMetadata(false, null, CoerceIsEditableProperty));
+ }
+
+ private void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
+ {
+ if (oldCommand != null)
+ {
+ this.UnhookCommand(oldCommand);
+ }
+
+ if (newCommand != null)
+ {
+ this.HookCommand(newCommand);
+ }
+ }
+
+ private void UnhookCommand(ICommand command)
+ {
+ CanExecuteChangedEventManager.RemoveHandler(command, this.OnCanExecuteChanged);
+ this.UpdateCanExecute();
+ }
+
+ private void HookCommand(ICommand command)
+ {
+ CanExecuteChangedEventManager.AddHandler(command, this.OnCanExecuteChanged);
+ this.UpdateCanExecute();
+ }
+
+ private void OnCanExecuteChanged(object sender, EventArgs e)
+ {
+ this.UpdateCanExecute();
+ }
+
+ private void UpdateCanExecute()
+ {
+ this.CanExecute = this.Command == null || CommandHelpers.CanExecuteCommandSource(this);
+ }
+
+ ///
+ protected override bool IsEnabledCore => base.IsEnabledCore && this.CanExecute;
+
+ private bool canExecute = true;
+
+ private bool CanExecute
+ {
+ get => this.canExecute;
+ set
+ {
+ if (value == this.canExecute)
+ {
+ return;
+ }
+
+ this.canExecute = value;
+ this.CoerceValue(IsEnabledProperty);
+ }
}
- private static object CoerceIsEnabledProperty(DependencyObject dependencyObject, object value)
+ private static object CoerceIsEditableProperty(DependencyObject dependencyObject, object value)
{
// For now SplitButton is not editable
return false;
@@ -243,8 +298,11 @@ private static object CoerceIsEnabledProperty(DependencyObject dependencyObject,
private void ButtonClick(object sender, RoutedEventArgs e)
{
+ CommandHelpers.ExecuteCommandSource(this);
+
e.RoutedEvent = ClickEvent;
this.RaiseEvent(e);
+
this.SetCurrentValue(IsDropDownOpenProperty, false);
}
@@ -258,37 +316,30 @@ public override void OnApplyTemplate()
{
base.OnApplyTemplate();
- if (this._clickButton != null)
+ if (this.button != null)
{
- this._clickButton.Click -= this.ButtonClick;
- this._clickButton.IsEnabledChanged -= this.ButtonIsEnabledChanged;
+ this.button.Click -= this.ButtonClick;
}
- this._clickButton = this.GetTemplateChild("PART_Button") as Button;
- if (this._clickButton != null)
+ this.button = this.GetTemplateChild("PART_Button") as Button;
+ if (this.button != null)
{
- this._clickButton.Click += this.ButtonClick;
- this._clickButton.IsEnabledChanged += this.ButtonIsEnabledChanged;
+ this.button.Click += this.ButtonClick;
}
- if (this._expander != null)
+ if (this.expanderButton != null)
{
- this._expander.PreviewMouseLeftButtonDown -= this.ExpanderMouseLeftButtonDown;
+ this.expanderButton.PreviewMouseLeftButtonDown -= this.ExpanderMouseLeftButtonDown;
}
- this._expander = this.GetTemplateChild("PART_Expander") as Button;
- if (this._expander != null)
+ this.expanderButton = this.GetTemplateChild("PART_Expander") as Button;
+ if (this.expanderButton != null)
{
- this._expander.PreviewMouseLeftButtonDown += this.ExpanderMouseLeftButtonDown;
+ this.expanderButton.PreviewMouseLeftButtonDown += this.ExpanderMouseLeftButtonDown;
}
}
- private void ButtonIsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
- {
- this.SetCurrentValue(IsEnabledProperty, e.NewValue);
- }
-
- private Button _clickButton;
- private Button _expander;
+ private Button button;
+ private Button expanderButton;
}
}
\ No newline at end of file
diff --git a/src/MahApps.Metro/Themes/DropDownButton.xaml b/src/MahApps.Metro/Themes/DropDownButton.xaml
index e7bd81f9b0..ebfc00ad00 100644
--- a/src/MahApps.Metro/Themes/DropDownButton.xaml
+++ b/src/MahApps.Metro/Themes/DropDownButton.xaml
@@ -1,4 +1,4 @@
-
@@ -54,9 +54,6 @@
VerticalContentAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
- Command="{TemplateBinding Command}"
- CommandParameter="{TemplateBinding CommandParameter}"
- CommandTarget="{TemplateBinding CommandTarget}"
FocusVisualStyle="{TemplateBinding FocusVisualStyle}"
Foreground="{TemplateBinding Foreground}"
RenderOptions.ClearTypeHint="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(RenderOptions.ClearTypeHint), Mode=OneWay}"
diff --git a/src/MahApps.Metro/Themes/SplitButton.xaml b/src/MahApps.Metro/Themes/SplitButton.xaml
index c848d21460..bcbb635289 100644
--- a/src/MahApps.Metro/Themes/SplitButton.xaml
+++ b/src/MahApps.Metro/Themes/SplitButton.xaml
@@ -28,9 +28,6 @@
VerticalContentAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
- Command="{TemplateBinding Command}"
- CommandParameter="{TemplateBinding CommandParameter}"
- CommandTarget="{TemplateBinding CommandTarget}"
FocusVisualStyle="{TemplateBinding FocusVisualStyle}"
Foreground="{TemplateBinding Foreground}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
@@ -156,9 +153,6 @@
VerticalContentAlignment="Stretch"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
- Command="{TemplateBinding Command}"
- CommandParameter="{TemplateBinding CommandParameter}"
- CommandTarget="{TemplateBinding CommandTarget}"
FocusVisualStyle="{TemplateBinding FocusVisualStyle}"
Foreground="{TemplateBinding Foreground}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
diff --git a/src/Mahapps.Metro.Tests/Tests/ButtonTest.cs b/src/Mahapps.Metro.Tests/Tests/ButtonTest.cs
index d342afbc36..a4990e6d5d 100644
--- a/src/Mahapps.Metro.Tests/Tests/ButtonTest.cs
+++ b/src/Mahapps.Metro.Tests/Tests/ButtonTest.cs
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
+using System.Windows;
using System.Windows.Controls;
using MahApps.Metro.Controls;
using MahApps.Metro.Tests.TestHelpers;
@@ -55,7 +56,7 @@ public async Task SquareButtonButtonTextIsLowerCase()
[Fact]
[DisplayTestMethodName]
- public async Task SquareButtonBespectsButtonHelperContentCharacterCasing()
+ public async Task SquareButtonRespectsButtonHelperContentCharacterCasing()
{
await TestHost.SwitchToAppThread();
@@ -67,5 +68,35 @@ public async Task SquareButtonBespectsButtonHelperContentCharacterCasing()
Assert.Equal("SomeText", presenter.Content);
}
+
+ [Fact]
+ [DisplayTestMethodName]
+ public async Task DropDownButtonShouldRespectParentIsEnabledProperty()
+ {
+ await TestHost.SwitchToAppThread();
+
+ var window = await WindowHelpers.CreateInvisibleWindowAsync();
+
+ window.TheStackPanel.SetCurrentValue(UIElement.IsEnabledProperty, false);
+ Assert.False(window.TheDropDownButton.IsEnabled);
+
+ window.TheStackPanel.SetCurrentValue(UIElement.IsEnabledProperty, true);
+ Assert.True(window.TheDropDownButton.IsEnabled);
+ }
+
+ [Fact]
+ [DisplayTestMethodName]
+ public async Task SplitButtonShouldRespectParentIsEnabledProperty()
+ {
+ await TestHost.SwitchToAppThread();
+
+ var window = await WindowHelpers.CreateInvisibleWindowAsync();
+
+ window.TheStackPanel.SetCurrentValue(UIElement.IsEnabledProperty, false);
+ Assert.False(window.TheSplitButton.IsEnabled);
+
+ window.TheStackPanel.SetCurrentValue(UIElement.IsEnabledProperty, true);
+ Assert.True(window.TheSplitButton.IsEnabled);
+ }
}
}
diff --git a/src/Mahapps.Metro.Tests/Views/ButtonWindow.xaml b/src/Mahapps.Metro.Tests/Views/ButtonWindow.xaml
index da42d7f610..5948ac2989 100644
--- a/src/Mahapps.Metro.Tests/Views/ButtonWindow.xaml
+++ b/src/Mahapps.Metro.Tests/Views/ButtonWindow.xaml
@@ -7,10 +7,25 @@
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
-
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file