我正在将RadioButton修改为用作分段控件,从iOS中可以看出。
我修改过的Style看起来像这样:
<Style TargetType="control:SegmentRadioButton" x:Key="SegmentedRadioButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:SegmentRadioButton">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding SelectedTextColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding TintColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
我的派生控件看起来像这样:
public class SegmentRadioButton : RadioButton
{
public static readonly DependencyProperty SelectedTextColorProperty = DependencyProperty.Register(
"SelectedTextColor",
typeof(SolidColorBrush), typeof(SegmentRadioButton),
new PropertyMetadata(default(SolidColorBrush), new PropertyChangedCallback(OnSelectedTextChanged)));
public SolidColorBrush SelectedTextColor
{
get => (SolidColorBrush) GetValue(SelectedTextColorProperty);
set => SetValue(SelectedTextColorProperty, value);
}
public static readonly DependencyProperty TintColorProperty = DependencyProperty.Register(
"TintColor", typeof(SolidColorBrush), typeof(SegmentRadioButton), new PropertyMetadata(default(SolidColorBrush), new PropertyChangedCallback(OnTintChanged)));
public SolidColorBrush TintColor
{
get => (SolidColorBrush) GetValue(TintColorProperty);
set => SetValue(TintColorProperty, value);
}
public SegmentRadioButton()
{
}
private static void OnTintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SegmentRadioButton segment)
{
segment.BorderBrush = (SolidColorBrush) e.NewValue;
if (segment.IsChecked ?? false)
{
// Hack to make the selected segment re-draw.
segment.IsChecked = false;
segment.IsChecked = true;
}
}
}
private static void OnSelectedTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SegmentRadioButton segment)
{
if (segment.IsChecked ?? false)
{
// Hack to make the selected segment re-draw.
segment.IsChecked = false;
segment.IsChecked = true;
}
}
}
}
问题:这实际上有效,但我想知道是否有更优雅且更少 hacky 的方式来进行TintColor
和SelectedTextColor
更新?从两个内联注释中可以看出,我正在通过翻转IsChecked
值来更新/重新绘制
答案 0 :(得分:1)
我最终使用了Visual State Manager和一个简单的Refresh。
代码在GitHub上:Plugin.SegmentedControl
以下是代码的关键部分:
public class SegmentRadioButton : RadioButton
{
public static readonly DependencyProperty SelectedTextColorProperty = DependencyProperty.Register(
"SelectedTextColor",
typeof(SolidColorBrush), typeof(SegmentRadioButton),
new PropertyMetadata(default(SolidColorBrush), new PropertyChangedCallback(OnSelectedTextChanged)));
public SolidColorBrush SelectedTextColor
{
get => (SolidColorBrush) GetValue(SelectedTextColorProperty);
set => SetValue(SelectedTextColorProperty, value);
}
public static readonly DependencyProperty TintColorProperty = DependencyProperty.Register(
"TintColor", typeof(SolidColorBrush), typeof(SegmentRadioButton), new PropertyMetadata(default(SolidColorBrush), new PropertyChangedCallback(OnTintChanged)));
public SolidColorBrush TintColor
{
get => (SolidColorBrush) GetValue(TintColorProperty);
set => SetValue(TintColorProperty, value);
}
public static readonly DependencyProperty DisabledColorProperty = DependencyProperty.Register(
"DisabledColor", typeof(SolidColorBrush), typeof(SegmentRadioButton), new PropertyMetadata(default(SolidColorBrush), new PropertyChangedCallback(OnDisabledColorChanged)));
public SolidColorBrush DisabledColor
{
get => (SolidColorBrush) GetValue(DisabledColorProperty);
set => SetValue(DisabledColorProperty, value);
}
public SegmentRadioButton()
{
this.IsEnabledChanged += SegmentRadioButton_IsEnabledChanged;
}
private void SegmentRadioButton_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is SegmentRadioButton segment)
{
Refresh(segment);
}
}
private static void OnTintChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SegmentRadioButton segment)
{
segment.BorderBrush = (SolidColorBrush) e.NewValue;
Refresh(segment);
}
}
private static void OnDisabledColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SegmentRadioButton segment)
{
segment.BorderBrush = (SolidColorBrush)e.NewValue;
Refresh(segment);
}
}
private static void OnSelectedTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SegmentRadioButton segment)
{
Refresh(segment);
}
}
private static void Refresh(SegmentRadioButton segment)
{
// Go to "Indeterminate" State to ensure that the GotoState is refreshed even if the state is the same.
// Necessary because properties might have changed even when the state have not.
VisualStateManager.GoToState(segment, "Indeterminate", false);
if (segment.IsChecked ?? false)
{
VisualStateManager.GoToState(segment, segment.IsEnabled ? "Checked" : "DisabledAndChecked", false);
}
else
{
VisualStateManager.GoToState(segment, segment.IsEnabled ? "Unchecked" : "DisabledAndUnchecked", false);
}
}
}
风格:
<Style TargetType="control1:SegmentRadioButton" x:Key="SegmentedRadioButtonStyle">
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="VerticalContentAlignment" Value="Center"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="8,4,8,4"></Setter>
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control1:SegmentRadioButton">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AutomationProperties.AccessibilityView="Raw"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid" />
</Storyboard>
</VisualState>
<VisualState x:Name="DisabledAndChecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding DisabledColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding SelectedTextColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding DisabledColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="DisabledAndUnchecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding DisabledColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding DisabledColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding TintColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding SelectedTextColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding TintColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding TintColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding TintColor, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>