通过xaml来定义Template样式。

ColorPicker.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using System;

namespace Shares.Avalonia.CustomControls;

[TemplatePart("PART_RedSlider", typeof(Slider))]
[TemplatePart("PART_GreenSlider", typeof(Slider))]
[TemplatePart("PART_BlueSlider", typeof(Slider))]
public class ColorPicker : TemplatedControl
{
    public static readonly StyledProperty<Color> ColorProperty =
        AvaloniaProperty.Register<ColorPicker, Color>(nameof(Color), Colors.Black);

    public static readonly StyledProperty<double> RedProperty =
        AvaloniaProperty.Register<ColorPicker, double>(nameof(Red), 0);

    public static readonly StyledProperty<double> GreenProperty =
        AvaloniaProperty.Register<ColorPicker, double>(nameof(Green), 0);

    public static readonly StyledProperty<double> BlueProperty =
        AvaloniaProperty.Register<ColorPicker, double>(nameof(Blue), 0);

    private Slider? redSlider;
    private Slider? greenSlider;
    private Slider? blueSlider;

    private IDisposable? colorSubscription;
    private IDisposable? redSubscription;
    private IDisposable? greenSubscription;
    private IDisposable? blueSubscription;

    public ColorPicker()
    {
        // 当 Color 变化 → 更新 RGB
        colorSubscription = this.GetObservable(ColorProperty)
            .Subscribe(OnColorChanged);

        // 当任意 RGB 变化 → 更新 Color
        redSubscription = this.GetObservable(RedProperty)
            .Subscribe(_ => UpdateColorFromRgb());
        greenSubscription = this.GetObservable(GreenProperty)
            .Subscribe(_ => UpdateColorFromRgb());
        blueSubscription = this.GetObservable(BlueProperty)
            .Subscribe(_ => UpdateColorFromRgb());
    }

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

    public double Red
    {
        get => GetValue(RedProperty);
        set => SetValue(RedProperty, value);
    }

    public double Green
    {
        get => GetValue(GreenProperty);
        set => SetValue(GreenProperty, value);
    }

    public double Blue
    {
        get => GetValue(BlueProperty);
        set => SetValue(BlueProperty, value);
    }

    private void OnColorChanged(Color color)
    {
        // 更新 RGB
        Red = (double)color.R;
        Green = (double)color.G;
        Blue = (double)color.B;
    }

    private void UpdateColorFromRgb()
    {
        var newColor = Color.FromRgb((byte)Red, (byte)Green, (byte)Blue);
        if (newColor != Color)
            Color = newColor;
    }

    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
    {
        base.OnApplyTemplate(e);

        redSlider = e.NameScope.Find<Slider>("PART_RedSlider");
        greenSlider = e.NameScope.Find<Slider>("PART_GreenSlider");
        blueSlider = e.NameScope.Find<Slider>("PART_BlueSlider");

        if (redSlider != null)
            redSlider[!!Slider.ValueProperty] = this[!!RedProperty];

        if (greenSlider != null)
            greenSlider[!!Slider.ValueProperty] = this[!!GreenProperty];

        if (blueSlider != null)
            blueSlider[!!Slider.ValueProperty] = this[!!BlueProperty];
    }

    protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
    {
        base.OnDetachedFromVisualTree(e);

        colorSubscription?.Dispose();
        redSubscription?.Dispose();
        greenSubscription?.Dispose();
        blueSubscription?.Dispose();
    }
}

ColorPickerTwoWays.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"
        Height="350" Width="300"
         xmlns:local="using:AvaloniaUI"
        x:Class="AvaloniaUI.ColorPickerTwoWays"
        Title="ColorPickerTwoWays">
    <Window.Resources>
        <ControlTemplate x:Key="FancyColorPickerTemplate" TargetType="local:ColorPicker2">
            <Border Background="LightGoldenrodYellow"
                    BorderBrush="Black"
                    BorderThickness="1"
                    Padding="8">
                <Grid RowDefinitions="*,auto" ColumnDefinitions="*,auto,auto,auto">

                    <!-- 通用样式 -->
                    <Grid.Styles>
                        <Style Selector="Slider">
                            <Setter Property="Orientation" Value="Vertical"/>
                            <Setter Property="TickFrequency" Value="10"/>
                            <Setter Property="Minimum" Value="0"/>
                            <Setter Property="Maximum" Value="255"/>
                            <Setter Property="Margin" Value="5"/>
                        </Style>

                        <Style Selector="TextBlock">
                            <Setter Property="Margin" Value="3"/>
                            <Setter Property="FontSize" Value="10"/>
                            <Setter Property="HorizontalAlignment" Value="Center"/>
                        </Style>
                    </Grid.Styles>

                    <!-- 圆形颜色显示 -->
                    <Ellipse Grid.Column="0" Grid.RowSpan="2"
                             Margin="10" Stroke="LightGray" StrokeThickness="5" Height="120">
                        <Ellipse.Fill>
                            <SolidColorBrush Color="{Binding Color, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </Ellipse.Fill>
                    </Ellipse>

                    <!-- 三个滑块 -->
                    <Slider x:Name="PART_RedSlider" Grid.Column="1"/>
                    <TextBlock Grid.Row="1" Grid.Column="1" Text="RED"/>

                    <Slider x:Name="PART_GreenSlider" Grid.Column="2"/>
                    <TextBlock Grid.Row="1" Grid.Column="2" Text="GREEN"/>

                    <Slider x:Name="PART_BlueSlider" Grid.Column="3"/>
                    <TextBlock Grid.Row="1" Grid.Column="3" Text="BLUE"/>
                </Grid>
            </Border>
        </ControlTemplate>
    </Window.Resources>

    <StackPanel>
        <!-- 第一个颜色选择器 -->
        <local:ColorPicker2
            x:Name="colorPicker1"
            Margin="2" Padding="3"
            Color="AliceBlue"/>

        <!-- 第二个颜色选择器使用 Fancy 模板,并与第一个双向绑定 -->
        <local:ColorPicker2
            x:Name="colorPicker2"
            Margin="5,20,5,5"
            Template="{StaticResource FancyColorPickerTemplate}"
            Color="{Binding #colorPicker1.Color, Mode=TwoWay}"/>
    </StackPanel>
</Window>

ColorPickerTwoWays.axaml.cs代码

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

namespace AvaloniaUI;

public partial class ColorPickerTwoWays : Window
{
    public ColorPickerTwoWays()
    {
        InitializeComponent();
        this.Load("avares://AvaloniaUI/Demos/Book/18/CustomControls/ColorPicker.axaml");
    }
}

运行效果

image

 

posted on 2025-11-17 08:35  dalgleish  阅读(3)  评论(0)    收藏  举报