Pseudoclasses
Overview
Pseudoclasses in Avalonia, similar to those in CSS, are keywords exposed by a Control
that indicate a distinct control
state in a convenient way for Style Selectors. These states are used to conditionally style controls. For example, a
Button
could have a different appearance while it's being pressed or a TextBox while it is disabled.
Pseudoclass state is tracked by the Control
's PseudoClasses
property. By convention, pseudoclass names begin with
a :
, such as :pointerover
or :pressed
.
Selector Usage
The following demonstrates how to apply Bold text to a CheckBox
when it is checked:
<Window.Styles>
<Style Selector="CheckBox:checked">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Window.Styles>
<CheckBox Content="Pseudoselectors" />
A Control
can have multiple pseudoclasses active and you can target multiple pseudoclasses with a Selector. For example:
<Style Selector="Button.red:focus:pointerover">
The selector above targets Button
controls with the red style class, and that have the :focus
and the :pointerover
pseudoclass state.
General Pseudoclasses
These pseudoclasses are defined by InputElement
and are accessible on every Control
:
Pseudoclass | Description |
---|---|
:disabled | The Control is disabled and cannot be interacted with. |
:pointerover | The Pointer is over the Control as determined by hit testing. |
:focus | The Control has focus. |
:focus-within | The Control has focus or contains a descendant that has focus. |
:focus-visible | The Control has focus and should show a visual indicator. |
Accessibility
The PseudoClasses
collection is a protected
property. This accessibility blocks the external setting of existing and custom
pseudoclasses via code-behind and attached behavior. As such, customizing must be implemented through inheritance.
Custom Pseudoclass Example
When creating a custom control, you can define custom pseudoclasses to expose control state. The [PseudoClasses]
attribute
provides information about your pseudoclass to the IDE. This behavior is inherited, so the custom control automatically benefits from
pseudoclasses defined and managed by more primitive controls, such as InputElement
's :pointerover
.
The following example defines and sets pseudoclasses when the pointer is over different regions of a Button
.
- C# Control
- Example XAML
- ControlTheme XAML
[PseudoClasses(":left", ":right", ":middle")]
public class AreaButton : Button
{
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
var pos = e.GetPosition(this);
if (pos.X < Bounds.Width * 0.25)
SetAreaPseudoclasses(true, false, false);
else if (pos.X > Bounds.Width * 0.75)
SetAreaPseudoclasses(false, true, false);
else
SetAreaPseudoclasses(false, false, true);
}
protected override void OnPointerExited(PointerEventArgs e)
{
base.OnPointerExited(e);
SetAreaPseudoclasses(false, false, false);
}
private void SetAreaPseudoclasses(bool left, bool right, bool middle)
{
PseudoClasses.Set(":left", left);
PseudoClasses.Set(":right", right);
PseudoClasses.Set(":middle", middle);
}
}
<Window.Styles>
<Style Selector="local|AreaButton">
<Setter Property="Content" Value="Testing Area" />
<Setter Property="MinWidth" Value="200" />
<Style Selector="^:left">
<Setter Property="Content" Value="Left" />
</Style>
<Style Selector="^:right">
<Setter Property="Content" Value="Right" />
</Style>
<Style Selector="^:middle">
<Setter Property="Content" Value="Middle" />
</Style>
</Style>
</Window.Styles>
<local:AreaButton />
<ControlTheme
x:Key="{x:Type local:AreaButton}"
BasedOn="{StaticResource {x:Type Button}}"
TargetType="local:AreaButton" />
StyleKeyOverride
is used when creating simple, derived controls with the ControlTheme
defined by their parent. In this
case since Button
is a TemplatedControl
, creating a ControlTheme
is necessary as the Selector must target AreaButton
for the new pseudoclasses.