using System.Windows.Controls;
using System.Windows.Controls.Primitives;
// ReSharper disable once CheckNamespace
namespace WPFluent.Controls;
///
/// Represents a button with two parts that can be invoked separately. One part behaves like a standard button and the
/// other part invokes a flyout.
///
[TemplatePart(Name = TemplateElementToggleButton, Type = typeof(ToggleButton))]
public class SplitButton : Button
{
///
/// Template element represented by the ToggleButton name.
///
private const string TemplateElementToggleButton = "PART_ToggleButton";
///
/// Identifies the dependency property.
///
public static readonly DependencyProperty FlyoutProperty = DependencyProperty.Register(
nameof(Flyout),
typeof(object),
typeof(SplitButton),
new PropertyMetadata(null, OnFlyoutChanged));
///
/// Identifies the dependency property.
///
public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
nameof(IsDropDownOpen),
typeof(bool),
typeof(SplitButton),
new PropertyMetadata(false, OnIsDropDownOpenChanged));
private ContextMenu? _contextMenu;
public SplitButton()
{
Unloaded += static (sender, _) =>
{
var self = (SplitButton)sender;
self.ReleaseTemplateResources();
};
}
private static void OnFlyoutChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SplitButton dropDownButton)
{
dropDownButton.OnFlyoutChanged(e.NewValue);
}
}
private static void OnIsDropDownOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SplitButton dropDownButton)
{
dropDownButton.OnIsDropDownOpenChanged(e.NewValue is bool boolVal && boolVal);
}
}
private void OnSplitButtonToggleButtonOnClick(object sender, RoutedEventArgs e)
{
if (sender is not ToggleButton || _contextMenu is null)
{
return;
}
_contextMenu.SetCurrentValue(MinWidthProperty, ActualWidth);
_contextMenu.SetCurrentValue(ContextMenu.PlacementTargetProperty, this);
_contextMenu.SetCurrentValue(ContextMenu.PlacementProperty, PlacementMode.Bottom);
_contextMenu.SetCurrentValue(ContextMenu.IsOpenProperty, true);
}
protected virtual void OnContextMenuClosed(object sender, RoutedEventArgs e)
{ SetCurrentValue(IsDropDownOpenProperty, false); }
protected virtual void OnContextMenuOpened(object sender, RoutedEventArgs e)
{ SetCurrentValue(IsDropDownOpenProperty, true); }
///
/// This method is invoked when the changes.
///
/// The new value of .
protected virtual void OnFlyoutChanged(object value)
{
if (value is ContextMenu contextMenu)
{
_contextMenu = contextMenu;
_contextMenu.Opened += OnContextMenuOpened;
_contextMenu.Closed += OnContextMenuClosed;
}
}
///
/// This method is invoked when the changes.
///
/// The new value of .
protected virtual void OnIsDropDownOpenChanged(bool currentValue)
{
}
///
/// Triggered when the control is unloaded. Releases resource bindings.
///
protected virtual void ReleaseTemplateResources()
{ SplitButtonToggleButton.Click -= OnSplitButtonToggleButtonOnClick; }
///
/// Gets or sets control responsible for toggling the drop-down button.
///
protected ToggleButton SplitButtonToggleButton { get; set; } = null!;
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (GetTemplateChild(TemplateElementToggleButton) is ToggleButton toggleButton)
{
SplitButtonToggleButton = toggleButton;
SplitButtonToggleButton.Click -= OnSplitButtonToggleButtonOnClick;
SplitButtonToggleButton.Click += OnSplitButtonToggleButtonOnClick;
}
else
{
throw new NullReferenceException(
$"Element {nameof(TemplateElementToggleButton)} of type {typeof(ToggleButton)} not found in {typeof(SplitButton)}");
}
}
///
/// Gets or sets the flyout associated with this button.
///
[Bindable(true)]
public object Flyout { get => GetValue(FlyoutProperty); set => SetValue(FlyoutProperty, value); }
///
/// Gets or sets a value indicating whether the drop-down for a button is currently open.
///
///
/// if the drop-down is open; otherwise, . The default is .
///
[Bindable(true)]
[Browsable(false)]
[Category("Appearance")]
public bool IsDropDownOpen
{
get => (bool)GetValue(IsDropDownOpenProperty);
set => SetValue(IsDropDownOpenProperty, value);
}
}