【问题标题】:WPF place popup under toggle button in fixed positionWPF将弹出窗口置于固定位置的切换按钮下
【发布时间】:2018-08-01 04:25:58
【问题描述】:

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

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

<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>

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

【问题讨论】:

    标签: c# wpf gridview combobox


    【解决方案1】:

    只需更改您的弹出窗口放置选项,您应该能够获得您想要显示的内容。

    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
            }
        }
    }
    

    输出:

    【讨论】:

    • 这会将下拉菜单放在底部(正确),但它是右对齐的。你知道如何强制弹出框与 ComboBox 的左侧对齐吗?
    • @TimBiegeleisen 不知道你是什么意思,它已经与组合框的左侧对齐了。
    • 那个屏幕截图是我想要的,而不是当前状态。 WPF 的默认行为是右对齐。
    • 感谢您的帮助,但您不能使用我的 XAML 代码,否则您会看到与我相同的内容。
    • @TimBiegeleisen 让我把整个代码。是不是似曾相识?
    猜你喜欢
    • 2018-03-26
    • 2017-09-06
    • 2017-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多