如何下拉任何窗口?






4.75/5 (37投票s)
向您展示如何使用 Popup 创建您自己的类似下拉窗口。
引言
下拉窗口作为对话框的轻量级替代方案,被广泛应用于各种软件应用中。Popup
类是一个 UI 容器,可以容纳任何 UI 元素,并且可以在另一个 UI 容器中弹出。本文向您展示如何使用 Popup
类创建您自己的下拉窗口。
背景
从功能上讲,Popup
是模态或非模态对话框的轻量级替代方案。可能最广为人知的 Popup
示例是 ComboBox
,也称为下拉列表。如果您使用 Expression Blend 来检查 ComboBox
,您会发现它由一个 ToggleButton
和一个承载 ItemsPresenter
的 Popup
组成。您当然可以将 ItemsPresenter
替换为其他内容,以便您可以自定义 ComboBox
。 并且您可以创建自己的类似下拉的 Popup
来承载任何 UI 元素。
Using the Code
本文向您展示:
- 如何切换
Popup
的打开/关闭; - 如何定位容器窗口;
- 如何承载用户控件;
- 如何绑定数据。
包含的示例演示了不同的方法。 通常,屏幕的左侧显示轻量级方法; 而屏幕的右侧显示适用于复杂情况的方法。 在下面,我们将使用 ToggleButton
和 Popup
类来构建下拉列表; 请注意它们之间的集成。
打开/关闭切换
Popup
的打开或关闭由 Popup.IsOpen
属性处理。 因此,您需要将其绑定到代码中的布尔属性。 控制打开/关闭的最简单方法是使用 ToggleButton
。 因此,Popup.IsOpen
和 ToggleButton.IsChecked
的属性值应同步。 对于简单的情况,您可以使用元素绑定,如下所示:
<ToggleButton x:Name="dgDropdown" Content="q" FontFamily="Wingdings 3" />
…
<Popup IsOpen="{Binding IsChecked, Mode=TwoWay, ElementName=dgDropdown}" StaysOpen="False"
PlacementTarget="{Binding ElementName=dgDropdown}" Placement="Bottom" >
如果您需要更多控制,您可以将两个属性绑定到 ViewModel 中声明的布尔属性,该 ViewModel 实现 INotifyPropertyChanged
接口以刷新属性更改。
<ToggleButton x:Name="icDropdown" Content="q" FontFamily="Wingdings 3"
IsChecked="{Binding IsItemsControlOpen, Mode=TwoWay}" />
…
<Popup IsOpen="{Binding IsItemsControlOpen, Mode=TwoWay}" StaysOpen="False"
PlacementTarget="{Binding ElementName=icDropdown}" Placement="Bottom" PopupAnimation="Slide">
为了实现下拉外观,您需要指定 Popup
和 ToggleButton
的相对位置。 您可以将 Popup.PlacementTarget
属性绑定到 ToggleButton
的元素名称。 Popup.Placement
属性指定 Popup
打开时的方向和行为。 由于您需要在 ToggleButton
下方打开 Popup
,因此您需要将此属性设置为 Bottom
。
作为第二种方式,您也可以将 Popup
视为 ToggleButton
内容的一部分,并将 Popup
嵌套在 ToggleButton
中。 这样,Popup.PlacementTarget
会自动与 ToggleButton
关联。 请注意,由于我设置了 ToggleButton.Padding
,因此我需要调整 Popup.HorizontalOffset
和 Popup.VerticalOffset
以使两个控件正确对齐。 偏移值应包括 ToggleButton
的边框厚度。
<ToggleButton IsChecked="{Binding IsItemsControlOpen, Mode=TwoWay}"
HorizontalAlignment="Left" Padding="4, 2" Margin="10">
<ToggleButton.Content>
<StackPanel>
<TextBlock>
<Run Text="Selection" />
<Run Text="q" FontFamily="Wingdings 3" />
</TextBlock>
<Popup IsOpen="{Binding IsItemsControlOpen, Mode=TwoWay}" StaysOpen="False"
Placement="Bottom" PopupAnimation="Slide"
HorizontalOffset="-5" VerticalOffset="3">
<Border BorderBrush="SlateBlue" BorderThickness="1" CornerRadius="2" >
<local:MyUserControl />
</Border>
</Popup>
</StackPanel>
</ToggleButton.Content>
</ToggleButton>
轻量级承载方式
Popup
可以承载许多不同的 UI 元素。 如果您的情况很简单,只需要承载几个控件,您可以在 XAML 中执行此操作,如示例屏幕的左侧所示。 这是 XAML 代码:
<Popup IsOpen="{Binding IsChecked, Mode=TwoWay, ElementName=dgDropdown}" StaysOpen="False"
PlacementTarget="{Binding ElementName=dgDropdown}" Placement="Bottom" PopupAnimation="Slide">
<Border BorderBrush="SlateBlue" BorderThickness="1" CornerRadius="2" >
<Grid Background="LightGray">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Data}" CanUserAddRows="False" IsReadOnly="True"
RowHeaderWidth="0" HorizontalGridLinesBrush="Transparent"
VerticalGridLinesBrush="Transparent" Margin="2">
<i:Interaction.Behaviors>
<local:ColumnHeaderBehavior />
</i:Interaction.Behaviors>
</DataGrid>
<TextBox Grid.Row="1"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="100" Margin="2"/>
</Grid>
</Border>
</Popup>
请参阅 A Smart Behavior for DataGrid.AutoGenerateColumns 了解如何使用 Behavior 和 Attribute 进行列标题显示。
承载用户控件
如果您需要在 Popup
中承载很多东西,并且可能具有复杂的逻辑,您可以将所有内容包装在 UserControl 中,并将其承载在 Popup
中,如示例屏幕的右侧所示。
<Popup IsOpen="{Binding IsItemsControlOpen, Mode=TwoWay}" StaysOpen="False"
PlacementTarget="{Binding ElementName=icDropdown}" Placement="Bottom" PopupAnimation="Slide">
<Border BorderBrush="SlateBlue" BorderThickness="1" CornerRadius="2" >
<local:MyUserControl />
</Border>
</Popup>
关注点
通过将 Popup
的打开/关闭与 ToggleButton
同步,您可以有效地创建自己的 Popup
窗口。 对齐两个控件的相对位置将使外观像下拉列表。 您可以进一步自定义 Popup
的 Border
以增强用户体验。