由于C# Maui是跨平台,所以按钮是没有写PointerOver这个样式的。本例子通过两种方法展示Button的Style修改。

方法一,使用xaml。这个方法,可以看我前面的教程,在App.xaml中添加MyStyles.xaml,然后自定义的按钮样式就可以被所有项目使用了。

<!--Button定义-->
<Style TargetType="Button" x:Key="WpfStyleButton">
    <!-- 基础样式 -->
    <Setter Property="BackgroundColor" Value="#DDDDDD" />
    <Setter Property="TextColor" Value="Black" />
    <Setter Property="BorderColor" Value="Gray" />
    <Setter Property="BorderWidth" Value="1" />
    <Setter Property="CornerRadius" Value="4" />
    <Setter Property="Padding" Value="12,6" />
    <Setter Property="FontSize" Value="14" />
<!-- 视觉状态管理 -->
<Setter Property="VisualStateManager.VisualStateGroups">
    <VisualStateGroupList>
        <VisualStateGroup x:Name="CommonStates">
            <!-- 默认状态 -->
            <VisualState x:Name="Normal" />
            
            <!-- 鼠标悬停状态 -->
            <VisualState x:Name="PointerOver">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="#BBBBBB" />
                    <Setter Property="BorderColor" Value="#888888" />
                </VisualState.Setters>
            </VisualState>
            
            <!-- 按下状态 -->
            <VisualState x:Name="Pressed">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="#999999" />
                    <Setter Property="TextColor" Value="#EEEEEE" />
                </VisualState.Setters>
            </VisualState>
            
            <!-- 禁用状态 -->
            <VisualState x:Name="Disabled">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="#EEEEEE" />
                    <Setter Property="TextColor" Value="Gray" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateGroupList>
</Setter>
</Style>

方式二,自己写一个WpfButton,这个按钮是支持直接内容的,是wpf的button使用方法一样。在写之前,我在Shares.Utility项目里创建了VisualManager.cs,这样以后所有的状态切换可以在这里实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Shares.Utility
{
    public static class VisualManager
    {
        public static Dictionary<string, VisualStateGroupList> Visuals = new Dictionary<string, VisualStateGroupList>();

        static VisualManager() 
        {
            Visuals[nameof(WpfButton)] = CreateWpfButtonStyle();
        }
        public static VisualStateGroupList CreateWpfButtonStyle()
        {
            return new VisualStateGroupList
            {
                new VisualStateGroup
                {
                    Name = "CommonStates",
                    States =
                    {
                        // 正常状态
                        new VisualState
                        {
                            Name = "Normal",
                            Setters =
                            {
                                new Setter {Property =WpfButton.BackgroundColorProperty, Value = Color.FromArgb("#DDDDDD")},
                                new Setter {Property = WpfButton.TextColorProperty, Value = Colors.Black}
                            }
                        },
                        // 指针悬停状态
                        new VisualState
                        {
                            Name = "PointerOver",
                            Setters =
                            {
                                new Setter { Property = WpfButton.BackgroundColorProperty, Value =Color.FromArgb("#BBBBBB") },
                                new Setter { Property = WpfButton.BorderColorProperty, Value =Color.FromArgb("#888888")},
                            }
                        },
                        // 按下状态
                        new VisualState
                        {
                            Name = "Pressed",
                            Setters =
                            {
                                new Setter { Property = WpfButton.BackgroundColorProperty, Value = Color.FromArgb("#999999") },
                                new Setter { Property = WpfButton.TextColorProperty, Value = Color.FromArgb("#EEEEEE")},
                            }
                        },
                        // 禁用状态
                        new VisualState
                        {
                            Name = "Disabled",
                            Setters =
                            {
                                new Setter { Property = WpfButton.BackgroundColorProperty, Value = Color.FromArgb("#EEEEEE") },
                                new Setter { Property = WpfButton.TextColorProperty, Value = Colors.Gray },
                            }
                        }
                    }
                }
            };
        }

    }
}

