WPF在固定位置的切换按钮下方弹出窗口

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF在固定位置的切换按钮下方弹出窗口相关的知识,希望对你有一定的参考价值。

我正在尝试构建一个显示ListView的自定义ComboBox。这是我想要实现的屏幕截图:

enter image description here

我的大部分代码都基于this very helpful blog,它讨论了如何在ComboBox中嵌入DataGrid(或者在我的情况下,是GridView)。从功能的角度来看,一切都在发挥作用。但是,我似乎无法找到一种方法来解决下拉问题。理想情况下,无论窗口大小或窗口位置如何,我都希望它始终如上所示。目前,弹出窗口尝试右对齐,除非窗口靠近屏幕的左边缘,此时弹出窗口向内迁移。正如下面的XAML所示,问题是ListView位于Popup内部,而Popups并没有真正绑定到普通窗口,因此我们无法直接控制它们的位置。

<Window.Resources>
  <Style x:Key="ComboBoxTest2" TargetType="{x:Type ComboBox}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="ComboBox">
          <Grid>
            <ToggleButton x:Name="TGButton" Grid.Column="2" Focusable="false"
                          IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,
                          RelativeSource={RelativeSource TemplatedParent}}"
                          Padding="0,0,50,0">
              <ToggleButton.Template>
                <ControlTemplate>
                  <Grid>
                    <Grid.ColumnDefinitions>
                      <ColumnDefinition Width="*" />
                      <ColumnDefinition Width="30" />
                    </Grid.ColumnDefinitions>
                    <Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="5"
                         Background="LightGray" BorderBrush="Black" BorderThickness="1" />
                    <Border x:Name="Border2" Grid.Column="0" CornerRadius="5,0,0,5"
                            Margin="1" Background="White" BorderBrush="Black"
                            BorderThickness="0,0,1,0" />
                    <Path x:Name="Arrow" Grid.Column="1"  Fill="Black"
                          HorizontalAlignment="Center" VerticalAlignment="Center"
                          Data="M 0 0 L 4 4 L 8 0 Z"/>
                  </Grid>
                  <ControlTemplate.Triggers>
                    <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                      <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                    </Trigger>
                    <Trigger Property="ToggleButton.IsChecked" Value="true">
                      <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                      <Setter TargetName="Border" Property="CornerRadius" Value="5,5,0,0" />
                      <Setter TargetName="Border2" Property="CornerRadius" Value="5,0,0,0" />
                    </Trigger>
                  </ControlTemplate.Triggers>
                </ControlTemplate>
              </ToggleButton.Template>
            </ToggleButton>
            <ContentPresenter Name="ContentSite" IsHitTestVisible="False"
                            Content="{TemplateBinding SelectionBoxItem}"
                            ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                            ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                            Margin="3,3,40,3" />
            <TextBox x:Name="PART_EditableTextBox" Visibility="Hidden"
                     IsReadOnly="{TemplateBinding IsReadOnly}" Width="50" />
            <Popup Name="Popup" IsOpen="{TemplateBinding IsDropDownOpen}"
                   AllowsTransparency="True" Focusable="False" PopupAnimation="Slide"
                   Placement="Relative" VerticalOffset="{TemplateBinding ActualHeight}"
                   HorizontalOffset="{TemplateBinding ActualWidth}">
             <Grid Name="DropDown" SnapsToDevicePixels="True"
                   MinWidth="{TemplateBinding ActualWidth}"
                   MaxHeight="{TemplateBinding MaxDropDownHeight}">
               <Border x:Name="DropDownBorder" Background="White" BorderThickness="1"
                       BorderBrush="Black"/>
                 <ListView ItemsSource="{TemplateBinding ItemsSource}"
                           SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent},
                           Path=SelectedItem}">
                   <ListView.View>
                     <GridView>
                       <GridViewColumn Header="Key" DisplayMemberBinding="{Binding Key}" />
                       <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
                     </GridView>
                   </ListView.View>
                 </ListView>
               </Grid>
             </Popup>
           </Grid>
         </ControlTemplate>
       </Setter.Value>
     </Setter>
   </Style>
 </Window.Resources>
<Grid>
  <ComboBox x:Name="cBox" Height="30" Width="200" ItemsSource="{Binding Path=People}"
            SelectedValue="Selected" DisplayMemberPath="Name" SelectedValuePath="Name"
            Style="{StaticResource ComboBoxTest2}" 
            SelectionChanged="Function_SelectionChanged" />
</Grid>

