Xamarin.Forms: 在 UWP 中自定义悬停按钮样式
Xamarin.Forms: Customize button style on hover in UWP
我在 Xamarin Forms 项目的 App.xaml
中定义了一些样式。但是,如果您将鼠标悬停在按钮上或按下它,这不会影响按钮。此处字体颜色变为黑色,按钮周围出现灰色边框。现在我想覆盖这个样式。
第一次尝试: 添加定义到UWP项目App.xaml
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonPointerOverBackgroundThemeBrush" Color="#00FF00" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
结果:完全没有变化
第二次尝试:覆盖 UWP 项目App.xaml
中的PointOver
视觉状态
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<Style TargetType="Button" x:Key="HoverButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="#00FF00" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="#00FF00" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
结果:完全没有变化,我想我必须应用样式(如果我这样做按钮似乎不在这里)
第三次尝试:添加完整的按钮样式并应用
<Style TargetType="Button" x:Key="HoverButtonStyle">
<Setter Property="Background" Value="{ThemeResource ButtonBackgroundThemeBrush}" />
<Setter Property="Foreground" Value="{ThemeResource ButtonForegroundThemeBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="12,4,12,4" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="3">
<ContentPresenter x:Name="ContentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
<Rectangle x:Name="FocusVisualWhite"
IsHitTestVisible="False"
Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="1.5" />
<Rectangle x:Name="FocusVisualBlack"
IsHitTestVisible="False"
Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="0.5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
自定义渲染器:
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (this.Element != null)
{
this.Control.Style = Windows.UI.Xaml.Application.Current.Resources["HoverButtonStyle"] as Windows.UI.Xaml.Style;
}
}
结果:似乎应用了样式,但我在 Xamarin Forms 中定义的背景颜色没有占据按钮的整个宽度。边框颜色也没有改变。
这是怎么做到的?
现在我发现了这种样式的工作原理。首先,您必须找到基础 UWP class(按住 Ctrl 并单击 class 名称或查看 here). E.g. for Picker
it is ComboBox
. If you use Google you come to this page, where you find everything you need to know about overwriting the default layout of a ComboBox
. For a Button
it is this page 等等。所以解决方案是有一个像这样的 App.xaml
(UWP 项目)(选择您选择的颜色):
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SystemControlHighlightBaseMediumLowBrush" Color="White" />
<SolidColorBrush x:Key="SystemControlHighlightBaseHighBrush" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
要仅对某些按钮应用样式,您必须执行以下步骤:
在您的 UWP 项目的 App.xaml
中,您需要以下条目:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/DefaultButtonControlTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
这里注册一个样式,它在一个单独的文件中。我有一个名为 Styles
的文件夹,其中放置了文件 DefaultButtonControlTemplate.xaml。从 MSDN 获取的文件内容如下所示:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.UWP.ControlTemplates">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsAndBrushes.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="DefaultButtonControlTemplate" TargetType="Button">
<!-- here is the content of the file -->
</ControlTemplate>
</ResourceDictionary>
如您所见,我正在引用一个通用文件,其中包含我所有的颜色(或 UWP 世界中的画笔)。
最后,您需要一个像这样的自定义渲染器:
public class DefaultButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.Template = Windows.UI.Xaml.Application.Current.Resources["DefaultButtonControlTemplate"] as Windows.UI.Xaml.Controls.ControlTemplate;
}
}
}
找到了一种将其全部保留在 UWP 自定义渲染器中的方法,而不必担心修改其他任何内容或其他按钮设置是否会发生冲突。就我而言,我创建了一个自定义 PillButton,因此显然可以更新您的 类 和颜色等等。如果您没有在按钮上看到圆角半径,那么它将是一个使用下面的普通按钮。
[assembly: ExportRenderer(typeof(PillButton), typeof(PillButtonRenderer))]
namespace YourProject.UWP.Renderers
{
public class PillButtonRenderer : ButtonRenderer
{
public PillButton PillButtonElement => Element as PillButton;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Windows.UI.Xaml.Controls.Button button = Control;
Resources = (Windows.UI.Xaml.ResourceDictionary)XamlReader.Load(PillButtonStyleDictionary);
Resources["PillCornerRadius"] = PillButtonElement.CornerRadius;
Resources["PillBorderWidth"] = PillButtonElement.BorderWidth;
// if hover color not supplied, then hover color will be lighter version of background color, unless background color is transparent in which case it will be the border color
var hoverColor = PillButtonElement.UwpHoverColor != default(Color) ? PillButtonElement.UwpHoverColor
: (PillButtonElement.BackgroundColor == Color.Transparent
? PillButtonElement.BorderColor
: PillButtonElement.BackgroundColor.ChangeColorBrightness(0.15));
Resources["PillFillColorOnHover"] = new SolidColorBrush(hoverColor.ToUwp());
// if pressed color not supplied, then make it a darker shade of the hover color
var pressedColor = PillButtonElement.UwpPressedColor != default(Color) ? PillButtonElement.UwpPressedColor : hoverColor.ChangeColorBrightness(-0.09);
Resources["PillFillColorOnPressed"] = new SolidColorBrush(pressedColor.ToUwp());
// if text color on hover/press not supplied, then make it black or white depending on how dark the hover color is
var textColor = PillButtonElement.PressedTextColor != default(Color) ? PillButtonElement.PressedTextColor : hoverColor.BlackOrWhiteForegroundTextColor();
Resources["PillTextColorOnHoverOrPressed"] = new SolidColorBrush(textColor.ToUwp());
// set normal style
Resources["PillBackgroundColor"] = new SolidColorBrush(PillButtonElement.BackgroundColor.ToUwp());
Resources["PillTextColor"] = new SolidColorBrush(PillButtonElement.TextColor.ToUwp());
PillButtonElement.BackgroundColor = Color.Transparent; // hack
button.Style = Resources["PillButtonStyle"] as Windows.UI.Xaml.Style;
}
}
private const string PillButtonStyleDictionary = @"<ResourceDictionary
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<x:Double x:Key=""PillCornerRadius"">0</x:Double>
<x:Double x:Key=""PillBorderWidth"">0</x:Double>
<SolidColorBrush
x:Key=""PillBackgroundColor""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillTextColor""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillFillColorOnHover""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillFillColorOnPressed""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillTextColorOnHoverOrPressed""
Color=""Black"" />
<Style
x:Key=""PillButtonStyle""
TargetType=""Button"">
<Setter
Property=""Background""
Value=""{ThemeResource SystemControlBackgroundBaseLowBrush}"" />
<Setter
Property=""Foreground""
Value=""{ThemeResource SystemControlForegroundBaseHighBrush}"" />
<Setter
Property=""BorderBrush""
Value=""{ThemeResource SystemControlForegroundTransparentBrush}"" />
<Setter
Property=""BorderThickness""
Value=""{ThemeResource ButtonBorderThemeThickness}"" />
<Setter
Property=""Padding""
Value=""8,4,8,4"" />
<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=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""Button"">
<Grid x:Name=""RootGrid"">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name=""CommonStates"">
<VisualState x:Name=""Normal"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillBackgroundColor}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColor}"" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""PointerOver"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillFillColorOnHover}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColorOnHoverOrPressed}"" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""Pressed"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillFillColorOnPressed}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColorOnHoverOrPressed}"" />
</ObjectAnimationUsingKeyFrames>
<PointerDownThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""Disabled"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlBackgroundBaseLowBrush}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlDisabledBaseMediumLowBrush}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Stroke"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlDisabledTransparentBrush}"" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle
x:Name=""Pill""
RadiusX=""{StaticResource PillCornerRadius}""
RadiusY=""{StaticResource PillCornerRadius}""
Stroke=""{TemplateBinding BorderBrush}""
StrokeThickness=""{StaticResource PillBorderWidth}"" />
<ContentPresenter
x:Name=""ContentPresenter""
Padding=""{TemplateBinding Padding}""
HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}""
VerticalAlignment=""Center""
VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""
AutomationProperties.AccessibilityView=""Raw""
Content=""{TemplateBinding Content}""
ContentTemplate=""{TemplateBinding ContentTemplate}""
ContentTransitions=""{TemplateBinding ContentTransitions}"" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>";
}
}
以防万一,这里是用于从 Xamarin Color 转换为 UWP Color 的 ToUwp 颜色扩展,在该代码中使用:
internal static class ColorExtensions
{
public static Color ToUwp(this Xamarin.Forms.Color color)
{
return Color.FromArgb((byte)(color.A * 255),
(byte)(color.R * 255),
(byte)(color.G * 255),
(byte)(color.B * 255));
}
}
我在 Xamarin Forms 项目的 App.xaml
中定义了一些样式。但是,如果您将鼠标悬停在按钮上或按下它,这不会影响按钮。此处字体颜色变为黑色,按钮周围出现灰色边框。现在我想覆盖这个样式。
第一次尝试: 添加定义到UWP项目App.xaml
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="ButtonPointerOverBackgroundThemeBrush" Color="#00FF00" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
结果:完全没有变化
第二次尝试:覆盖 UWP 项目App.xaml
中的PointOver
视觉状态
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<Style TargetType="Button" x:Key="HoverButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="#00FF00" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="#00FF00" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
结果:完全没有变化,我想我必须应用样式(如果我这样做按钮似乎不在这里)
第三次尝试:添加完整的按钮样式并应用
<Style TargetType="Button" x:Key="HoverButtonStyle">
<Setter Property="Background" Value="{ThemeResource ButtonBackgroundThemeBrush}" />
<Setter Property="Foreground" Value="{ThemeResource ButtonForegroundThemeBrush}"/>
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderThemeBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="12,4,12,4" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonPointerOverBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBackgroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledBorderThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter"
Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonDisabledForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
<VisualState x:Name="PointerFocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="3">
<ContentPresenter x:Name="ContentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"/>
</Border>
<Rectangle x:Name="FocusVisualWhite"
IsHitTestVisible="False"
Stroke="{ThemeResource FocusVisualWhiteStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="1.5" />
<Rectangle x:Name="FocusVisualBlack"
IsHitTestVisible="False"
Stroke="{ThemeResource FocusVisualBlackStrokeThemeBrush}"
StrokeEndLineCap="Square"
StrokeDashArray="1,1"
Opacity="0"
StrokeDashOffset="0.5" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
自定义渲染器:
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (this.Element != null)
{
this.Control.Style = Windows.UI.Xaml.Application.Current.Resources["HoverButtonStyle"] as Windows.UI.Xaml.Style;
}
}
结果:似乎应用了样式,但我在 Xamarin Forms 中定义的背景颜色没有占据按钮的整个宽度。边框颜色也没有改变。
这是怎么做到的?
现在我发现了这种样式的工作原理。首先,您必须找到基础 UWP class(按住 Ctrl 并单击 class 名称或查看 here). E.g. for Picker
it is ComboBox
. If you use Google you come to this page, where you find everything you need to know about overwriting the default layout of a ComboBox
. For a Button
it is this page 等等。所以解决方案是有一个像这样的 App.xaml
(UWP 项目)(选择您选择的颜色):
<Application
x:Class="YourApp.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:YourApp.UWP"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="SystemControlHighlightBaseMediumLowBrush" Color="White" />
<SolidColorBrush x:Key="SystemControlHighlightBaseHighBrush" Color="White" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
要仅对某些按钮应用样式,您必须执行以下步骤:
在您的 UWP 项目的 App.xaml
中,您需要以下条目:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/DefaultButtonControlTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
这里注册一个样式,它在一个单独的文件中。我有一个名为 Styles
的文件夹,其中放置了文件 DefaultButtonControlTemplate.xaml。从 MSDN 获取的文件内容如下所示:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp.UWP.ControlTemplates">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsAndBrushes.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="DefaultButtonControlTemplate" TargetType="Button">
<!-- here is the content of the file -->
</ControlTemplate>
</ResourceDictionary>
如您所见,我正在引用一个通用文件,其中包含我所有的颜色(或 UWP 世界中的画笔)。
最后,您需要一个像这样的自定义渲染器:
public class DefaultButtonRenderer : ButtonRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.Template = Windows.UI.Xaml.Application.Current.Resources["DefaultButtonControlTemplate"] as Windows.UI.Xaml.Controls.ControlTemplate;
}
}
}
找到了一种将其全部保留在 UWP 自定义渲染器中的方法,而不必担心修改其他任何内容或其他按钮设置是否会发生冲突。就我而言,我创建了一个自定义 PillButton,因此显然可以更新您的 类 和颜色等等。如果您没有在按钮上看到圆角半径,那么它将是一个使用下面的普通按钮。
[assembly: ExportRenderer(typeof(PillButton), typeof(PillButtonRenderer))]
namespace YourProject.UWP.Renderers
{
public class PillButtonRenderer : ButtonRenderer
{
public PillButton PillButtonElement => Element as PillButton;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Windows.UI.Xaml.Controls.Button button = Control;
Resources = (Windows.UI.Xaml.ResourceDictionary)XamlReader.Load(PillButtonStyleDictionary);
Resources["PillCornerRadius"] = PillButtonElement.CornerRadius;
Resources["PillBorderWidth"] = PillButtonElement.BorderWidth;
// if hover color not supplied, then hover color will be lighter version of background color, unless background color is transparent in which case it will be the border color
var hoverColor = PillButtonElement.UwpHoverColor != default(Color) ? PillButtonElement.UwpHoverColor
: (PillButtonElement.BackgroundColor == Color.Transparent
? PillButtonElement.BorderColor
: PillButtonElement.BackgroundColor.ChangeColorBrightness(0.15));
Resources["PillFillColorOnHover"] = new SolidColorBrush(hoverColor.ToUwp());
// if pressed color not supplied, then make it a darker shade of the hover color
var pressedColor = PillButtonElement.UwpPressedColor != default(Color) ? PillButtonElement.UwpPressedColor : hoverColor.ChangeColorBrightness(-0.09);
Resources["PillFillColorOnPressed"] = new SolidColorBrush(pressedColor.ToUwp());
// if text color on hover/press not supplied, then make it black or white depending on how dark the hover color is
var textColor = PillButtonElement.PressedTextColor != default(Color) ? PillButtonElement.PressedTextColor : hoverColor.BlackOrWhiteForegroundTextColor();
Resources["PillTextColorOnHoverOrPressed"] = new SolidColorBrush(textColor.ToUwp());
// set normal style
Resources["PillBackgroundColor"] = new SolidColorBrush(PillButtonElement.BackgroundColor.ToUwp());
Resources["PillTextColor"] = new SolidColorBrush(PillButtonElement.TextColor.ToUwp());
PillButtonElement.BackgroundColor = Color.Transparent; // hack
button.Style = Resources["PillButtonStyle"] as Windows.UI.Xaml.Style;
}
}
private const string PillButtonStyleDictionary = @"<ResourceDictionary
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<x:Double x:Key=""PillCornerRadius"">0</x:Double>
<x:Double x:Key=""PillBorderWidth"">0</x:Double>
<SolidColorBrush
x:Key=""PillBackgroundColor""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillTextColor""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillFillColorOnHover""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillFillColorOnPressed""
Color=""Black"" />
<SolidColorBrush
x:Key=""PillTextColorOnHoverOrPressed""
Color=""Black"" />
<Style
x:Key=""PillButtonStyle""
TargetType=""Button"">
<Setter
Property=""Background""
Value=""{ThemeResource SystemControlBackgroundBaseLowBrush}"" />
<Setter
Property=""Foreground""
Value=""{ThemeResource SystemControlForegroundBaseHighBrush}"" />
<Setter
Property=""BorderBrush""
Value=""{ThemeResource SystemControlForegroundTransparentBrush}"" />
<Setter
Property=""BorderThickness""
Value=""{ThemeResource ButtonBorderThemeThickness}"" />
<Setter
Property=""Padding""
Value=""8,4,8,4"" />
<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=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""Button"">
<Grid x:Name=""RootGrid"">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name=""CommonStates"">
<VisualState x:Name=""Normal"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillBackgroundColor}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColor}"" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""PointerOver"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillFillColorOnHover}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColorOnHoverOrPressed}"" />
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""Pressed"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillFillColorOnPressed}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{StaticResource PillTextColorOnHoverOrPressed}"" />
</ObjectAnimationUsingKeyFrames>
<PointerDownThemeAnimation Storyboard.TargetName=""RootGrid"" />
</Storyboard>
</VisualState>
<VisualState x:Name=""Disabled"">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Fill"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlBackgroundBaseLowBrush}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""ContentPresenter""
Storyboard.TargetProperty=""Foreground"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlDisabledBaseMediumLowBrush}"" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetName=""Pill""
Storyboard.TargetProperty=""Stroke"">
<DiscreteObjectKeyFrame
KeyTime=""0""
Value=""{ThemeResource SystemControlDisabledTransparentBrush}"" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle
x:Name=""Pill""
RadiusX=""{StaticResource PillCornerRadius}""
RadiusY=""{StaticResource PillCornerRadius}""
Stroke=""{TemplateBinding BorderBrush}""
StrokeThickness=""{StaticResource PillBorderWidth}"" />
<ContentPresenter
x:Name=""ContentPresenter""
Padding=""{TemplateBinding Padding}""
HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}""
VerticalAlignment=""Center""
VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""
AutomationProperties.AccessibilityView=""Raw""
Content=""{TemplateBinding Content}""
ContentTemplate=""{TemplateBinding ContentTemplate}""
ContentTransitions=""{TemplateBinding ContentTransitions}"" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>";
}
}
以防万一,这里是用于从 Xamarin Color 转换为 UWP Color 的 ToUwp 颜色扩展,在该代码中使用:
internal static class ColorExtensions
{
public static Color ToUwp(this Xamarin.Forms.Color color)
{
return Color.FromArgb((byte)(color.A * 255),
(byte)(color.R * 255),
(byte)(color.G * 255),
(byte)(color.B * 255));
}
}