WPF ControlTemplate Setter停止工作

时间:2018-04-20 05:51:07

标签: wpf xaml controltemplate

我有一个控件模板,我正在为自定义标签控件创建。这是模板的代码(MyTabItem只是继承自TabItem,并添加了一个' Expanded' DependencyProperty,这是一个bool):

<ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}">
    <Grid x:Name="templateRoot" Height="50" UseLayoutRounding="True"  Margin="0,0,-500,0"
                    Effect="{StaticResource EffectDropShadowNoOffset}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition Width="Auto" x:Name="ColExpander" />
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" Background="Blue" x:Name="PART_ImageBackground">

        </Border>
        <Border Grid.Column="1" x:Name="PART_OpacityMask" Background="Transparent">
            <Border Background="Blue" x:Name="PART_MaskScalingBorder">
                <Border.RenderTransform>
                    <ScaleTransform ScaleX="0" ScaleY="1" />
                </Border.RenderTransform>
            </Border>
        </Border>
        <Border Grid.Column="1" Background="Orange" x:Name="PART_ContentBackground">
            <Border.OpacityMask>
                <VisualBrush Visual="{Binding ElementName=PART_OpacityMask}" />
            </Border.OpacityMask>
            <ContentPresenter x:Name="contentPresenter" 
                          ContentTemplate="{TemplateBinding HeaderTemplate}"
                          Content="{TemplateBinding Header}" 
                          ContentStringFormat="{TemplateBinding HeaderStringFormat}" 
                          ContentSource="Header" Focusable="False" 
                          HorizontalAlignment="Left" 
                          Margin="15,0" 
                          Grid.Column="1"
                          RecognizesAccessKey="True" 
                          IsHitTestVisible="False"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                          VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
        </Border>
        <Border Background="Transparent" x:Name="PART_HitBackground" />
    </Grid>
    <ControlTemplate.Triggers>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Expanded, RelativeSource={RelativeSource Self}}" Value="False" />
                <Condition Binding="{Binding IsMouseOver, ElementName=PART_HitBackground}" Value="True" />
            </MultiDataTrigger.Conditions>
            <MultiDataTrigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.25" To="1"
                                         BeginTime="0:0:0.5"
                                         Storyboard.TargetName="PART_MaskScalingBorder"
                                         Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </MultiDataTrigger.EnterActions>
            <MultiDataTrigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.25" To="0"
                                         Storyboard.TargetName="PART_MaskScalingBorder"
                                         Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </MultiDataTrigger.ExitActions>
            <Setter TargetName="PART_HitBackground" Property="Grid.ColumnSpan" Value="2" />
        </MultiDataTrigger>
        <MultiDataTrigger>
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding IsMouseOver, ElementName=PART_HitBackground}" Value="False" />
                <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False" />
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" TargetName="PART_ContentBackground" Value="{StaticResource BrSideMenu}" />
            <Setter Property="Background" TargetName="PART_ImageBackground" Value="{StaticResource BrSideMenu}" />
            <Setter Property="Effect" TargetName="templateRoot">
                <Setter.Value>
                    <x:Null />
                </Setter.Value>
            </Setter>

        </MultiDataTrigger>
        <Trigger Property="Expanded" Value="True">
            <Trigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.25" To="1"
                                         Storyboard.TargetName="PART_MaskScalingBorder"
                                         Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColExpander" 
                                                       Storyboard.TargetProperty="Width"
                                                       Duration="0" BeginTime="0">
                            <DiscreteObjectKeyFrame>
                                <DiscreteObjectKeyFrame.Value>
                                    <GridLength>*</GridLength>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="templateRoot" 
                                                       Storyboard.TargetProperty="Margin"
                                                       Duration="0" BeginTime="0">
                            <SplineThicknessKeyFrame Value="0" />
                        </ThicknessAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ColExpander" 
                                                       Storyboard.TargetProperty="Width"
                                                       Duration="0" BeginTime="0:0:0.25">
                            <DiscreteObjectKeyFrame>
                                <DiscreteObjectKeyFrame.Value>
                                    <GridLength>Auto</GridLength>
                                </DiscreteObjectKeyFrame.Value>
                            </DiscreteObjectKeyFrame>
                        </ObjectAnimationUsingKeyFrames>
                        <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="templateRoot" 
                                                       Storyboard.TargetProperty="Margin"
                                                       Duration="0" BeginTime="0:0:0.25">
                            <SplineThicknessKeyFrame Value="0,0,-500,0" />
                        </ThicknessAnimationUsingKeyFrames>
                        <DoubleAnimation Duration="0:0:0.25" To="0"
                                         Storyboard.TargetName="PART_MaskScalingBorder"
                                         Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.ExitActions>
            <Setter TargetName="PART_HitBackground" Property="Grid.ColumnSpan" Value="2" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

这相当于我想做的95%。以下是该行动的快速视频:

