开发者

WPF VisualStateManager - How to animate properties inside a templated child

I have a UserControl that contains an ItemsControl with a custom ItemsPanel, with a dependency property called "MaxColumns". I'd like to define a VisualState (at the UserControl level) that can animate the "MaxColumns" property on t开发者_如何学Che custom panel.

In essence, the XAML looks something like:

<Grid x:Name="LayoutRoot">
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="MyCoolState">
      <VisualState x:Name="Normal" />
      <VisualState x:Name="NotNormal">
        <Storyboard>
          <Int32Animation Duration="0"
                          Storyboard.TargetName="Details"
                          Storyboard.TargetProperty="(ItemsControl.ItemsPanel).(local:CoolPanel.MaxColumns)"
                          To="4" />
        </Storyboard>
      </VisualState>
    </VisualStateGroup>
  <VisualStateManager>
  <ItemsControl x:Name="Details">
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <local:CoolPanel x:Name="MyCoolPanel"
                         MaxColumns="1" />
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
  </ItemsControl>
</Grid>

However, I cannot for the life of me figure out what the right syntax is for the animation? If I use the syntax shown above, I get the error: "'ItemsPanel' property does not point to a DependencyObject in path '(0).(1)'". I'm presuming this is because it's technically pointing to a ItemsPanelTemplate?

If I refer to "MyCoolPanel" directly in the Storyboard.TargetName property, I get an error about Name scope (presumably because "MyCoolPanel" isn't in the namescope of LayoutRoot). I don't know if there is a way to qualify name scope in "TargetName"?

Does anyone have a solution for this? It seems like something that should be doable without resorting to custom attached properties? I mean, I'm not opposed to attached properties, but I feel like you ought to be able to do this directly in the XAML?


Okay, indeed the ItemsPanel is not a real object but a template with which the object is going to be created. So technically your reference is not going to work.

I've got the following about implementation:

  1. you set some attached property on the ItemsPanel (which is anyway a template), but on the ItemsControl itself.
  2. You bind the CoolPanel's MaxColumns to that attached property using RelativeSource FindAncestor.

Well, you could omit the attached property, and use Tag for it :-) Indeed, the ItemsControl is totally in your control, so there is no crime in abusing the Tag a little bit.

So the code would be like this:

<Grid x:Name="LayoutRoot">
  <VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="MyCoolState">
      <VisualState x:Name="Normal" />
      <VisualState x:Name="NotNormal">
        <Storyboard>
          <Int32Animation Duration="0"
                          Storyboard.TargetName="Details"
                          Storyboard.TargetProperty="Tag"
                          To="4" />
        </Storyboard>
      </VisualState>
    </VisualStateGroup>
  <VisualStateManager>
  <ItemsControl x:Name="Details" Tag="3">
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <local:CoolPanel
            MaxColumns="{Binding Tag, RelativeSource={RelativeSource FindAncestor,
                                           AncestorType=ItemsControl}}" />
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
  </ItemsControl>
</Grid>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