Browse Source

SplitButton Avoid unnecessary pseudoclasses by using Avalonia-friendly approaches

pull/7422/head
Max Katz 5 years ago
parent
commit
07e2952750
  1. 291
      src/Avalonia.Controls/SplitButton/SplitButton.cs
  2. 6
      src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs
  3. 450
      src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml

291
src/Avalonia.Controls/SplitButton/SplitButton.cs

@ -1,5 +1,4 @@
using System;
using System.Reactive.Disposables;
using System.Windows.Input;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
@ -13,45 +12,11 @@ namespace Avalonia.Controls
/// A button with primary and secondary parts that can each be pressed separately.
/// The primary part behaves like a <see cref="Button"/> and the secondary part opens a flyout.
/// </summary>
[PseudoClasses(
pcDisabled,
pcSecondaryButtonRight,
pcSecondaryButtonSpan,
pcCheckedFlyoutOpen,
pcFlyoutOpen,
pcCheckedTouchPressed,
pcChecked,
pcCheckedPrimaryPressed,
pcCheckedPrimaryPointerOver,
pcCheckedSecondaryPressed,
pcCheckedSecondaryPointerOver,
pcTouchPressed,
pcPrimaryPressed,
pcPrimaryPointerOver,
pcSecondaryPressed,
pcSecondaryPointerOver)]
[PseudoClasses(pcFlyoutOpen)]
public class SplitButton : ContentControl, ICommandSource
{
private const string pcDisabled = ":disabled";
private const string pcSecondaryButtonRight = ":secondary-button-right";
private const string pcSecondaryButtonSpan = ":secondary-button-span";
private const string pcCheckedFlyoutOpen = ":checked-flyout-open";
private const string pcFlyoutOpen = ":flyout-open";
private const string pcCheckedTouchPressed = ":checked-touch-pressed";
private const string pcChecked = ":checked";
private const string pcCheckedPrimaryPressed = ":checked-primary-pressed";
private const string pcCheckedPrimaryPointerOver = ":checked-primary-pointerover";
private const string pcCheckedSecondaryPressed = ":checked-secondary-pressed";
private const string pcCheckedSecondaryPointerOver = ":checked-secondary-pointerover";
private const string pcTouchPressed = ":touch-pressed";
private const string pcPrimaryPressed = ":primary-pressed";
private const string pcPrimaryPointerOver = ":primary-pointerover";
private const string pcSecondaryPressed = ":secondary-pressed";
private const string pcSecondaryPointerOver = ":secondary-pointerover";
protected const string pcChecked = ":checked";
/// <summary>
/// Raised when the user presses the primary part of the <see cref="SplitButton"/>.
@ -62,13 +27,7 @@ namespace Avalonia.Controls
remove => RemoveHandler(ClickEvent, value);
}
/// <summary>
/// Defines the <see cref="Click"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<SplitButton, RoutedEventArgs>(
nameof(Click),
RoutingStrategies.Bubble);
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent = Button.ClickEvent;
/// <summary>
/// Defines the <see cref="Command"/> property.
@ -96,13 +55,9 @@ namespace Avalonia.Controls
private Button _secondaryButton = null;
private bool _commandCanExecute = true;
protected bool _hasLoaded = false;
private bool _isAttachedToLogicalTree = false;
private bool _isFlyoutOpen = false;
private bool _isKeyDown = false;
private PointerType _lastPointerType = PointerType.Mouse;
private CompositeDisposable _buttonPropertyChangedDisposable;
private IDisposable _flyoutPropertyChangedDisposable;
////////////////////////////////////////////////////////////////////////
@ -181,152 +136,8 @@ namespace Avalonia.Controls
/// </summary>
protected void UpdatePseudoClasses()
{
// Place the secondary button
//
// In WinUI, the span of the secondary button is changed to full-width for touch-based
// devices in certain conditions. The full reasoning for this is unknown. Some theories
// include:
//
// My guess is that the design team at MS decided that it's a better experience
// for touch users to make them select from the drop down rather than the shortcut
// top level button, so touch basically just turns this into a drop down button.
// Whether that's ideal or not is going is a subjective opinion.
//
// For Avalonia, it may not always make sense to disable the primary button like that
// on touch-first platforms. Users and developers would normally expect a control to
// function the same on all platforms. Therefore, this functionality is disabled here
// but could be re-enabled in the future if more reasons become known.
//
// Finally, these are mutually exclusive PseudoClasses handled separately from
// SetExclusivePseudoClass(). They must be applied in addition to the others.
/*
if (_lastPointerType == PointerType.Touch || _isKeyDown)
{
PseudoClasses.Set(pcSecondaryButtonSpan, true);
PseudoClasses.Set(pcSecondaryButtonRight, false);
}
else
{
PseudoClasses.Set(pcSecondaryButtonSpan, false);
PseudoClasses.Set(pcSecondaryButtonRight, true);
}
*/
// Change the visual state
if (!IsEffectivelyEnabled)
{
SetExclusivePseudoClass(pcDisabled);
}
else if (_primaryButton != null && _secondaryButton != null)
{
if (_isFlyoutOpen)
{
if (InternalIsChecked)
{
SetExclusivePseudoClass(pcCheckedFlyoutOpen);
}
else
{
SetExclusivePseudoClass(pcFlyoutOpen);
}
}
// SplitButton and ToggleSplitButton share a template -- this section is driving the checked states for ToggleSplitButton.
else if (InternalIsChecked)
{
if (_lastPointerType == PointerType.Touch || _isKeyDown)
{
if (_primaryButton.IsPressed || _secondaryButton.IsPressed || _isKeyDown)
{
SetExclusivePseudoClass(pcCheckedTouchPressed);
}
else
{
SetExclusivePseudoClass(pcChecked);
}
}
else if (_primaryButton.IsPressed)
{
SetExclusivePseudoClass(pcCheckedPrimaryPressed);
}
else if (_primaryButton.IsPointerOver)
{
SetExclusivePseudoClass(pcCheckedPrimaryPointerOver);
}
else if (_secondaryButton.IsPressed)
{
SetExclusivePseudoClass(pcCheckedSecondaryPressed);
}
else if (_secondaryButton.IsPointerOver)
{
SetExclusivePseudoClass(pcCheckedSecondaryPointerOver);
}
else
{
SetExclusivePseudoClass(pcChecked);
}
}
else
{
if (_lastPointerType == PointerType.Touch || _isKeyDown)
{
if (_primaryButton.IsPressed || _secondaryButton.IsPressed || _isKeyDown)
{
SetExclusivePseudoClass(pcTouchPressed);
}
else
{
// Calling without a parameter is treated as ':normal' and will clear all other
// PseudoClasses returning to the default state
SetExclusivePseudoClass();
}
}
else if (_primaryButton.IsPressed)
{
SetExclusivePseudoClass(pcPrimaryPressed);
}
else if (_primaryButton.IsPointerOver)
{
SetExclusivePseudoClass(pcPrimaryPointerOver);
}
else if (_secondaryButton.IsPressed)
{
SetExclusivePseudoClass(pcSecondaryPressed);
}
else if (_secondaryButton.IsPointerOver)
{
SetExclusivePseudoClass(pcSecondaryPointerOver);
}
else
{
// Calling without a parameter is treated as ':normal' and will clear all other
// PseudoClasses returning to the default state
SetExclusivePseudoClass();
}
}
}
// Local function to enable the specified PseudoClass and disable all others
// This more closely matches the VisualStateManager of WinUI where the default style originated
void SetExclusivePseudoClass(string pseudoClass = "")
{
PseudoClasses.Set(pcDisabled, pseudoClass == pcDisabled);
PseudoClasses.Set(pcCheckedFlyoutOpen, pseudoClass == pcCheckedFlyoutOpen);
PseudoClasses.Set(pcFlyoutOpen, pseudoClass == pcFlyoutOpen);
PseudoClasses.Set(pcCheckedTouchPressed, pseudoClass == pcCheckedTouchPressed);
PseudoClasses.Set(pcChecked, pseudoClass == pcChecked);
PseudoClasses.Set(pcCheckedPrimaryPressed, pseudoClass == pcCheckedPrimaryPressed);
PseudoClasses.Set(pcCheckedPrimaryPointerOver, pseudoClass == pcCheckedPrimaryPointerOver);
PseudoClasses.Set(pcCheckedSecondaryPressed, pseudoClass == pcCheckedSecondaryPressed);
PseudoClasses.Set(pcCheckedSecondaryPointerOver, pseudoClass == pcCheckedSecondaryPointerOver);
PseudoClasses.Set(pcTouchPressed, pseudoClass == pcTouchPressed);
PseudoClasses.Set(pcPrimaryPressed, pseudoClass == pcPrimaryPressed);
PseudoClasses.Set(pcPrimaryPointerOver, pseudoClass == pcPrimaryPointerOver);
PseudoClasses.Set(pcSecondaryPressed, pseudoClass == pcSecondaryPressed);
PseudoClasses.Set(pcSecondaryPointerOver, pseudoClass == pcSecondaryPointerOver);
}
PseudoClasses.Set(pcFlyoutOpen, _isFlyoutOpen);
PseudoClasses.Set(pcChecked, GetValue(ToggleSplitButton.IsCheckedProperty));
}
/// <summary>
@ -387,29 +198,14 @@ namespace Avalonia.Controls
/// </summary>
private void UnregisterEvents()
{
_buttonPropertyChangedDisposable?.Dispose();
_buttonPropertyChangedDisposable = null;
if (_primaryButton != null)
{
_primaryButton.Click -= PrimaryButton_Click;
_primaryButton.PointerEnter -= Button_PointerEvent;
_primaryButton.PointerLeave -= Button_PointerEvent;
_primaryButton.PointerPressed -= Button_PointerEvent;
_primaryButton.PointerReleased -= Button_PointerEvent;
_primaryButton.PointerCaptureLost -= Button_PointerCaptureLost;
}
if (_secondaryButton != null)
{
_secondaryButton.Click -= SecondaryButton_Click;
_secondaryButton.PointerEnter -= Button_PointerEvent;
_secondaryButton.PointerLeave -= Button_PointerEvent;
_secondaryButton.PointerPressed -= Button_PointerEvent;
_secondaryButton.PointerReleased -= Button_PointerEvent;
_secondaryButton.PointerCaptureLost -= Button_PointerCaptureLost;
}
}
@ -426,54 +222,18 @@ namespace Avalonia.Controls
_primaryButton = e.NameScope.Find<Button>("PART_PrimaryButton");
_secondaryButton = e.NameScope.Find<Button>("PART_SecondaryButton");
_buttonPropertyChangedDisposable = new CompositeDisposable();
if (_primaryButton != null)
{
_primaryButton.Click += PrimaryButton_Click;
_buttonPropertyChangedDisposable.Add(_primaryButton.GetPropertyChangedObservable(Button.IsPressedProperty).Subscribe(Button_VisualPropertyChanged));
_buttonPropertyChangedDisposable.Add(_primaryButton.GetPropertyChangedObservable(Button.IsPointerOverProperty).Subscribe(Button_VisualPropertyChanged));
// Register for pointer events to keep track of the last used pointer type and update visual states
_primaryButton.PointerEnter += Button_PointerEvent;
_primaryButton.PointerLeave += Button_PointerEvent;
_primaryButton.PointerPressed += Button_PointerEvent;
_primaryButton.PointerReleased += Button_PointerEvent;
_primaryButton.PointerCaptureLost += Button_PointerCaptureLost;
}
if (_secondaryButton != null)
{
_secondaryButton.Click += SecondaryButton_Click;
_buttonPropertyChangedDisposable.Add(_secondaryButton.GetPropertyChangedObservable(Button.IsPressedProperty).Subscribe(Button_VisualPropertyChanged));
_buttonPropertyChangedDisposable.Add(_secondaryButton.GetPropertyChangedObservable(Button.IsPointerOverProperty).Subscribe(Button_VisualPropertyChanged));
// Register for pointer events to keep track of the last used pointer type and update visual states
_secondaryButton.PointerEnter += Button_PointerEvent;
_secondaryButton.PointerLeave += Button_PointerEvent;
_secondaryButton.PointerPressed += Button_PointerEvent;
_secondaryButton.PointerReleased += Button_PointerEvent;
_secondaryButton.PointerCaptureLost += Button_PointerCaptureLost;
}
RegisterFlyoutEvents(Flyout);
UpdatePseudoClasses();
_hasLoaded = true;
}
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
}
/// <inheritdoc/>
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
}
/// <inheritdoc/>
@ -566,7 +326,6 @@ namespace Avalonia.Controls
if (key == Key.Space || key == Key.Enter) // Key.GamepadA is not currently supported
{
_isKeyDown = true;
UpdatePseudoClasses();
}
@ -580,7 +339,6 @@ namespace Avalonia.Controls
if (key == Key.Space || key == Key.Enter) // Key.GamepadA is not currently supported
{
_isKeyDown = false;
UpdatePseudoClasses();
// Consider this a click on the primary button
@ -623,7 +381,7 @@ namespace Avalonia.Controls
// Note: It is not currently required to check enabled status; however, this is a failsafe
if (IsEffectivelyEnabled)
{
var eventArgs = new RoutedEventArgs(ClickEvent);
var eventArgs = new RoutedEventArgs(Button.ClickEvent);
RaiseEvent(eventArgs);
if (!eventArgs.Handled && Command?.CanExecute(CommandParameter) == true)
@ -667,43 +425,6 @@ namespace Avalonia.Controls
OnClickSecondary(e);
}
/// <summary>
/// Event handler for when pointer events occur in the primary or secondary buttons.
/// </summary>
private void Button_PointerEvent(object sender, PointerEventArgs e)
{
// Warning: Code must match with Button_PointerCaptureLost
if (_lastPointerType != e.Pointer.Type)
{
_lastPointerType = e.Pointer.Type;
UpdatePseudoClasses();
}
}
/// <summary>
/// Event handler for when the pointer capture is lost in the primary or secondary buttons.
/// </summary>
/// <remarks>
/// In upstream WinUI this is not a separate event handler. However, Avalonia has different args.
/// </remarks>
private void Button_PointerCaptureLost(object sender, PointerCaptureLostEventArgs e)
{
// Warning: Code must match with Button_PointerEvent
if (_lastPointerType != e.Pointer.Type)
{
_lastPointerType = e.Pointer.Type;
UpdatePseudoClasses();
}
}
/// <summary>
/// Called when a primary or secondary button property changes that affects the visual states.
/// </summary>
private void Button_VisualPropertyChanged(AvaloniaPropertyChangedEventArgs e)
{
UpdatePseudoClasses();
}
/// <summary>
/// Called when the <see cref="FlyoutBase.Placement"/> property changes.
/// </summary>

6
src/Avalonia.Controls/SplitButton/ToggleSplitButton.cs

@ -1,4 +1,6 @@
using System;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Styling;
@ -10,6 +12,7 @@ namespace Avalonia.Controls
/// The primary part behaves like a <see cref="ToggleButton"/> with two states and
/// the secondary part opens a flyout.
/// </summary>
[PseudoClasses(pcChecked)]
public class ToggleSplitButton : SplitButton, IStyleable
{
/// <summary>
@ -101,7 +104,8 @@ namespace Avalonia.Controls
/// </summary>
protected virtual void OnIsCheckedChanged()
{
if (_hasLoaded)
// IsLoaded check
if (Parent is not null)
{
var eventArgs = new RoutedEventArgs(IsCheckedChangedEvent);
RaiseEvent(eventArgs);

450
src/Avalonia.Themes.Fluent/Controls/SplitButton.xaml

@ -3,14 +3,26 @@
x:CompileBindings="True">
<Design.PreviewWith>
<Border Padding="20">
<SplitButton />
<Border Padding="60">
<StackPanel>
<SplitButton Content="Hello World">
<SplitButton.Flyout>
<Flyout>Hello</Flyout>
</SplitButton.Flyout>
</SplitButton>
<ToggleSplitButton Content="Hello World">
<ToggleSplitButton.Flyout>
<Flyout>Hello</Flyout>
</ToggleSplitButton.Flyout>
</ToggleSplitButton>
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<x:Double x:Key="SplitButtonPrimaryButtonSize">32</x:Double>
<GridLength x:Key="SplitButtonSecondaryButtonSize">32</GridLength>
<x:Double x:Key="SplitButtonSecondaryButtonSize">32</x:Double>
<StreamGeometry x:Key="SplitButtonSecondaryButtonIconGeometry">M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z</StreamGeometry>
</Styles.Resources>
<Style Selector="SplitButton">
@ -20,380 +32,174 @@
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<!--<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<!--<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="KeyboardNavigation.IsTabStop" Value="True" />
<Setter Property="Focusable" Value="True" />
<Setter Property="Padding" Value="{DynamicResource ButtonPadding}" />
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- A Border was added here since Avalonia does not support Background/Border/CornerRadius on Grid directly
This also allowed removing the extra background border and integrating it here. -->
<Border x:Name="RootBorder"
Background="Transparent"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<!-- In WinUI, a separate child style for the inner buttons is provided here.
For avalonia that style is simplified and pulled out separately below.
A style is needed to override any button default background/foreground/border colors, etc. -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"
MinWidth="{StaticResource SplitButtonPrimaryButtonSize}" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="{StaticResource SplitButtonSecondaryButtonSize}" />
</Grid.ColumnDefinitions>
<!-- Changed from Grid to Border since Avalonia does not support Background/CornerRadius on Grid directly -->
<Border x:Name="PrimaryBackgroundBorder"
Background="{TemplateBinding Background}"
CornerRadius="{Binding $parent[SplitButton].CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
<Border x:Name="SecondaryBackgroundBorder"
Background="{TemplateBinding Background}"
Grid.Column="2"
CornerRadius="{Binding $parent[SplitButton].CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
<Button x:Name="PART_PrimaryButton"
Grid.Column="0"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
CornerRadius="{Binding $parent[SplitButton].CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
KeyboardNavigation.IsTabStop="False" />
<Button x:Name="PART_SecondaryButton"
Grid.Column="2"
CornerRadius="{Binding $parent[SplitButton].CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Transparent"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
KeyboardNavigation.IsTabStop="False"
Padding="0,0,10,0">
<Button.Content>
<!-- TextBlock converted to a Path and placed within Viewbox -->
<Viewbox UseLayoutRounding="False"
IsHitTestVisible="False"
Height="12"
Width="12">
<Path x:Name="DropDownGlyphPath"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Fill="{TemplateBinding Foreground}"
Data="M1939 486L2029 576L1024 1581L19 576L109 486L1024 1401L1939 486Z"
Stretch="Uniform" />
</Viewbox>
</Button.Content>
</Button>
</Grid>
</Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_PrimaryButton"
Grid.Column="0"
MinWidth="{DynamicResource SplitButtonPrimaryButtonSize}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Command="{TemplateBinding Command}"
CommandParameter="{TemplateBinding CommandParameter}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="{TemplateBinding Padding}"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
<Button x:Name="PART_SecondaryButton"
Grid.Column="1"
MinWidth="{DynamicResource SplitButtonSecondaryButtonSize}"
BorderBrush="{TemplateBinding BorderBrush}"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Center"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Focusable="False"
KeyboardNavigation.IsTabStop="False" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Normal State (not a PseudoClass) -->
<!-- The 'Button' style here was moved out of the main style above.
In WinUI there is a child, customized style shared for both primary and secondary buttons.
In Avalonia, we can use selectors to make a 'normal' style apply only to those buttons but
pull it out separately from the full style above. -->
<Style Selector="SplitButton /template/ Button">
<!-- Applies to both Primary/Secondary buttons -->
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForeground}" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="{DynamicResource SplitButtonBorderThemeThickness}" />
<Setter Property="Template">
<ControlTemplate>
<!-- Switched from Grid to Border to optimize and support Foreground/Background properties -->
<!-- It is important for the background to be clear and no CornerRadius set. -->
<Border Background="Transparent">
<ContentPresenter x:Name="ContentPresenter"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</ControlTemplate>
<!-- Default overridable styles -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource LeftCornerRadiusFilterConverter}}" />
</Style>
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton">
<Setter Property="CornerRadius" Value="{TemplateBinding CornerRadius, Converter={StaticResource RightCornerRadiusFilterConverter}}" />
<Setter Property="Padding" Value="9,0" />
<Setter Property="Content">
<Template>
<PathIcon Height="12"
Width="12"
Data="{StaticResource SplitButtonSecondaryButtonIconGeometry}" />
</Template>
</Setter>
</Style>
<!-- Disabled State -->
<Style Selector="SplitButton:disabled /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<!-- Primary and Secondary buttons PointerOver State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton:disabled /template/ Button">
<!-- Applies to both Primary/Secondary buttons -->
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- FlyoutOpen State -->
<Style Selector="SplitButton:flyout-open /template/ Border#PrimaryBackgroundBorder">
<!-- Primary and Secondary buttons Pressed State -->
<Style Selector="SplitButton /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton">
<Style Selector="SplitButton /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- TouchPressed State -->
<Style Selector="SplitButton:touch-pressed /template/ Border#PrimaryBackgroundBorder">
<!-- Primary and Secondary buttons Flyout Open State -->
<Style Selector="SplitButton:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
</Style>
<Style Selector="SplitButton:touch-pressed /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
</Style>
<Style Selector="SplitButton:touch-pressed /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:touch-pressed /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:touch-pressed /template/ Button#PART_SecondaryButton">
<Style Selector="SplitButton:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<!-- PrimaryPointerOver State -->
<Style Selector="SplitButton:primary-pointerover /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
</Style>
<Style Selector="SplitButton:primary-pointerover /template/ Button#PART_PrimaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<Style Selector="SplitButton:primary-pointerover /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
</Style>
<!-- PrimaryPressed State -->
<Style Selector="SplitButton:primary-pressed /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
</Style>
<Style Selector="SplitButton:primary-pressed /template/ Button#PART_PrimaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
</Style>
<Style Selector="SplitButton:primary-pressed /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
</Style>
<!-- SecondaryPointerOver State -->
<Style Selector="SplitButton:secondary-pointerover /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
</Style>
<Style Selector="SplitButton:secondary-pointerover /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPointerOver}" />
</Style>
<Style Selector="SplitButton:secondary-pointerover /template/ Button#PART_SecondaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPointerOver}" />
</Style>
<!-- SecondaryPressed State -->
<Style Selector="SplitButton:secondary-pressed /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackground}" />
</Style>
<Style Selector="SplitButton:secondary-pressed /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundPressed}" />
<!-- Disabled State -->
<Style Selector="SplitButton:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<Style Selector="SplitButton:secondary-pressed /template/ Button#PART_SecondaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundPressed}" />
<Style Selector="SplitButton:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundDisabled}" />
</Style>
<!-- Checked State -->
<Style Selector="SplitButton:checked /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Border#SecondaryBackgroundBorder">
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton">
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- CheckedFlyoutOpen State -->
<Style Selector="SplitButton:checked-flyout-open /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-flyout-open /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-flyout-open /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-flyout-open /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-flyout-open /template/ Button#PART_SecondaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-flyout-open /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- CheckedTouchPressed State -->
<Style Selector="SplitButton:checked-touch-pressed /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-touch-pressed /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-touch-pressed /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-touch-pressed /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-touch-pressed /template/ Button#PART_SecondaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-touch-pressed /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- CheckedPrimaryPointerOver State -->
<Style Selector="SplitButton:checked-primary-pointerover /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
</Style>
<Style Selector="SplitButton:checked-primary-pointerover /template/ Border#PrimaryBackgroundBorder">
<!-- Checked PointerOver State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pointerover /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked-primary-pointerover /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-primary-pointerover /template/ Button#PART_PrimaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked-primary-pointerover /template/ Button#PART_SecondaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked-primary-pointerover /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundChecked}" />
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pointerover PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<!-- CheckedPrimaryPressed State -->
<Style Selector="SplitButton:checked-primary-pressed /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
</Style>
<Style Selector="SplitButton:checked-primary-pressed /template/ Border#PrimaryBackgroundBorder">
<!-- Checked Pressed State -->
<Style Selector="SplitButton:checked /template/ Button#PART_PrimaryButton:pressed /template/ ContentPresenter,
SplitButton:checked /template/ Button#PART_SecondaryButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-primary-pressed /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-primary-pressed /template/ Button#PART_PrimaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-primary-pressed /template/ Button#PART_SecondaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-primary-pressed /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<!-- CheckedSecondaryPointerOver State -->
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Border#SecondaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Button#PART_SecondaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPointerOver}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pointerover /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundCheckedPointerOver}" />
<Style Selector="SplitButton:checked /template/ Button#PART_SecondaryButton:pressed PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- CheckedSecondaryPressed State -->
<Style Selector="SplitButton:checked-secondary-pressed /template/ Border#RootBorder">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pressed /template/ Border#PrimaryBackgroundBorder">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pressed /template/ Border#SecondaryBackgroundBorder">
<!-- Checked Flyout Open State -->
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pressed /template/ Button#PART_PrimaryButton">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundChecked}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pressed /template/ Button#PART_SecondaryButton">
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedPressed}" />
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="SplitButton:checked-secondary-pressed /template/ Path#DropDownGlyphPath">
<Setter Property="Fill" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
<Style Selector="SplitButton:checked:flyout-open /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedPressed}" />
</Style>
<!-- SecondaryButtonPlacement States -->
<!-- Note: Setting Grid attached properties doesn't work here for some unknown reason.
They simply are never changed. In addition, touch-device changes of secondary button
size are currently disabled in code-behind. -->
<Style Selector="SplitButton:secondary-button-right /template/ Button#PART_SecondaryButton">
<Setter Property="(Grid.Column)" Value="2" />
<Setter Property="(Grid.ColumnSpan)" Value="1" />
<!-- Disabled Checked State -->
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_PrimaryButton /template/ ContentPresenter,
SplitButton:checked:disabled /template/ Button#PART_SecondaryButton /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource SplitButtonBackgroundCheckedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource SplitButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="SplitButton:secondary-button-span /template/ Button#PART_SecondaryButton">
<Setter Property="(Grid.Column)" Value="0" />
<Setter Property="(Grid.ColumnSpan)" Value="3" />
<Style Selector="SplitButton:checked:disabled /template/ Button#PART_SecondaryButton PathIcon">
<Setter Property="Foreground" Value="{DynamicResource SplitButtonForegroundCheckedDisabled}" />
</Style>
</Styles>

Loading…
Cancel
Save