https://www.screencast.com/t/qNHvJaSo4bp

如您所见,当您第一次开始时,当您将鼠标悬停在它上面时,会给出文本弹出窗口,这很棒。当我展开它时,它显示全部项目的全宽,也很棒。然而,一旦我崩溃了面板,弹出消息就会消失......为什么?据我所知,退出操作将一切设置回到进入操作之前的状态。当我这样做时,我也没有在VS / Blend中收到任何绑定错误或任何内容,这些突发事件根本就没有显示出来。

任何人都可以放弃任何光明吗?

1 个答案:

答案 0 :(得分:0)

好的,我想出来了......是的。我仍然不知道为什么会这样做,但我找到了一种更好的方法来“抚摸猫”,我认为它更干净,实际上是完全有效的。

我最终做的是拆分模板。我为具有弹出窗口和必需触发器的非扩展状态创建了一个ControlTemplate。然后我创建了一个单独的模板,它是没有触发器的扩展状态,基本上是一个简单的网格。然后在我的样式中,我在'Expanded'属性上创建了一个触发器,下面的setter根据父级的状态更改了展开的一个和折叠的一个之间的控制模板。

请注意,在我的情况下,因为关闭是动画的,只是让它作为一个setter使得全宽度部分立即消失,看起来有点滑稽。既然你不能(据我所知)使用故事板来设置不同的内容模板,我所做的就是在'Expanded'属性的触发器中我创建了故事板并且只是使用了BooleanAnimationUsengKeyFrame来告诉它等待直到完成切换模板。

以下是我的模板:

倒塌的状态

 <ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}" x:Key="CtTabCollapsed">
    <!--Template root with a negative margin allows flyouts to be visible.  Shadow is on grid to allow for multiple inner borders-->
    <Grid x:Name="templateRoot" Height="50" UseLayoutRounding="True"  Margin="0,0,-500,0"
          Effect="{StaticResource EffectDropShadowNoOffset}" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition Width="Auto" x:Name="ColExpander" />
        </Grid.ColumnDefinitions>

        <!--This border sets the background and holds the image.  No opacity necessary as it is always visible-->
        <Border x:Name="PART_ImageBackground" Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedColor}">
            <Image Width="32" Height="32" x:Name="PART_Image" Stretch="Uniform" 
               Source="{Binding IconActive, RelativeSource={RelativeSource TemplatedParent}}"
               HorizontalAlignment="Center" VerticalAlignment="Center" IsHitTestVisible="False" />
        </Border>

        <!--This is the opacity mask that shows or hides the flyout portion-->
        <Border Grid.Column="1" x:Name="PART_OpacityMask" Background="Transparent">
            <!--Outer border is the actual visual to use and is transparent.  Inner border is opaque to show visible portions and is the portion that is scaled-->
            <Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedColor}" x:Name="PART_MaskScalingBorder">
                <Border.RenderTransform>
                    <ScaleTransform ScaleX="0" ScaleY="1" />
                </Border.RenderTransform>
            </Border>
        </Border>

        <!--This sets the background and holds the content for the non image portion.  Opacity mask is set by the opacity mask border that resides in the same column-->
        <Border Grid.Column="1" Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedColor}" x:Name="PART_ContentBackground">
            <Border.OpacityMask>
                <VisualBrush Visual="{Binding ElementName=PART_OpacityMask}" />
            </Border.OpacityMask>
            <ContentPresenter x:Name="contentPresenter" 
                                      ContentTemplate="{TemplateBinding HeaderTemplate}"
                                      Content="{TemplateBinding Header}" 
                                      ContentStringFormat="{TemplateBinding HeaderStringFormat}" 
                                      ContentSource="Header" Focusable="False" 
                                      HorizontalAlignment="Left" 
                                      Margin="15,0" 
                                      RecognizesAccessKey="True" 
                                      IsHitTestVisible="False"
                                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                      VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
        </Border>

        <!--This is what we use for triggers to determine IsMouseOver.  If we leave it as templated parent it does the whole page and if templateRoot it
                    does the full width even if it's hidden by opacity mask, so we need a separate hit test border here and triggers to make it the right width-->
        <Border Background="Transparent" x:Name="PART_HitBackground" Grid.Column="0" />
    </Grid>
    <ControlTemplate.Triggers>
        <!--Trigger for expanding the flyout.  Active if not expanded and mouse is over-->
        <DataTrigger Binding="{Binding IsMouseOver, ElementName=PART_HitBackground}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.25" To="1"
                                                     BeginTime="0:0:0.5"
                                                     Storyboard.TargetName="PART_MaskScalingBorder"
                                                     Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Duration="0:0:0.25" To="0"
                                                     Storyboard.TargetName="PART_MaskScalingBorder"
                                                     Storyboard.TargetProperty="RenderTransform.ScaleX">
                            <DoubleAnimation.EasingFunction>
                                <QuadraticEase EasingMode="EaseInOut" />
                            </DoubleAnimation.EasingFunction>
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.ExitActions>
            <!--If we expand then the hit test border needs to cover both columns so it doesn't go away when they move the mouse over the text.-->
            <Setter TargetName="PART_HitBackground" Property="Grid.ColumnSpan" Value="2" />
        </DataTrigger>
        <MultiDataTrigger>
            <!--Trigger for setting all elements to inactive state if the tab is not selected and the mouse is not over.-->
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding IsMouseOver, ElementName=PART_HitBackground}" Value="False" />
                <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False" />
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" TargetName="PART_ImageBackground" Value="{StaticResource BrSideMenu}" />
            <Setter Property="Background" TargetName="PART_ContentBackground" Value="{StaticResource BrSideMenu}" />
            <Setter Property="Effect" TargetName="templateRoot">
                <Setter.Value>
                    <x:Null />
                </Setter.Value>
            </Setter>
            <Setter TargetName="PART_Image" Property="Source" Value="{Binding IconInactive,RelativeSource={RelativeSource TemplatedParent}}" />
        </MultiDataTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

