CustomDrawnElementChrome类,用于重写Button类模板。

CustomDrawnElementChrome.cs

using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Media;

namespace Shares.Avalonia.CustomControls
{
    public class CustomDrawnElementChrome : Decorator
    {
        public static readonly StyledProperty<Color> BackgroundColorProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, Color>(
                nameof(BackgroundColor),
                Colors.LightGray);

        public Color BackgroundColor
        {
            get => GetValue(BackgroundColorProperty);
            set => SetValue(BackgroundColorProperty, value);
        }

        public static readonly StyledProperty<Color> HoverColorProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, Color>(
                nameof(HoverColor),
                Colors.White);

        public Color HoverColor
        {
            get => GetValue(HoverColorProperty);
            set => SetValue(HoverColorProperty, value);
        }

        public static readonly StyledProperty<Color> PressedColorProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, Color>(
                nameof(PressedColor),
                Colors.Snow);

        public Color PressedColor
        {
            get => GetValue(PressedColorProperty);
            set => SetValue(PressedColorProperty, value);
        }

        public static readonly StyledProperty<IBrush?> BorderBrushProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, IBrush?>(
                nameof(BorderBrush));

        public IBrush? BorderBrush
        {
            get => GetValue(BorderBrushProperty);
            set => SetValue(BorderBrushProperty, value);
        }

        public static readonly StyledProperty<double> BorderThicknessProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, double>(
                nameof(BorderThickness),
                1.0);

        public double BorderThickness
        {
            get => GetValue(BorderThicknessProperty);
            set => SetValue(BorderThicknessProperty, value);
        }

        public static readonly StyledProperty<bool> IsPressedProperty =
            AvaloniaProperty.Register<CustomDrawnElementChrome, bool>(
                nameof(IsPressed));

        public bool IsPressed
        {
            get => GetValue(IsPressedProperty);
            set => SetValue(IsPressedProperty, value);
        }

        // 最新的鼠标位置
        private Point? lastPointerPosition;

        public CustomDrawnElementChrome()
        {
            PointerMoved += OnPointerMoved;
            PointerExited += OnPointerExited;
            PointerPressed += OnPointerPressed;
            PointerReleased += OnPointerReleased;
            PointerCaptureLost += OnPointerCaptureLost;
        }

        private void OnPointerMoved(object? sender, PointerEventArgs e)
        {
            lastPointerPosition = e.GetPosition(this);
            InvalidateVisual();
        }

        private void OnPointerExited(object? sender, PointerEventArgs e)
        {
            lastPointerPosition = null;
            InvalidateVisual();
        }

        private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
        {
            if (!e.Handled && e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
            {
                IsPressed = true;
                InvalidateVisual();
            }
        }

        private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
        {
            if (IsPressed)
            {
                IsPressed = false;
                InvalidateVisual();
            }
        }

        private void OnPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
        {
            if (IsPressed)
            {
                IsPressed = false;
                InvalidateVisual();
            }
        }

        public override void Render(DrawingContext context)
        {
            // 先绘制子元素
            base.Render(context);

            var bounds = new Rect(Bounds.Size);
            if (bounds.Width <= 0 || bounds.Height <= 0)
                return;

            var brush = CreateBackgroundBrush(bounds);

            Pen? borderPen = null;
            if (BorderBrush != null && BorderThickness > 0)
            {
                borderPen = new Pen(BorderBrush, BorderThickness);
            }

            context.DrawRectangle(brush, borderPen, bounds);
        }

        private IBrush CreateBackgroundBrush(Rect bounds)
        {
            var baseColor = BackgroundColor;

            if (!IsPointerOver || lastPointerPosition == null)
            {
                return new SolidColorBrush(baseColor);
            }

            var position = lastPointerPosition.Value;

            double rx = position.X / bounds.Width;
            double ry = position.Y / bounds.Height;

            if (double.IsNaN(rx) || double.IsInfinity(rx) ||
                double.IsNaN(ry) || double.IsInfinity(ry))
            {
                return new SolidColorBrush(baseColor);
            }

            var centerPoint = new RelativePoint(
                new Point(rx, ry),
                RelativeUnit.Relative);

            var highlightColor = IsPressed ? PressedColor : HoverColor;

            return new RadialGradientBrush
            {
                GradientStops =
                {
                    new GradientStop(highlightColor, 0),
                    new GradientStop(baseColor, 1)
                },
                Center = centerPoint,
                GradientOrigin = centerPoint,

                RadiusX = new RelativeScalar(0.5, RelativeUnit.Relative),
                RadiusY = new RelativeScalar(0.5, RelativeUnit.Relative)
            };
        }
    }
}

CustomDrawnElementChromeTest.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        x:Class="AvaloniaUI.CustomDrawnElementChromeTest"
        Height="300" Width="300"
        Title="CustomDrawnElementChromeTest">

    <Window.Resources>
        <ControlTemplate x:Key="ButtonChromeTemplate" TargetType="Button">
            <CustomDrawnElementChrome
                BackgroundColor="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                IsPressed="{TemplateBinding IsPressed}">

                <ContentPresenter
                    Content="{TemplateBinding Content}"
                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                    Margin="{TemplateBinding Padding}" />

            </CustomDrawnElementChrome>

        </ControlTemplate>

    </Window.Resources>
    <StackPanel HorizontalAlignment="Center" Spacing="5">
        <Button
            Template="{StaticResource ButtonChromeTemplate}"
            Background="#FF3F51B5"
            BorderBrush="#80212121"
            BorderThickness="1.5"
            Padding="8,4"
            Content="Chrome Button" />
        
        <Button Content="Normal Button"/>
    </StackPanel>
</Window>

CustomDrawnElementChromeTest.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Shares.Avalonia;

namespace AvaloniaUI;

public partial class CustomDrawnElementChromeTest : Window
{
    public CustomDrawnElementChromeTest()
    {
        InitializeComponent();
    }
}

运行效果

image

 

posted on 2025-12-08 15:43  dalgleish  阅读(0)  评论(0)    收藏  举报