【发布时间】:2020-10-14 20:01:22
【问题描述】:
我创建了一个自定义控件,我想在控件加载后立即更改该控件的视觉状态。
我想使用绑定模式从依赖属性中获取颜色,以便动态设置它们。
我找到了一种方法来绑定属性并避免可冻结属性的限制:分离情节提要并使其成为资源,正如您在 xaml 代码中看到的那样。
更改视觉状态(在我的情况下为“已激活”)可以正常工作,并且它使用绑定属性中的值,但是当我想在控件加载后立即更改状态时问题就出现了 :绑定变得“损坏”,从属性获得的颜色似乎是“空”(或透明)。
如果我使用静态颜色而不是绑定属性(例如绿色或#ffffffff),即使在加载后立即正确加载颜色,但这种方式在我的情况下不可用,因为这意味着颜色将“不可更改”。
xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1">
<Style TargetType="{x:Type local:Switcher}">
<Setter Property="Background" Value="#FF3F3F46"/>
<Setter Property="BackgroundOnActivated" Value="#FF2D2D30"/>
<Setter Property="IsActivated" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:Switcher}">
<Grid>
<Grid.Resources>
<Storyboard x:Key="SwitcherOnActivated">
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PART_Border"
Storyboard.TargetProperty="Background">
<!-- with Value="Green" animation works correctly -->
<DiscreteObjectKeyFrame KeyTime="0"
Value="{Binding BackgroundOnActivated,
RelativeSource={RelativeSource TemplatedParent}}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<!-- it goes to "Activated" when IsActivated becomes true-->
<VisualState x:Name="Activated"
Storyboard="{StaticResource SwitcherOnActivated}"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="PART_Border"
Background="{TemplateBinding Background}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
背后的代码
public class Switcher : Control
{
public Brush BackgroundOnActivated
{
get => (Brush)GetValue(BackgroundOnActivatedProperty);
set => SetValue(BackgroundOnActivatedProperty, value);
}
public static readonly DependencyProperty BackgroundOnActivatedProperty =
DependencyProperty.Register(nameof(BackgroundOnActivated), typeof(Brush), typeof(Switcher));
public bool IsActivated
{
get => (bool)GetValue(IsActivatedProperty);
set => SetValue(IsActivatedProperty, value);
}
public static readonly DependencyProperty IsActivatedProperty =
DependencyProperty.Register(nameof(IsActivated), typeof(bool), typeof(Switcher),
new PropertyMetadata(false, new PropertyChangedCallback(OnIsActivatedChanged)));
static Switcher()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Switcher), new FrameworkPropertyMetadata(typeof(Switcher)));
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
/*OnApplyTemplate() is used to apply the correct initial
visualstate after loading the control (but it seems to be broken)*/
_ = VisualStateManager.GoToState(this, IsActivated ? "Activated" : "Normal", true);
}
protected virtual void OnActivationChanged()
{
/*If the boolean value of IsActivated is changed, the visualstate
is switched between "Normal" and "Activated"*/
_ = VisualStateManager.GoToState(this, IsActivated ? "Activated" : "Normal", true);
}
private static void OnIsActivatedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Switcher)d).OnActivationChanged();
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
//When I click on the control the boolean value of the property
IsActivated is inverted and this will call OnIsActivatedChanged()*/
base.OnMouseLeftButtonDown(e);
IsActivated = !IsActivated;
}
}
尝试将我的控件添加到 MainWindow.xaml 并将属性 IsActivated 设置为 true,因此加载后状态必须变为已激活,但这会破坏控件并且它变得不可见。
<local:Switcher x:Name="switcher" HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="50,50,0,0" Height="100" Width="100" IsActivated="True"/>
控件处于正常状态:
我想得到什么:
我实际得到的:
即使视觉状态在加载后立即更改,是否有任何解决方案/解决方法来获得正确的颜色?
【问题讨论】:
-
绝对不清楚您的代码 sn-ps 中发生了什么。您忘记提供一些上下文。无论如何,请订阅
Switcher.Loaded事件。 -
只是一种预感,但请尝试将 Storyboard 从 Grid.Resources 中移出,并使其成为 VIsualState 的直接子级,而无需 x:Key。这可能是资源解析问题。
-
您想要实现的目标似乎已经存在——它是一个切换按钮。只需重新模板即可。
-
@Keithernet 不起作用,因为要使用这种方式,必须冻结属性,并且不能使用绑定。
标签: wpf binding custom-controls loading visualstates