带有支持所有方向的动画的扩展器
Expander with animation that support all direction
我的 Objective: 是创建一个带有展开动画的自定义扩展器,应该支持所有方向。
我尝试了什么: 我在 this 的帮助下实施了一个解决方案。我修改了它并使其根据我的需要工作。我的解决方案将与 Up
一起工作,即使在 Down
Direction 下我也可以让它工作。
我现在的问题是什么:我无法让它适用于所有方向。我尝试并未能将 ExpanderDirection
设置为 Left
和 Right
.
这是我的解决方案:
扩展模板:
<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
<Border x:Name="ExpanderButtonBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Transparent"
Grid.ColumnSpan="2"/>
<Ellipse Name="Circle"
Grid.Column="0"
Stroke="DarkGray"
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<Path x:Name="Arrow"
Grid.Column="0"
Data="M 1,1.5 L 4.5,5 8,1.5"
Stroke="#FF666666"
StrokeThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5"
>
<Path.RenderTransform>
<RotateTransform Angle="0"/>
</Path.RenderTransform>
</Path>
<ContentPresenter x:Name="HeaderContent"
Grid.Column="1"
Margin="4,0,0,0"
ContentSource="Content"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- Animate arrow when toggled-->
<Trigger Property="IsChecked"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="180"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Stroke"
Value="#FF3C7FB1"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#222"
TargetName="Arrow"/>
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Stroke"
Value="#FF526C7B"
TargetName="Circle"/>
<Setter Property="StrokeThickness"
Value="1.5"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#FF003366"
TargetName="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Slide Out Content Expander's Template,
Uses: AnimatedExpanderButtonTemp from above,
MultiplyConverter in codebehind-->
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
<DockPanel>
<Border Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom" Padding="5,3">
<ToggleButton x:Name="ExpanderButton" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Template="{StaticResource AnimatedExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="1.5,0">
</ToggleButton>
</Border>
<ScrollViewer x:Name="ExpanderContentScrollView" DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Bottom"
>
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ScrollViewer.Height>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Height>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
MultiplyConverter.cs(模板需要)
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
这样使用:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Orange">
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
HorizontalAlignment="Left"
VerticalAlignment="Top" Background="LightGray">
<StackPanel>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
</StackPanel>
</Expander>
</Grid>
<Grid Grid.Row="2" Background="Chocolate" />
</Grid>
以上解决方案不适用于 ExpandDirection="Left" or "Right"
关于如何使它适用于所有方向或任何其他替代方案的任何建议?
经过 8 小时的努力,我找到了解决问题的方法。让我回答我自己的问题,这样可以为其他寻求解决方案的人节省一些时间。
扩展器的控制模板:
<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
<Border x:Name="ExpanderArrow" CornerRadius="5"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Transparent"
Grid.ColumnSpan="2"/>
<Ellipse Name="Circle"
Grid.Column="0"
Stroke="DarkGray"
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<Path x:Name="Arrow"
Grid.Column="0"
Data="M 0 0 L 3,6 L 6,0"
Stroke="#FF666666"
StrokeThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5"
>
<Path.RenderTransform>
<RotateTransform Angle="0"/>
</Path.RenderTransform>
</Path>
<ContentPresenter x:Name="HeaderContent"
Margin="4,0,0,0"
ContentSource="Content"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="DockPanel.Dock"
Value="Right">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3 3 L 0 6 "/>
</Trigger>
<Trigger Property="DockPanel.Dock"
Value="Left">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3 3 L 0 6"/>
</Trigger>
<Trigger Property="DockPanel.Dock"
Value="Top">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3,6 L 6,0"/>
</Trigger>
<!-- Animate arrow when toggled-->
<Trigger Property="IsChecked"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="180"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Stroke"
Value="#FF3C7FB1"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#222"
TargetName="Arrow"/>
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Stroke"
Value="#FF526C7B"
TargetName="Circle"/>
<Setter Property="StrokeThickness"
Value="1.5"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#FF003366"
TargetName="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Slide Out Content Expander's Template,
Uses: AnimatedExpanderButtonTemp from above,
MultiplyConverter in codebehind-->
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
<DockPanel>
<!--<Border x:Name="ExpanderButtonBorder" Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom" Padding="5,3">-->
<ToggleButton x:Name="ExpanderButton" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom"
Template="{StaticResource AnimatedExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="0">
</ToggleButton>
<!--</Border>-->
<ScrollViewer x:Name="ExpanderContentScrollView" DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Bottom"
>
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="ExpandDirection" Value="Right">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Right"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Left"/>
<Setter Property="Width" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualWidth" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Up">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Top"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Bottom"/>
<Setter Property="Height" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Left">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Left"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Right"/>
<Setter Property="Width" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualWidth" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Down">
<Setter Property="Height" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
MultiplyConverter.cs(模板需要)
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
演示:
<!-- Small demo of Expander Template above-->
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Orange">
<Expander Template="{StaticResource RevealExpanderTemp}" ExpandDirection="Right"
OverridesDefaultStyle="True"
HorizontalAlignment="Left"
VerticalAlignment="Top" Background="LightGray">
<StackPanel>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
</StackPanel>
</Expander>
</Grid>
<Grid Grid.Row="2" Background="Chocolate" />
</Grid>
代码未完全测试,可能存在错误。
我的 Objective: 是创建一个带有展开动画的自定义扩展器,应该支持所有方向。
我尝试了什么: 我在 this 的帮助下实施了一个解决方案。我修改了它并使其根据我的需要工作。我的解决方案将与 Up
一起工作,即使在 Down
Direction 下我也可以让它工作。
我现在的问题是什么:我无法让它适用于所有方向。我尝试并未能将 ExpanderDirection
设置为 Left
和 Right
.
这是我的解决方案:
扩展模板:
<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
<Border x:Name="ExpanderButtonBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Transparent"
Grid.ColumnSpan="2"/>
<Ellipse Name="Circle"
Grid.Column="0"
Stroke="DarkGray"
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<Path x:Name="Arrow"
Grid.Column="0"
Data="M 1,1.5 L 4.5,5 8,1.5"
Stroke="#FF666666"
StrokeThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5"
>
<Path.RenderTransform>
<RotateTransform Angle="0"/>
</Path.RenderTransform>
</Path>
<ContentPresenter x:Name="HeaderContent"
Grid.Column="1"
Margin="4,0,0,0"
ContentSource="Content"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- Animate arrow when toggled-->
<Trigger Property="IsChecked"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="180"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Stroke"
Value="#FF3C7FB1"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#222"
TargetName="Arrow"/>
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Stroke"
Value="#FF526C7B"
TargetName="Circle"/>
<Setter Property="StrokeThickness"
Value="1.5"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#FF003366"
TargetName="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Slide Out Content Expander's Template,
Uses: AnimatedExpanderButtonTemp from above,
MultiplyConverter in codebehind-->
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
<DockPanel>
<Border Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom" Padding="5,3">
<ToggleButton x:Name="ExpanderButton" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Template="{StaticResource AnimatedExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="1.5,0">
</ToggleButton>
</Border>
<ScrollViewer x:Name="ExpanderContentScrollView" DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Bottom"
>
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ScrollViewer.Height>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</ScrollViewer.Height>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
MultiplyConverter.cs(模板需要)
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
这样使用:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Orange">
<Expander Template="{StaticResource RevealExpanderTemp}"
OverridesDefaultStyle="True"
HorizontalAlignment="Left"
VerticalAlignment="Top" Background="LightGray">
<StackPanel>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
</StackPanel>
</Expander>
</Grid>
<Grid Grid.Row="2" Background="Chocolate" />
</Grid>
以上解决方案不适用于 ExpandDirection="Left" or "Right"
关于如何使它适用于所有方向或任何其他替代方案的任何建议?
经过 8 小时的努力,我找到了解决问题的方法。让我回答我自己的问题,这样可以为其他寻求解决方案的人节省一些时间。
扩展器的控制模板:
<ControlTemplate x:Key="AnimatedExpanderButtonTemp" TargetType="{x:Type ToggleButton}">
<Border x:Name="ExpanderArrow" CornerRadius="5"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Transparent"
Grid.ColumnSpan="2"/>
<Ellipse Name="Circle"
Grid.Column="0"
Stroke="DarkGray"
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
<Path x:Name="Arrow"
Grid.Column="0"
Data="M 0 0 L 3,6 L 6,0"
Stroke="#FF666666"
StrokeThickness="2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5"
>
<Path.RenderTransform>
<RotateTransform Angle="0"/>
</Path.RenderTransform>
</Path>
<ContentPresenter x:Name="HeaderContent"
Margin="4,0,0,0"
ContentSource="Content"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="DockPanel.Dock"
Value="Right">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3 3 L 0 6 "/>
</Trigger>
<Trigger Property="DockPanel.Dock"
Value="Left">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3 3 L 0 6"/>
</Trigger>
<Trigger Property="DockPanel.Dock"
Value="Top">
<Setter Property="Data" TargetName="Arrow" Value="M 0 0 L 3,6 L 6,0"/>
</Trigger>
<!-- Animate arrow when toggled-->
<Trigger Property="IsChecked"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="180"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Arrow"
Storyboard.TargetProperty="(Path.RenderTransform).(RotateTransform.Angle)"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
<!-- MouseOver, Pressed behaviours-->
<Trigger Property="IsMouseOver"
Value="true">
<Setter Property="Stroke"
Value="#FF3C7FB1"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#222"
TargetName="Arrow"/>
</Trigger>
<Trigger Property="IsPressed"
Value="true">
<Setter Property="Stroke"
Value="#FF526C7B"
TargetName="Circle"/>
<Setter Property="StrokeThickness"
Value="1.5"
TargetName="Circle"/>
<Setter Property="Stroke"
Value="#FF003366"
TargetName="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- Slide Out Content Expander's Template,
Uses: AnimatedExpanderButtonTemp from above,
MultiplyConverter in codebehind-->
<local:MultiplyConverter x:Key="multiplyConverter" />
<ControlTemplate x:Key="RevealExpanderTemp" TargetType="{x:Type Expander}">
<DockPanel>
<!--<Border x:Name="ExpanderButtonBorder" Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom" Padding="5,3">-->
<ToggleButton x:Name="ExpanderButton" HorizontalAlignment="Stretch" DockPanel.Dock="Bottom"
Template="{StaticResource AnimatedExpanderButtonTemp}"
Content="{TemplateBinding Header}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}"
OverridesDefaultStyle="True"
Padding="0">
</ToggleButton>
<!--</Border>-->
<ScrollViewer x:Name="ExpanderContentScrollView" DockPanel.Dock="Top"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Bottom"
>
<ScrollViewer.Tag>
<sys:Double>0.0</sys:Double>
</ScrollViewer.Tag>
<ContentPresenter x:Name="ExpanderContent" ContentSource="Content"/>
</ScrollViewer>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="ExpandDirection" Value="Right">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Right"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Left"/>
<Setter Property="Width" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualWidth" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Up">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Top"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Bottom"/>
<Setter Property="Height" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Left">
<Setter Property="DockPanel.Dock" TargetName="ExpanderButton" Value="Left"/>
<Setter Property="DockPanel.Dock" TargetName="ExpanderContentScrollView" Value="Right"/>
<Setter Property="Width" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualWidth" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ExpandDirection" Value="Down">
<Setter Property="Height" TargetName="ExpanderContentScrollView">
<Setter.Value>
<MultiBinding Converter="{StaticResource multiplyConverter}">
<Binding Path="ActualHeight" ElementName="ExpanderContent"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Self}" />
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="1"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="ExpanderContentScrollView"
Storyboard.TargetProperty="Tag"
To="0"
Duration="0:0:0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
MultiplyConverter.cs(模板需要)
public class MultiplyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
double result = 1.0;
for (int i = 0; i < values.Length; i++)
{
if (values[i] is double)
result *= (double)values[i];
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new Exception("Not implemented");
}
}
演示:
<!-- Small demo of Expander Template above-->
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Orange">
<Expander Template="{StaticResource RevealExpanderTemp}" ExpandDirection="Right"
OverridesDefaultStyle="True"
HorizontalAlignment="Left"
VerticalAlignment="Top" Background="LightGray">
<StackPanel>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
<TextBlock Text="This is Sample Text for Expander" Margin="5"/>
</StackPanel>
</Expander>
</Grid>
<Grid Grid.Row="2" Background="Chocolate" />
</Grid>
代码未完全测试,可能存在错误。