CustomControl 中带有弹出窗口的行为 ToggleButton

Behaviour ToggleButton with Popup in CustomControl

所以我在 wpf 中有以下 xaml 代码。这使我的 DropdownButton 行为符合我的预期。

<ToggleButton Content="Test"
                  VerticalAlignment="Center"
                  Focusable="False"
                  IsChecked="{Binding IsOpen, ElementName=Popup, Mode=TwoWay}"
                  Height="25"
                  Width="20"
                  x:Name="btn">
        <ToggleButton.Style>
            <Style TargetType="{x:Type ToggleButton}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsOpen, ElementName=Popup}" Value="True">
                        <Setter Property="IsHitTestVisible" Value="False" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ToggleButton.Style>
    </ToggleButton>

    <Popup Placement="Bottom"
           PlacementTarget="{Binding ElementName=btn}"
           x:Name="Popup"
           StaysOpen="False">
        <TextBlock Background="AliceBlue">Bla</TextBlock>
    </Popup>

既然我使用了几次,我想创建一个自定义控件。

到目前为止 class 看起来像这样:

public class CustomDropdownButton : ToggleButton
{
    public static readonly DependencyProperty DropdownContentProperty = DependencyProperty.Register("DropdownContent", typeof(UIElement), typeof(CustomDropdownButton));

    static CustomDropdownButton()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomDropdownButton), new FrameworkPropertyMetadata(typeof(CustomDropdownButton)));
    }

    public UIElement DropdownContent
    {
        get
        {
            return (UIElement)GetValue(DropdownContentProperty);
        }
        set
        {
            SetValue(DropdownContentProperty, value);
        }
    }
}

然后我编辑了样式,它看起来像这样:

<Style x:Key="ButtonFocusVisual">
    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate>
                <Rectangle StrokeDashArray="1 2" StrokeThickness="1" 
                           Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" 
                           SnapsToDevicePixels="true" Margin="2"/>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
    <GradientStop Color="#F3F3F3" Offset="0"/>
    <GradientStop Color="#EBEBEB" Offset="0.5"/>
    <GradientStop Color="#DDDDDD" Offset="0.5"/>
    <GradientStop Color="#CDCDCD" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ButtonNormalBorder" Color="#FF707070"/>

<Style TargetType="{x:Type local:CustomDropdownButton}">
    <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
    <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
    <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="1"/>
    <Setter Property="ClickMode" Value="Press"/>
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="IsChecked" Value="{Binding IsOpen, ElementName=Popup, Mode=TwoWay}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:CustomDropdownButton}">

                <themes:ButtonChrome x:Name="Chrome"
                                     BorderBrush="{TemplateBinding BorderBrush}"
                                     Background="{TemplateBinding Background}"
                                     RenderMouseOver="{TemplateBinding IsMouseOver}"
                                     SnapsToDevicePixels="true">
                    <StackPanel Orientation="Horizontal"
                                IsHitTestVisible="True">
                        <ContentPresenter Content="{TemplateBinding Content}"
                                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          Margin="5,0,0,0"/>
                        <Path x:Name="ArrowPath"
                              Margin="7,3,3,2"
                              Fill="Gray"
                              Stroke="Gray"
                              StrokeThickness="1"
                              StrokeStartLineCap="Round"
                              StrokeEndLineCap="Round"
                              Stretch="Uniform"
                              VerticalAlignment="Center"
                              HorizontalAlignment="Center"
                              RenderTransformOrigin=".5,.5"
                              Data="M0,0 L2,0 L4,2 L5,3 L6,2 L8,0 L10,0 L8,2 L5,7 L2,2 z">
                            <Path.RenderTransform>
                                <RotateTransform Angle="-90"/>
                            </Path.RenderTransform>
                        </Path>

                        <Popup Placement="Bottom"
                               x:Name="Popup"
                               StaysOpen="False"
                               Child="{TemplateBinding DropdownContent}"/>
                    </StackPanel>
                </themes:ButtonChrome>

                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsOpen, ElementName=Popup}" Value="True">
                        <Setter Property="IsHitTestVisible" Value="False" />
                    </DataTrigger>
                    <Trigger Property="IsKeyboardFocused" Value="true">
                        <Setter Property="RenderDefaulted" TargetName="Chrome" Value="true"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="#ADADAD"/>
                    </Trigger>
                    <Trigger Property="IsChecked" Value="False">
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="ArrowPath"
                                                     Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)"
                                                     To="-90"
                                                     Duration="0:0:0.2"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetName="ArrowPath"
                                                     Storyboard.TargetProperty="RenderTransform.(RotateTransform.Angle)"
                                                     To="0"
                                                     Duration="0:0:0.2"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

弹出按钮的示例代码运行良好。 我想我会通过使用 Setter 将弹出窗口的 IsOpen 属性 绑定到 ToggleButton 的 IsChecked 属性 来获得相同的效果。

我是否缺少需要添加的触发行为?我不太明白为什么打不开。


通过删除 IsChecked 和 ClickMode Setter 来修复它,从弹出窗口绑定 IsOpen 属性 不是使用模板绑定,而是使用建议的 RelativeSource 绑定到 IsChecked,模式设置为 TwoWay。 StaysOpen 和 DataTrigger 被保留。

由于我为 ListView 中的 ListViewItems 设置了边距,弹出窗口仍然关闭。为内容持有者设置一个填充(总是可点击)或边距(什么都没有)。对我来说,它是 ListView 中的复选框。

将模板中PopupIsOpen属性绑定到控件的IsChecked属性:

<Popup Placement="Bottom"
        x:Name="Popup"
        StaysOpen="False"
        IsOpen="{Binding Path=IsChecked, RelativeSource={RelativeSource AncestorType=local:CustomDropdownButton}, Mode=TwoWay}"
        Child="{TemplateBinding DropdownContent}"/>

这行不通:

<Setter Property="IsChecked" Value="{Binding IsOpen, ElementName=Popup, Mode=TwoWay}"/>