我正在尝试构建一个显示ListView的自定义ComboBox。下面是我试图实现的一个屏幕截图:
我的大部分代码都基于 this very helpful blog ,它讨论了如何将DataGrid (或者在我的例子中是GridView)嵌入到ComboBox中。从功能的角度来看,一切都在工作。然而,我似乎找不到一种方法来修复下拉。理想情况下,我希望它总是出现在上面,无论窗口大小或窗口位置如何。当前,弹出窗口尝试右对齐,除非窗口靠近屏幕的左边缘,此时弹出窗口会向内移动。问题是,正如下面的XAML所示,ListView位于弹出窗口中,而弹出窗口并不真正绑定到正常窗口,因此我们无法直接控制它们的位置。
<Window.Resources>
<Style x:Key="ComboBoxTest2" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<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.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>
<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可能是一个解决方案,因为他们将接收所有调整大小的事件,并可以动态地重新定位弹出窗口。另一种选择可能是使用像DevExpress这样的库,但我正在努力避免这种情况。顺便说一下,我的问题不是 this one 的副本,因为用于在XAML中放置弹出窗口的偏移量只在渲染时有效,而不是在调整大小/移动时使用。
发布于 2018-08-01 12:34:47
只需更改弹出窗口的位置选项,您就可以获得您想要显示的内容。
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">
<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.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>
<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();