Windows Forms ComboBox DropDown Position
Here is an extended ComboBox
which have 2 new useful features to let you set the position and size of drop-down:
DropDownAlignment
: You can set it toLeft
, then the drop-down will appear in it's normal position and it's left is aligned with left of control. If you set it toMiddle
, the middle of drop-down will be aligned with control and if you set it toRight
, the right of drop-down will be aligned with right of control.AutoWidthDropDown
: If you set it totrue
to, thenDropdownWidth
will be set to the width of the longest item. If you set it tofalse
it usesWidth
asDropDownWidth
.
Here is appearance of drop-down after setting AutoWidthDropDown
to true
and DropDownAlignment
to Left
, Middle
and Right
:
Implementation - ComboBox with DropDown Position and AutoWith DropDown
You can handle WM_CTLCOLORLISTBOX
message, the lparam
is the handle of drop-down and then you can set position of drop-down using SetWindowPos
.
Also you can calculate width of longest item and set as DropDownWidth
if AutoWidthDropDown
is true.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;
public class MyComboBox : ComboBox
{
private const UInt32 WM_CTLCOLORLISTBOX = 0x0134;
private const int SWP_NOSIZE = 0x1;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
public enum DropDownAlignments { Left = 0, Middle, Right }
public bool AutoWidthDropDown { get; set; }
public DropDownAlignments DropDownAlignment { get; set; }
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CTLCOLORLISTBOX) {
var bottomLeft = this.PointToScreen(new Point(0, Height));
var x = bottomLeft.X;
if (DropDownAlignment == MyComboBox.DropDownAlignments.Middle)
x -= (DropDownWidth - Width) / 2;
else if (DropDownAlignment == DropDownAlignments.Right)
x -= (DropDownWidth - Width);
var y = bottomLeft.Y;
SetWindowPos(m.LParam, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE);
}
base.WndProc(ref m);
}
protected override void OnDropDown(EventArgs e)
{
if (AutoWidthDropDown)
DropDownWidth = Items.Cast<Object>().Select(x => GetItemText(x))
.Max(x => TextRenderer.MeasureText(x, Font,
Size.Empty, TextFormatFlags.Default).Width);
else
DropDownWidth = this.Width;
base.OnDropDown(e);
}
}
ComboBox Dropdown Position
Tricky problem. I could not find a good fix for this, merely a workaround. Add a new class and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form.
The workaround is not a very good one. The problem is that the dropdown window will ignore the attempt to move it until the drop-down animation is complete. The visual effect is not great, you'll see the window drop down, then jump to the left. I don't know an other way to slap it over the head, maybe somebody else does.
C# version:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MyComboBox : ComboBox {
protected override void OnDropDown(EventArgs e) {
// Is dropdown off the right side of the screen?
Point pos = this.PointToScreen(this.Location);
Screen scr = Screen.FromPoint(pos);
if (scr.WorkingArea.Right < pos.X + this.DropDownWidth) {
this.BeginInvoke(new Action(() => {
// Retrieve handle to dropdown list
COMBOBOXINFO info = new COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
SendMessageCb(this.Handle, 0x164, IntPtr.Zero, out info);
// Move the dropdown window
RECT rc;
GetWindowRect(info.hwndList, out rc);
int x = scr.WorkingArea.Right - (rc.Right - rc.Left);
SetWindowPos(info.hwndList, IntPtr.Zero, x, rc.Top, 0, 0, 5);
}));
}
base.OnDropDown(e);
}
// P/Invoke declarations
private struct COMBOBOXINFO {
public Int32 cbSize;
public RECT rcItem, rcButton;
public int buttonState;
public IntPtr hwndCombo, hwndEdit, hwndList;
}
private struct RECT {
public int Left, Top, Right, Bottom;
}
[DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr after, int x, int y, int cx, int cy, int flags);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT rc);
}
VB.Net version:
Imports System
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Public Class MyComboBox
Inherits ComboBox
'P/Invoke declarations
Private Structure COMBOBOXINFO
Public cbSize As Int32
Public rcItem As RECT
Public rcButton As RECT
Public buttonState As Integer
Public hwndCombo As IntPtr
Public hwndEdit As IntPtr
Public hwndList As IntPtr
End Structure
Private Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
<DllImport("user32.dll", EntryPoint:="SendMessageW", CharSet:=CharSet.Unicode)>
Private Shared Function SendMessageCb(hWnd As IntPtr, msg As Integer, wp As IntPtr, ByRef lp As COMBOBOXINFO) As IntPtr
End Function
<DllImport("user32.dll")>
Private Shared Function SetWindowPos(hWnd As IntPtr, after As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, flags As Integer) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function GetWindowRect(hWnd As IntPtr, ByRef rc As RECT) As Boolean
End Function
Protected Overrides Sub OnDropDown(e As EventArgs)
' Is dropdown off the right side of the screen?
Dim pos As Point = Me.PointToScreen(Me.Location)
Dim scr As Screen = Screen.FromPoint(pos)
If (scr.WorkingArea.Right < pos.X + Me.DropDownWidth) Then
Me.BeginInvoke(New Action(Sub()
'Retrieve handle to dropdown list
Dim info As COMBOBOXINFO = New COMBOBOXINFO()
info.cbSize = Marshal.SizeOf(info)
SendMessageCb(Me.Handle, &H164, IntPtr.Zero, info)
'Move the dropdown window
Dim rc As RECT
GetWindowRect(info.hwndList, rc)
Dim x As Integer = scr.WorkingArea.Right - (rc.Right - rc.Left)
SetWindowPos(info.hwndList, IntPtr.Zero, x, rc.Top, 0, 0, 5)
End Sub))
End If
MyBase.OnDropDown(e)
End Sub
End Class
changing combo box drop down position in winforms
You can achieve this by this post
ComboBox Dropdown Position
This works fine when I use below codes
MyComboBox cmb = new MyComboBox();
cmb.Dock = DockStyle.Right;
cmb.Items.Add("Hello world");
cmb.Items.Add("How are you man");
cmb.SelectedIndex = 0;
this.Controls.Add(cmb);
C# Custom ComboBox - DropDown Position
You can use the ToolStripDropDown.Show Method (Control, Point, ToolStripDropDownDirection) overload to control the drop direction. The code will need to perform bounds checking to decide whether to place the dropdown above or below the textbox.
The following is a simplistic method for doing the bounds checking and was only tested on a single screen configuration.
First, make textbox
a class level variable.
private TextBox textbox;
public CustomComboBox()
{
//var textbox = new TextBox();
textbox = new TextBox();
The display logic is as follows.
void Button_Click(object sender, EventArgs e)
{
Point textBoxScreenLocation = textbox.PointToScreen(textbox.Location);
// try to position _dropDown below textbox
Point pt = textBoxScreenLocation;
pt.Offset(0, textbox.Height);
// determine if it will fit on the screen below the textbox
Size dropdownSize = _dropDown.GetPreferredSize(Size.Empty);
Rectangle dropdownBounds = new Rectangle(pt, dropdownSize);
if (dropdownBounds.Bottom <= Screen.GetWorkingArea(dropdownBounds).Bottom)
{ // show below
_dropDown.Show(pt, ToolStripDropDownDirection.BelowRight);
}
else
{ // show above
_dropDown.Show(textBoxScreenLocation, ToolStripDropDownDirection.AboveRight);
}
}
}
WPF ComboBox DropDown Placement
I've done something similar before - I ended up deriving from ComboBox, getting the popup part of the control and using the CustomPopupPlacementCallback to position it. Something like this...
class MyComboBox : ComboBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var popup = (Popup)Template.FindName("PART_Popup", this);
popup.Placement = PlacementMode.Custom;
popup.CustomPopupPlacementCallback = placePopup;
}
private CustomPopupPlacement[] placePopup(Size popupSize, Size targetSize, Point offset)
{
var placements = new[] { new CustomPopupPlacement() };
placements[0].Point = // position the drop-down here!
return placements;
}
}
Positon of DropDown list of ComboBox in UWP
The DropDown of a ComboBox
is actually a Popup
, and the position where to show this Popup
is defined in the code behind, and we can't access to it. One workaround is finding this Popup
and relocate it when it is opened, but using this method we need to calculate the VerticalOffset
property each time when it is opened, and there are quite many scenarios for different value of VerticalOffset
.
So my suggestion is design a custom control which behavior like a ComboBox
, for example I created a UserControl
like this:
<Button x:Name="rootButton" BorderBrush="Gray" BorderThickness="2" Click="Button_Click" MinWidth="80" Background="Transparent" Padding="0">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Width="{Binding ElementName=rootButton, Path=ActualWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<TextBlock Text="{x:Bind selectedItem, Mode=OneWay}" Grid.Column="0" VerticalAlignment="Center" FontSize="15" HorizontalAlignment="Center" />
<FontIcon Grid.Column="1" FontSize="12" FontFamily="Segoe MDL2 Assets" Glyph="" HorizontalAlignment="Right"
Margin="0,10,10,10" VerticalAlignment="Center" />
</Grid>
<FlyoutBase.AttachedFlyout>
<MenuFlyout Placement="Bottom" x:Name="menuFlyout">
<MenuFlyoutItem Text="Item 1" Click="MenuFlyoutItem_Click" />
<MenuFlyoutItem Text="Item 2" Click="MenuFlyoutItem_Click" />
<MenuFlyoutItem Text="Item 3" Click="MenuFlyoutItem_Click" />
<MenuFlyoutItem Text="Item 4" Click="MenuFlyoutItem_Click" />
<MenuFlyoutItem Text="Item 5" Click="MenuFlyoutItem_Click" />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
</Button>
and the code behind in this UserControl
:
public sealed partial class CustomComboBox : UserControl, INotifyPropertyChanged
{
public CustomComboBox()
{
this.InitializeComponent();
selectedItem = "";
}
private string _selectedItem;
public string selectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("selectedItem"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void MenuFlyoutItem_Click(object sender, RoutedEventArgs e)
{
var item = sender as MenuFlyoutItem;
selectedItem = item.Text;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
FlyoutBase.ShowAttachedFlyout(sender as Button);
}
}
And you can use this CustomComboBox
in other page like this:
<local:CustomComboBox VerticalAlignment="Center" HorizontalAlignment="Center" />
By default this CustomComboBox
will show its DropDown
list under it, unless there is no enough space under it to hold this DropDown
, in this case, the DropDown
will be shown above this CustomComboBox
.
Related Topics
Copy Values from One Object to Another
How to Format Timespan in Xaml
How to Cast Expression<Func<T, Datetime>> to Expression<Func<T, Object>>
Keep Casing When Serializing Dictionaries
How to Check If Thread Finished Execution
How to Check If Another Instance of the Application Is Running
Webdriver How to Wait Until the Element Is Clickable in Webdriver C#
How to Get Text Between Nested Parentheses
Change File Extension Using C#
ASP.NET MVC Routing - Add .HTML Extension to Routes
Get Instance of Excel Application with C# by Handle
Circular Reference Causing Stack Overflow with Automapper
Relative Path to Absolute Path in C#