【问题标题】:How to change templated control's properties from VisualState?如何从 VisualState 更改模板化控件的属性?
【发布时间】:2013-04-12 01:14:30
【问题描述】:

如果我从控件模板中定义 VisualStates,是否可以从情节提要中更改模板化控件本身的属性?这是一个简单的例子:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Window.Template>
    <ControlTemplate TargetType="{x:Type Window}">
      <Grid>
        <VisualStateManager.VisualStateGroups>
          <VisualStateGroup x:Name="WindowStyleStates"
                            x:Uid="WindowStyleStates">
            <Storyboard x:Uid="Storyboard_1">
              <ObjectAnimationUsingKeyFrames Storyboard.TargetName="?????"
                                             Storyboard.TargetProperty="ResizeMode">
                <DiscreteObjectKeyFrame KeyTime="0"
                                        Value="CanResizeWithGrip" />
              </ObjectAnimationUsingKeyFrames>
            </Storyboard>
          </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
      </Grid>
    </ControlTemplate>
  </Window.Template>
</Window>

问题在于情节提要只能访问网格中定义的对象。如果我正在为 Window 定义 controltemplate,为什么我不能更改我正在模板化的 Window 上的值。

【问题讨论】:

    标签: c# wpf xaml visualstates


    【解决方案1】:

    您不需要ControlTemplate 即可访问VisualStateManager。这应该可以解决问题,虽然我没有尝试过。

    <Window x:Name="YourWindow" ...>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="WindowStyleStates" x:Uid="WindowStyleStates">
                <Storyboard x:Uid="Storyboard_1">
                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="YourWindow"
                                                   Storyboard.TargetProperty="ResizeMode">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="CanResizeWithGrip"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Window>
    

    但在后面的代码中 VisualStateManager.GoToState(...) 似乎存在问题,至少对于 .Net 3.5 而言。但是有一个workaround。我不知道这对你是否重要。

    回答标题中的问题:我认为您误解了控件模板背后的概念。一个小例子……

    <!-- A simple button with round corners -->
    <Button>
      <Button.Template>
        <ControlTemplate>
          <Border x:Name="ButtonBorder"
                  CornerRadius="10"
                  BorderBrush="{TemplateBinding BorderBrush}"
                  BorderThickness="{TemplateBinding BorderThickness}">
            <Grid>
              <ContentPresenter x:Name="ButtonContent"
                                Content="{TemplateBinding Content}" />
              <Border x:Name="ButtonBackground"
                      Background="{TemplateBinding Background}"
                      BorderBrush="{x:Null}"
                      BorderThickness="0" />
            </Grid>
          </Border>
        </ControlTemplate>
      </Button.Template>
    </Button>
    

    如您所见,模板为Button 赋予了新外观。此外,视觉行为也被覆盖。好吧,在这种情况下,没有视觉行为,尽管按钮会按预期工作。
    要定义视觉行为,您可以使用VisualStateManager 和预定义状态或您的自定义状态。但是您只能修改模板中的元素,这是合理的,因为您想重新实现按钮的外观。因此,您需要将VisualStateManager 添加到ButtonBorder

    【讨论】:

    • 将视觉状态移出控件模板确实可以部分修改模板化的窗口属性。我不认为这是首选方法,但是因为 Expression Blend 甚至不承认放置在控件本身上的状态。