扩展状态

<ControlTemplate TargetType="{x:Type graphicElements:MyTabItem}" x:Key="CtTabExpanded">
    <Grid x:Name="templateRoot" Height="50" UseLayoutRounding="True"  Margin="0"
          Effect="{StaticResource EffectDropShadowNoOffset}"
          Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedColor}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition Width="*" x:Name="ColExpander" />
        </Grid.ColumnDefinitions>

        <Image Width="32" Height="32" x:Name="PART_Image" Stretch="Uniform" 
                               Source="{Binding IconActive, RelativeSource={RelativeSource TemplatedParent}}"
                               HorizontalAlignment="Center" VerticalAlignment="Center" IsHitTestVisible="False" />

        <ContentPresenter x:Name="contentPresenter" 
                                      ContentTemplate="{TemplateBinding HeaderTemplate}"
                                      Content="{TemplateBinding Header}" 
                                      ContentStringFormat="{TemplateBinding HeaderStringFormat}" 
                                      ContentSource="Header" Focusable="False" 
                                      HorizontalAlignment="Left" 
                                      Margin="15,0" 
                          Grid.Column="1"
                                      RecognizesAccessKey="True" 
                                      IsHitTestVisible="False"
                                      SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                      VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
    </Grid>
    <ControlTemplate.Triggers> 
        <MultiDataTrigger>
            <!--Trigger for setting all elements to inactive state if the tab is not selected and the mouse is not over.-->
            <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding IsMouseOver, ElementName=templateRoot}" Value="False" />
                <Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False" />
            </MultiDataTrigger.Conditions>
            <Setter Property="Background" TargetName="templateRoot" Value="{StaticResource BrSideMenu}" />
            <Setter Property="Effect" TargetName="templateRoot">
                <Setter.Value>
                    <x:Null />
                </Setter.Value>
            </Setter>
            <Setter TargetName="PART_Image" Property="Source" Value="{Binding IconInactive,RelativeSource={RelativeSource TemplatedParent}}" />
        </MultiDataTrigger>            
    </ControlTemplate.Triggers>
</ControlTemplate>

风格

<Style TargetType="{x:Type graphicElements:MyTabItem}">
    <Setter Property="Foreground" Value="Black"/>
    <Setter Property="SelectedColor" Value="{Binding ApplicationColor, RelativeSource={RelativeSource AncestorType=graphicElements:MyTabControl}}" />
    <Setter Property="Expanded" Value="False" />
    <Setter Property="BorderBrush" Value="#FFACACAC"/>
    <Setter Property="Margin" Value="0"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="SnapsToDevicePixels" Value="False" />
    <Setter Property="Template" Value="{StaticResource CtTabCollapsed}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding IsSideBarExpanded, RelativeSource={RelativeSource AncestorType=graphicElements:MyTabControl}}" Value="True">
            <DataTrigger.EnterActions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="Expanded" BeginTime="0" Duration="0">
                            <DiscreteBooleanKeyFrame Value="True" />
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="Expanded" BeginTime="0:0:0.25" Duration="0">
                            <DiscreteBooleanKeyFrame Value="False" />
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.ExitActions>
        </DataTrigger>
        <Trigger Property="Expanded" Value="True">
            <Setter Property="Template" Value="{StaticResource CtTabExpanded}" />
        </Trigger>
    </Style.Triggers>
</Style>

我暂时不会接受我自己的答案,以防其他人有更好的答案,但这似乎有效。

修改

对此进行轻微修改。作为其他任何人的FYI,我发现如果您基于这些创建新样式,上面样式的故事板找不到它们的属性。关于如何处理,请看@Andy在this thread上的精彩答案。