WpfButton类代码

    public class WpfButton : Border
    {
        // 事件
        public event EventHandler? Clicked;
        public event EventHandler? Released;
        public event EventHandler? PointerEntered;
        public event EventHandler? PointerExited;
        // 命令绑定
        public static readonly BindableProperty CommandProperty =
            BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(WpfButton), null);

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(WpfButton), null);

        public static readonly BindableProperty TextProperty =
            BindableProperty.Create(nameof(Text), typeof(string), typeof(WpfButton), null);

        public static readonly BindableProperty TextColorProperty =
            BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(WpfButton), Colors.Black);

        public static readonly BindableProperty FontSizeProperty =
            BindableProperty.Create(nameof(FontSize), typeof(float), typeof(WpfButton), 14f);

        public static readonly BindableProperty FontAttributesProperty =
            BindableProperty.Create(nameof(FontAttributes), typeof(FontAttributes), typeof(WpfButton), FontAttributes.None);

        public static readonly BindableProperty FontFamilyProperty =
            BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(WpfButton), null);

        public static readonly BindableProperty BorderColorProperty =
            BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(WpfButton), Colors.Gray);

        public static readonly BindableProperty BorderWidthProperty =
            BindableProperty.Create(nameof(BorderWidth), typeof(float), typeof(WpfButton), 1f);

        public static readonly BindableProperty CornerRadiusProperty =
            BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(WpfButton), 0f);
        
        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }
        public object CommandParameter
        {
            get => GetValue(CommandParameterProperty);
            set => SetValue(CommandParameterProperty, value);
        }
        public string Text
        {
            get => (string)GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }
        public Color TextColor
        {
            get => (Color)GetValue(TextColorProperty);
            set => SetValue(TextColorProperty, value);
        }
        public float FontSize
        {
            get=>(float)GetValue(FontSizeProperty);
            set => SetValue(FontSizeProperty, value);
        }
        public FontAttributes FontAttributes
        {
            get=>(FontAttributes)GetValue(FontAttributesProperty);
            set => SetValue(FontAttributesProperty, value);
        }
        public string FontFamily
        {
            get => (string)GetValue(FontFamilyProperty);
            set => SetValue(FontFamilyProperty, value);
        }
        public Color BorderColor
        {
            get => (Color)GetValue(BorderColorProperty);
            set => SetValue(BorderColorProperty, value);
        }
        public float BorderWidth
        {
            get => (float)GetValue(BorderWidthProperty);
            set => SetValue(BorderWidthProperty, value);
        }
        public float CornerRadius
        {
            get => (float)GetValue(CornerRadiusProperty);
            set => SetValue(CornerRadiusProperty, value);
        }

        public WpfButton()
        {
            //默认是一个Label
            this.Content = new Label();
            this.HeightRequest = 40;
            this.Padding = new Thickness(12, 6);
            //初始化绑定
            this.SetBinding(Border.StrokeProperty, new Binding(nameof(BorderColor), BindingMode.TwoWay, source: this));
            this.SetBinding(Border.StrokeThicknessProperty, new Binding(nameof(BorderWidth), BindingMode.TwoWay, source: this));
            this.StrokeShape = new RoundRectangle();
            ((RoundRectangle)this.StrokeShape).SetBinding(RoundRectangle.CornerRadiusProperty,
                     new Binding(nameof(CornerRadius), BindingMode.TwoWay, source: this));
            //初始化样式
            VisualStateManager.SetVisualStateGroups(this, VisualManager.Visuals[nameof(WpfButton)]);
            //初始化事件
            EnablePointerInteraction();
        }
        protected void UpdateLabelBindings(object? obj)
        {
            if (obj is Label label && (this.Content?.GetType() == obj.GetType()))
            {
                label.SetBinding(Label.TextProperty, new Binding(nameof(Text), BindingMode.TwoWay, source: this));
                label.SetBinding(Label.TextColorProperty, new Binding(nameof(TextColor), BindingMode.TwoWay, source: this));
                label.SetBinding(Label.FontSizeProperty, new Binding(nameof(FontSize), BindingMode.TwoWay, source: this));
                label.SetBinding(Label.FontAttributesProperty, new Binding(nameof(FontAttributes), BindingMode.TwoWay, source: this));
                label.SetBinding(Label.FontFamilyProperty, new Binding(nameof(FontFamily), BindingMode.TwoWay, source: this));
                //只有一个Label时,则自动对齐
                label.VerticalOptions = LayoutOptions.Center;
                label.HorizontalOptions = LayoutOptions.Center;
            }
            HeightRequest = -1;
        }
        protected override void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            base.OnPropertyChanged(propertyName);
            if (propertyName == nameof(Content))
            {
                UpdateLabelBindings(this.Content);
            }
        }
        protected void EnablePointerInteraction()
        {
            var pointer = new PointerGestureRecognizer();
            pointer.PointerEntered += (s, e) =>
            {
                if (!IsEnabled)
                    return;
                PointerEntered?.Invoke(this, e);
            };
            pointer.PointerExited += (s, e) =>
            {
                if (!IsEnabled)
                    return;
                PointerExited?.Invoke(this, e);
            };
            pointer.PointerPressed += (s, e) =>
            {
                if (!IsEnabled)
                    return;
                if (Command?.CanExecute(CommandParameter) == true)
                {
                    Command.Execute(CommandParameter);
                }
                Clicked?.Invoke(this, e);
                VisualStateManager.GoToState(this, "Pressed");
            };
            pointer.PointerReleased += (s, e) =>
            {
                if (!IsEnabled)
                    return;
                Released?.Invoke(this, e);
                VisualStateManager.GoToState(this, "Normal");
            };
            GestureRecognizers.Add(pointer);
        }
    }

ButtonsWithContent.xaml使用例子

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiViews.MauiDemos.Book._06.ButtonsWithContent"
             Title="ButtonsWithContent" WidthRequest="300" HeightRequest="600">
    <ScrollView>
        <StackLayout Margin="3" Spacing="5">
            <Button Margin="3" Text="Text Button"/>
            <Button Margin="3" Text="Wpf Style Button" Style="{StaticResource WpfStyleButton}" CornerRadius="15" 
                Clicked="Button_Clicked"/>
            <Button Margin="3" Style="{StaticResource WpfStyleButton}" ImageSource="happyface.jpg" Text="happyface"
                    ContentLayout="Top, 5"/>
            <WpfButton Text="Maui按钮" CornerRadius="15" />
            <WpfButton Padding="3" Margin="3" HorizontalOptions="Start">
                <StackLayout>
                    <Label Margin="3" Text="Type something here:"/>
                    <TextBox Margin="3" HorizontalOptions="Start" Placeholder="Text box in a button"/>
                </StackLayout>
            </WpfButton>
        </StackLayout>
    </ScrollView>
</ContentPage>

对应的cs代码

using Shares;
using System.Diagnostics;

namespace MauiViews.MauiDemos.Book._06;

public partial class ButtonsWithContent : ContentPage
{
	public ButtonsWithContent()
	{
		InitializeComponent();
	}

    private void Button_Clicked(object sender, EventArgs e)
    {
		Shell.Current.MessageBox("提示", "你按下了WPF样式的按钮");//这个是Shell扩展中实现,大家可以看看之前代码。
    }
}

运行效果

 

posted on 2025-07-03 07:47  dalgleish  阅读(11)  评论(0)    收藏  举报