【发布时间】:2017-12-08 21:11:12
【问题描述】:
我对使用 VisualStateManager 的自定义控件有疑问。
状态之间的转换按我的预期工作,但我不明白如何设置初始状态。
我已经做了完整的例子来说明这个问题。 此示例使用基于 ButtonBase 的自定义控件。
该控件有一个 VisualState 组,该组具有“已检查”和“未检查”两种状态。 这是控件的 C# 代码。
using System.Windows;
using System.Windows.Controls.Primitives;
namespace VisualStateTest
{
[TemplateVisualStateAttribute(Name = "Checked", GroupName = "CheckStates")]
[TemplateVisualStateAttribute(Name = "Unchecked", GroupName = "CheckStates")]
public class CustomButton : ButtonBase
{
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register ( "IsChecked",
typeof(bool),
typeof(CustomButton),
new FrameworkPropertyMetadata ( false,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
OnCheckedChanged ) ) ;
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public static void OnCheckedChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
var button = d as CustomButton ;
if ((bool)e.NewValue)
{
VisualStateManager.GoToState(button, "Checked", true);
}
else
{
VisualStateManager.GoToState(button, "Unchecked", true);
}
}
}
}
设置 IsChecked 属性时,控件模板在左侧和顶部显示阴影。
(我知道设计很差,但这不是图形设计的问题。)
这是控制模板:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VisualStateTest">
<Style TargetType="{x:Type local:CustomButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Border x:Name="outerborder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0"
Storyboard.TargetName="topshadow"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1.0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0"
Storyboard.TargetName="leftshadow"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="1.0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0"
Storyboard.TargetName="topshadow"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="0:0:0"
Storyboard.TargetName="leftshadow"
Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Cursor="Hand" ClipToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="lineargradient"
Grid.RowSpan="2" Grid.ColumnSpan="2"
Stroke="#7F000000"
StrokeThickness="0">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#20808080"/>
<GradientStop Color="#008A8A8A" Offset="0.5"/>
<GradientStop Color="#20000000" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<ContentPresenter HorizontalAlignment="Center"
x:Name="contentPresenter"
Grid.RowSpan="2" Grid.ColumnSpan="2"
VerticalAlignment="Center" />
<Rectangle x:Name="topshadow" Fill="#40000000" Grid.Row="0" Grid.ColumnSpan="2" Opacity="0">
<Rectangle.Effect>
<BlurEffect Radius="3"/>
</Rectangle.Effect>
</Rectangle>
<Rectangle x:Name="leftshadow" Fill="#40000000" Grid.Row="1" Grid.Column="0" Opacity="0">
<Rectangle.Effect>
<BlurEffect Radius="3"/>
</Rectangle.Effect>
</Rectangle>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
对于这个测试,我定义了一个带有两个布尔属性(Option1 和 Option2)的 ViewModel。 其中一个属性的初始值为 false,另一个为 true。
主窗口有两个 CustomButton 控件,连接到两个选项属性,还有两个复选框连接到相同的属性。
这是视图模型的完整代码...
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace VisualStateTest
{
public class ViewModel : INotifyPropertyChanged
{
// Events for INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private bool _option1 = false ;
private bool _option2 = true ;
public ICommand Notify1Command { get; private set; }
public ICommand Notify2Command { get; private set; }
public ViewModel()
{
Notify1Command = new RelayCommand (new Action<object>(Execute_Notify1Command));
Notify2Command = new RelayCommand (new Action<object>(Execute_Notify2Command));
}
public bool Option1
{
get { return _option1 ; }
set
{
_option1 = value ;
NotifyPropertyChanged ( "Option1" ) ;
}
}
public bool Option2
{
get { return _option2 ; }
set
{
_option2 = value ;
NotifyPropertyChanged ( "Option2" ) ;
}
}
public void Execute_Notify1Command ( object value )
{
Option1 = !Option1 ;
}
public void Execute_Notify2Command ( object value )
{
Option2 = !Option2 ;
}
private void NotifyPropertyChanged ( String propertyName )
{
if ( this.PropertyChanged != null )
{
this.PropertyChanged ( this, new PropertyChangedEventArgs(propertyName) ) ;
}
}
}
}
和主窗口...
<Window x:Class="VisualStateTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
xmlns:local="clr-namespace:VisualStateTest"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<local:CustomButton Grid.Row="0" Grid.Column="0"
Command="{Binding Notify1Command}"
IsChecked="{Binding Option1, Mode=OneWay}"
Content="Option 1"
Margin="20"/>
<local:CustomButton Grid.Row="0" Grid.Column="1"
Command="{Binding Notify2Command}"
IsChecked="{Binding Option2, Mode=OneWay}"
Content="Option 2"
Margin="20"/>
<CheckBox Grid.Row="1" Grid.Column="0"
IsChecked="{Binding Option1}"
Content="Option 1"
Margin="20 5"/>
<CheckBox Grid.Row="1" Grid.Column="1"
IsChecked="{Binding Option2}"
Content="Option 2"
Margin="20 5"/>
</Grid>
</Window>
程序启动后,单击自定义按钮或复选框可切换选项并显示或隐藏阴影效果。
这是“正常”状态下的样子:
问题在于程序何时启动。虽然Option2初始化为true,并且调用了函数VisualStateManager.GoToState,但是阴影效果并没有显示出来。
这是启动时的样子。
右边的复选框表示选项2为真,但阴影效果不存在。
我确信我错过了一小部分拼图。如果有帮助,我可以上传示例程序。
如果这太详细了,我很抱歉。
【问题讨论】:
标签: c# wpf xaml visualstatemanager