我在一些地方读过,Adorners可能是一个解决方案,因为他们会收到所有resize事件并可以动态重新定位弹出窗口。另一种选择可能是使用像DevExpress这样的库,但我试图避免这种情况。顺便说一句,我的问题不是this one的副本,因为用于在XAML中放置Popup的偏移仅在渲染时受到尊重,而不是在调整大小/移动时。

答案

只需更改弹出式放置选项,您就可以获得想要显示的内容。

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300">
    <Window.Resources>
        <Style x:Key="ComboBoxTest2" TargetType="{x:Type ComboBox}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ComboBox">
                        <Grid>
                            <ToggleButton x:Name="TGButton" Grid.Column="2" Focusable="false"
                          IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,
                          RelativeSource={RelativeSource TemplatedParent}}"
                          Padding="0,0,50,0">
                                <ToggleButton.Template>
                                    <ControlTemplate>
                                        <Grid>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*" />
                                                <ColumnDefinition Width="30" />
                                            </Grid.ColumnDefinitions>
                                            <Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="5"
                         Background="LightGray" BorderBrush="Black" BorderThickness="1" />
                                            <Border x:Name="Border2" Grid.Column="0" CornerRadius="5,0,0,5"
                            Margin="1" Background="White" BorderBrush="Black"
                            BorderThickness="0,0,1,0" />
                                            <Path x:Name="Arrow" Grid.Column="1"  Fill="Black"
                          HorizontalAlignment="Center" VerticalAlignment="Center"
                          Data="M 0 0 L 4 4 L 8 0 Z"/>
                                        </Grid>
                                        <ControlTemplate.Triggers>
                                            <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                                                <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                                            </Trigger>
                                            <Trigger Property="ToggleButton.IsChecked" Value="true">
                                                <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                                                <Setter TargetName="Border" Property="CornerRadius" Value="5,5,0,0" />
                                                <Setter TargetName="Border2" Property="CornerRadius" Value="5,0,0,0" />
                                            </Trigger>
                                        </ControlTemplate.Triggers>
                                    </ControlTemplate>
                                </ToggleButton.Template>
                            </ToggleButton>
                            <ContentPresenter Name="ContentSite" IsHitTestVisible="False"
                            Content="{TemplateBinding SelectionBoxItem}"
                            ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
                            ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
                            Margin="3,3,40,3" />
                            <TextBox x:Name="PART_EditableTextBox" Visibility="Hidden"
                     IsReadOnly="{TemplateBinding IsReadOnly}" Width="50" />
                            <Popup Name="Popup" IsOpen="{TemplateBinding IsDropDownOpen}"
                   AllowsTransparency="True" Focusable="False" PopupAnimation="Slide"
                   Placement="Bottom">
                                <Grid Name="DropDown" SnapsToDevicePixels="True"
                   MinWidth="{TemplateBinding ActualWidth}"
                   MaxHeight="{TemplateBinding MaxDropDownHeight}">
                                    <Border x:Name="DropDownBorder" Background="White" BorderThickness="1"
                       BorderBrush="Black"/>
                                    <ListView ItemsSource="{TemplateBinding ItemsSource}"
                           SelectedItem="{Binding RelativeSource={RelativeSource TemplatedParent},
                           Path=SelectedItem}">
                                        <ListView.View>
                                            <GridView>
                                                <GridViewColumn Header="Key" DisplayMemberBinding="{Binding Key}" Width="80" />
                                                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="250"/>
                                            </GridView>
                                        </ListView.View>
                                    </ListView>
                                </Grid>
                            </Popup>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ComboBox x:Name="cBox" Height="30" Width="200" ItemsSource="{Binding}"
            SelectedValue="Selected" DisplayMemberPath="Name" SelectedValuePath="Name"
            Style="{StaticResource ComboBoxTest2}" 
            SelectionChanged="Function_SelectionChanged" />
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = Enumerable.Range(1, 100).Select(x => new { Key = x, Name = $"Person {x} with really really really really really really really really really really really really really really really really really really really really really really really really really really really really really really really really long name" }).ToList();
        }

        private void Function_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // do whatever you want
        }
    }
}

输出:

enter image description here

以上是关于WPF在固定位置的切换按钮下方弹出窗口的主要内容,如果未能解决你的问题,请参考以下文章

为按钮提供网站反馈的固定位置 CSS

单击按钮时打开 WPF 弹出窗口

Bootstrap 3 - 模式在使用固定侧边栏时消失在背景下方

wpf弹出的窗口只能是一个?

wpf 窗口弹出问题

如何在wpf中为关闭弹出窗口创建圆形十字按钮?