Get MDI client horizontal scroll value - c#

Image:
How to get MDI client horizontal scroll value and other param, when drag MDI child form out of border?
Or how can I get MDI child form absolute coordinates include scrolled part of MDI client?

Found only this solution:
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
public int hpos = 0;
void MdiClient_Scroll(object sender, ScrollEventArgs e)
{
hpos += e.NewValue;
Console.WriteLine(hpos);
}
private MdiClientWrapper wrapper;
protected override void OnHandleCreated(EventArgs e)
{
// Find the MdiClient and sub-class it so we can get the Scroll event
base.OnHandleCreated(e);
if (wrapper != null) wrapper.Scroll -= MdiClient_Scroll;
var client = this.Controls.OfType<MdiClient>().First();
wrapper = new MdiClientWrapper();
wrapper.AssignHandle(client.Handle);
wrapper.Scroll += MdiClient_Scroll;
}
private class MdiClientWrapper : NativeWindow
{
public event ScrollEventHandler Scroll;
private int oldPos;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x114)
{ // Trap WM_HSCROLL
var type = (ScrollEventType)(m.WParam.ToInt32() & 0xffff);
var pos = GetScrollPos(this.Handle, SBS_HORZ);//m.WParam.ToInt32() >> 16;
Scroll(this, new ScrollEventArgs(type, oldPos, pos));
oldPos = pos;
}
base.WndProc(ref m);
}
}
now horizontal scroll position always be in hpos variable.

Related

Windows Forms: ctrl button prevents ListView from scrolling with mouse wheel

I want to scroll ListView with mouse wheel while Ctrl button is pressed. But apparently pressing Ctrl changes scroll behavior: it stops scrolling, possibly tries to apply some zooming logic, I don't know. And I can't find out how to override that.
Please any help or suggestions?
The solution to get mouse wheel scrolling working while Ctrl key is held down is to listen for the WndProc event and specifically detecting MOUSEWHEEL trigger, minimum simple working example:
ListBox with WndProc override
class CtrlListBoxScroll : ListBox
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_MOUSEWHEEL)
{
var scrollDirection = NativeMethods.GET_WHEEL_DELTA_WPARAM(m.WParam);
// scrolling down
if (this.TopIndex < this.Items.Count && scrollDirection < 0)
{
this.TopIndex += 1;
}
// scrolling up
if (this.TopIndex > 0 && scrollDirection > 0)
{
this.TopIndex -= 1;
}
}
}
}
NativeMethods to read the wParam and detect scroll direction
internal static class NativeMethods
{
internal static ushort HIWORD(IntPtr dwValue)
{
return (ushort)((((long)dwValue) >> 0x10) & 0xffff);
}
internal static ushort HIWORD(uint dwValue)
{
return (ushort)(dwValue >> 0x10);
}
internal static int GET_WHEEL_DELTA_WPARAM(IntPtr wParam)
{
return (short)HIWORD(wParam);
}
internal static int GET_WHEEL_DELTA_WPARAM(uint wParam)
{
return (short)HIWORD(wParam);
}
}
Then finally testing it
private void Form1_Load(object sender, EventArgs e)
{
var ctrlListBoxScroll = new CtrlListBoxScroll();
ctrlListBoxScroll.Items.AddRange
(
new object[]
{
"hello", "scroll", "bar", "pressing", "ctrl", "to scroll",
"this", "list", "box", "check", "ctrl", "key", "is", "held"
}
);
this.Controls.Add(ctrlListBoxScroll);
}

Change combobox drop down list border color in C#

Is it possible to change the border color of a combobox dropdown list in c#?
I want to change the white border to a darker shade to match the dark scheme. I searched through the .net documentation and found the DropDownList.BorderStyle property. However, I'm not sure if this will work. I am using WinForms.
Here is the class I'm using for the combobox:
public class FlattenCombo : ComboBox
{
private Brush BorderBrush = new SolidBrush(SystemColors.WindowFrame);
private Brush ArrowBrush = new SolidBrush(SystemColors.ControlText);
private Brush DropButtonBrush = new SolidBrush(SystemColors.Control);
private Color _borderColor = Color.Black;
private ButtonBorderStyle _borderStyle = ButtonBorderStyle.Solid;
private static int WM_PAINT = 0x000F;
private Color _ButtonColor = SystemColors.Control;
public Color ButtonColor
{
get { return _ButtonColor; }
set
{
_ButtonColor = value;
DropButtonBrush = new SolidBrush(this.ButtonColor);
this.Invalidate();
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case 0xf:
Graphics g = this.CreateGraphics();
Pen p = new Pen(Color.Black);
g.FillRectangle(BorderBrush, this.ClientRectangle);
//Draw the background of the dropdown button
Rectangle rect = new Rectangle(this.Width - 17, 0, 17, this.Height);
g.FillRectangle(DropButtonBrush, rect);
//Create the path for the arrow
System.Drawing.Drawing2D.GraphicsPath pth = new System.Drawing.Drawing2D.GraphicsPath();
PointF TopLeft = new PointF(this.Width - 13, (this.Height - 5) / 2);
PointF TopRight = new PointF(this.Width - 6, (this.Height - 5) / 2);
PointF Bottom = new PointF(this.Width - 9, (this.Height + 2) / 2);
pth.AddLine(TopLeft, TopRight);
pth.AddLine(TopRight, Bottom);
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//Determine the arrow's color.
if (this.DroppedDown)
{
ArrowBrush = new SolidBrush(SystemColors.HighlightText);
}
else
{
ArrowBrush = new SolidBrush(SystemColors.ControlText);
}
//Draw the arrow
g.FillPath(ArrowBrush, pth);
break;
default:
break;
}
}
[Category("Appearance")]
public Color BorderColor
{
get { return _borderColor; }
set
{
_borderColor = value;
Invalidate(); // causes control to be redrawn
}
}
[Category("Appearance")]
public ButtonBorderStyle BorderStyle
{
get { return _borderStyle; }
set
{
_borderStyle = value;
Invalidate();
}
}
protected override void OnLostFocus(System.EventArgs e)
{
base.OnLostFocus(e);
this.Invalidate();
}
protected override void OnGotFocus(System.EventArgs e)
{
base.OnGotFocus(e);
this.Invalidate();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
}
While the FlatComboBoxAdapter is indeed not available. It is still possible to capture the WM_CTLCOLORLISTBOX windows message and apply a native GDI rectangle to the drop down border. It is a bit of work, but it does the job. (There's a link to an example at the bottom if you wish to skip this)
First off, if you're not familiar with the WM_CTLCOLORLISTBOX windows message, it is described as such:
"Sent to the parent window of a list box before the system draws the list box. By responding to this message, the parent window can set the text and background colors of the list box by using the specified display device context handle."
The message constant would be define like so:
const int WM_CTLCOLORLISTBOX = 0x0134;
Once the message constant is defined, condition for it within your custom ComboBox's overridden WndProc() event:
protected override void WndProc(ref Message m)
{
// Filter window messages
switch (m.Msg)
{
// Draw a custom color border around the drop down pop-up
case WM_CTLCOLORLISTBOX:
base.WndProc(ref m);
DrawNativeBorder(m.LParam);
break;
default: base.WndProc(ref m); break;
}
}
The DrawNativeBorder() method is where we are going to use the Win API to draw our rectangle. It accepts the handle to the drop down as an argument. Before we can do that however, we need to define the native methods, enumerations, and structs that are going to be used:
public enum PenStyles
{
PS_SOLID = 0,
PS_DASH = 1,
PS_DOT = 2,
PS_DASHDOT = 3,
PS_DASHDOTDOT = 4
}
public enum ComboBoxButtonState
{
STATE_SYSTEM_NONE = 0,
STATE_SYSTEM_INVISIBLE = 0x00008000,
STATE_SYSTEM_PRESSED = 0x00000008
}
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("gdi32.dll")]
public static extern int ExcludeClipRect(IntPtr hdc, int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
[DllImport("gdi32.dll")]
public static extern IntPtr CreatePen(PenStyles enPenStyle, int nWidth, int crColor);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern void Rectangle(IntPtr hdc, int X1, int Y1, int X2, int Y2);
public static int RGB(int R, int G, int B)
{
return (R | (G << 8) | (B << 16));
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left_, int top_, int right_, int bottom_)
{
Left = left_;
Top = top_;
Right = right_;
Bottom = bottom_;
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is RECT))
{
return false;
}
return this.Equals((RECT)obj);
}
public bool Equals(RECT value)
{
return this.Left == value.Left &&
this.Top == value.Top &&
this.Right == value.Right &&
this.Bottom == value.Bottom;
}
public int Height
{
get
{
return Bottom - Top + 1;
}
}
public int Width
{
get
{
return Right - Left + 1;
}
}
public Size Size { get { return new Size(Width, Height); } }
public Point Location { get { return new Point(Left, Top); } }
// Handy method for converting to a System.Drawing.Rectangle
public System.Drawing.Rectangle ToRectangle()
{
return System.Drawing.Rectangle.FromLTRB(Left, Top, Right, Bottom);
}
public static RECT FromRectangle(Rectangle rectangle)
{
return new RECT(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
}
public void Inflate(int width, int height)
{
this.Left -= width;
this.Top -= height;
this.Right += width;
this.Bottom += height;
}
public override int GetHashCode()
{
return Left ^ ((Top << 13) | (Top >> 0x13))
^ ((Width << 0x1a) | (Width >> 6))
^ ((Height << 7) | (Height >> 0x19));
}
public static implicit operator Rectangle(RECT rect)
{
return System.Drawing.Rectangle.FromLTRB(rect.Left, rect.Top, rect.Right, rect.Bottom);
}
public static implicit operator RECT(Rectangle rect)
{
return new RECT(rect.Left, rect.Top, rect.Right, rect.Bottom);
}
}
The DrawNativeBorder() method is defined as:
/// <summary>
/// Non client area border drawing
/// </summary>
/// <param name="handle">The handle to the control</param>
public static void DrawNativeBorder(IntPtr handle)
{
// Define the windows frame rectangle of the control
RECT controlRect;
GetWindowRect(handle, out controlRect);
controlRect.Right -= controlRect.Left; controlRect.Bottom -= controlRect.Top;
controlRect.Top = controlRect.Left = 0;
// Get the device context of the control
IntPtr dc = GetWindowDC(handle);
// Define the client area inside the control rect so it won't be filled when drawing the border
RECT clientRect = controlRect;
clientRect.Left += 1;
clientRect.Top += 1;
clientRect.Right -= 1;
clientRect.Bottom -= 1;
ExcludeClipRect(dc, clientRect.Left, clientRect.Top, clientRect.Right, clientRect.Bottom);
// Create a pen and select it
Color borderColor = Color.Magenta;
IntPtr border = WinAPI.CreatePen(WinAPI.PenStyles.PS_SOLID, 1, RGB(borderColor.R,
borderColor.G, borderColor.B));
// Draw the border rectangle
IntPtr borderPen = SelectObject(dc, border);
Rectangle(dc, controlRect.Left, controlRect.Top, controlRect.Right, controlRect.Bottom);
SelectObject(dc, borderPen);
DeleteObject(border);
// Release the device context
ReleaseDC(handle, dc);
SetFocus(handle);
}
I used the color Magenta to clearly show the painting. That will do it for the border painting. There is however, one more problem. When the drop down displays and the mouse hasn't moved over a drop down item, the default border still shows. To handle that issue, you'd have to determine when the drop down is fully open. Then send a WM_CTLCOLORLISTBOX message of our own to update the border.
I crudely check for the drop down display moment using a timer. I tried various other options but they didn't pan out. Honestly, if someone has a better solution that works, that would be great.
You'll need a timer to check when the drop down actually drops fully:
private Timer _dropDownCheck = new Timer();
The timer is a field in your custom combobox. Set it up in your custom combobox constructor after InitializeComponent():
_dropDownCheck.Interval = 100;
_dropDownCheck.Tick += new EventHandler(dropDownCheck_Tick);
Override the custom combobox's OnDropDown() event, and set up the timer tick event:
/// <summary>
/// On drop down
/// </summary>
protected override void OnDropDown(EventArgs e)
{
base.OnDropDown(e);
// Start checking for the dropdown visibility
_dropDownCheck.Start();
}
/// <summary>
/// Checks when the drop down is fully visible
/// </summary>
private void dropDownCheck_Tick(object sender, EventArgs e)
{
// If the drop down has been fully dropped
if (DroppedDown)
{
// Stop the time, send a listbox update
_dropDownCheck.Stop();
Message m = GetControlListBoxMessage(this.Handle);
WndProc(ref m);
}
}
Lastly, create the following methods to get the drop down handle and to create a WM_CTLCOLORLISTBOX message to send to the control:
/// <summary>
/// Creates a default WM_CTLCOLORLISTBOX message
/// </summary>
/// <param name="handle">The drop down handle</param>
/// <returns>A WM_CTLCOLORLISTBOX message</returns>
public Message GetControlListBoxMessage(IntPtr handle)
{
// Force non-client redraw for focus border
Message m = new Message();
m.HWnd = handle;
m.LParam = GetListHandle(handle);
m.WParam = IntPtr.Zero;
m.Msg = WM_CTLCOLORLISTBOX;
m.Result = IntPtr.Zero;
return m;
}
/// <summary>
/// Gets the list control of a combo box
/// </summary>
/// <param name="handle">Handle of the combo box itself</param>
/// <returns>A handle to the list</returns>
public static IntPtr GetListHandle(IntPtr handle)
{
COMBOBOXINFO info;
info = new COMBOBOXINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
return GetComboBoxInfo(handle, ref info) ? info.hwndList : IntPtr.Zero;
}
That about does that, if you're still confused, it's probably just easier to take a look at the control in this example VS 2010 custom combobox project I have provided:
http://www.pyxosoft.com/downloads/CustomComboBoxBorderColor.zip
I wrestled with this for far too long. I see from a previous question you asked that you've got code from: http://www.codeproject.com/Articles/2433/Flatten-that-Combobox and have set the BackColor:
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case 0xf:
base.BackColor = Color.Black;
Using the WndProc, this was the closest I got but it didn't color the dropdownlists' Popup/ItemSelection border:
...
if (this.DroppedDown)
{
ArrowBrush = new SolidBrush(SystemColors.HighlightText);
Rectangle dropDownBounds = new Rectangle(0, 0, Width,Height + DropDownHeight );
//ControlPaint.DrawBorder(g, dropDownBounds, _borderColor, _borderStyle);
ControlPaint.DrawBorder(g, dropDownBounds, _borderColor,1, ButtonBorderStyle.Dotted ,Color.GreenYellow,1,ButtonBorderStyle.Solid ,Color.Gold,1,ButtonBorderStyle.Dashed,Color.HotPink,1,ButtonBorderStyle.Solid);
}
It turns out that the class FlatComboBoxAdapter needed to do this is private and the recommendation is use WPF:
ComboBox DropDown-Area Border Color
More info: Combobox borderstyle - but even with LarsTech and Hans suggestions (using the non-client paint message) it still doesn't work and flickers horribly.
Other suggestion besides WPF, rewrite Combobox .Net Framework code: http://www.dotnetframework.org/default.aspx/FX-1434/FX-1434/1#0/untmp/whidbey/REDBITS/ndp/fx/src/WinForms/Managed/System/WinForms/ComboBox#cs/2/ComboBox#cs

How to restrict form movement to horizontal?

I have a standard form with standard title bar that user may grab and move the form around. In certain situations I want to restrict this movement to horizontal only, so no matter how the mouse actually moves, the form remains on same Y coordinate.
To do this, I catch move event and when I detect deviation from Y, I move the form back to the original Y. Like that:
private void TemplateSlide_Move(object sender, EventArgs e)
{
int y = SlideSettings.LastLocation.Y;
if (y != this.Location.Y)
{
SlideSettings.LastLocation = new Point(this.Location.X, y);
this.Location=Settings.LastLocation;
}
}
But this causes a lot of flicker. Also because form actually moves for a brief moment away from desired Y this causes other issues specific to my program.
Is there a way to prevent form from moving away from desired Y coordinate?
Trap WM_MOVING and modify the RECT structure in LPARAM accordingly.
Something like:
public partial class Form1 : Form
{
public const int WM_MOVING = 0x216;
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private int OriginalY = 0;
private int OriginalHeight = 0;
private bool HorizontalMovementOnly = true;
public Form1()
{
InitializeComponent();
this.Shown += new EventHandler(Form1_Shown);
this.SizeChanged += new EventHandler(Form1_SizeChanged);
this.Move += new EventHandler(Form1_Move);
}
void Form1_Move(object sender, EventArgs e)
{
this.SaveValues();
}
void Form1_SizeChanged(object sender, EventArgs e)
{
this.SaveValues();
}
void Form1_Shown(object sender, EventArgs e)
{
this.SaveValues();
}
private void SaveValues()
{
this.OriginalY = this.Location.Y;
this.OriginalHeight = this.Size.Height;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_MOVING:
if (this.HorizontalMovementOnly)
{
RECT rect = (RECT)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(RECT));
rect.Top = this.OriginalY;
rect.Bottom = rect.Top + this.OriginalHeight;
System.Runtime.InteropServices.Marshal.StructureToPtr(rect, m.LParam, false);
}
break;
}
base.WndProc(ref m);
}
}
When it's appropriate, just use the Mouse's Y coordinate, substituting the X coordinate with your static value. e.g.
... // e.g Mouse Down
originalX = Mouse.X; // Or whatever static X value you have.
... // e.g Mouse Move
// Y is dynamically updated while X remains static
YourObject.Location = new Point(originalX, Mouse.Y);

How to detect mouse over on title bar or mouse out from title bar in c# winform apps

i want to detect mouse over on title bar or mouse out from title bar in my c# winform apps.
i got a code which works but the problem is when i place the mouse on any area of win form then mouse leave occur and when i put mouse on title bar then right event call. here is the code
using System.Runtime.InteropServices;
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0xA0) // WM_NCMOUSEMOVE
{
TrackNcMouseLeave(this);
//ShowClientArea();
label1.Text = "mouse move on title bar";
}
else if (m.Msg == 0x2A2) // WM_NCMOUSELEAVE
{
//HideClientAreaIfPointerIsOut();
label1.Text = "mouse leave from title bar";
}
base.WndProc(ref m);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
//HideClientAreaIfPointerIsOut();
}
private int previouseHeight;
private void ShowClientArea()
{
if (this.ClientSize.Height == 0)
this.ClientSize = new Size(this.ClientSize.Width, previouseHeight);
}
private void HideClientAreaIfPointerIsOut()
{
if (this.Bounds.Contains(Cursor.Position))
return;
previouseHeight = this.ClientSize.Height;
this.ClientSize = new Size(this.ClientSize.Width, 0);
}
public static void TrackNcMouseLeave(Control control)
{
TRACKMOUSEEVENT tme = new TRACKMOUSEEVENT();
tme.cbSize = (uint)Marshal.SizeOf(tme);
tme.dwFlags = 2 | 0x10; // TME_LEAVE | TME_NONCLIENT
tme.hwndTrack = control.Handle;
TrackMouseEvent(tme);
}
[DllImport("user32")]
public static extern bool TrackMouseEvent([In, Out] TRACKMOUSEEVENT lpEventTrack);
[StructLayout(LayoutKind.Sequential)]
public class TRACKMOUSEEVENT
{
public uint cbSize;
public uint dwFlags;
public IntPtr hwndTrack;
public uint dwHoverTime;
}
}
so some one please guide me what i need to change as a result when i will place mouse on body then i should not be notified. thanks

How do I control two listboxes using a Vertical Scrollbar?

I've got a Windows Form that has 6 listboxes in it.
I'm trying to find / make a code that would make them scroll together. So I dropped a Vertical Scroll Bar onto the form, then put in the following code:
private void vScrollR_Scroll(object sender, ScrollEventArgs e)
{
int i = vScrollR.Value;
lstcr1.SelectedIndex = i;
lstpr1.SelectedIndex = i;
lstsr1.SelectedIndex = i;
lstcr2.SelectedIndex = i;
lstpr2.SelectedIndex = i;
lstsr2.SelectedIndex = i;
}
For some reason though, it won't work (i always returns 0). Am I going about this the wrong way? Is there any other way to achieve what I want? Perhaps, there's a method I need first?
Many thanks to all who will answer.
Change SelectedIndex to TopIndex. I just tried this and it works.
To keep the UI in sync while updating, you can use Control.BeginUpdate and Control.EndUpdate
listBox1.BeginUpdate();
listBox2.BeginUpdate();
listBox1.TopIndex =
listBox2.TopIndex = ++x;
listBox1.EndUpdate();
listBox2.EndUpdate();
Try to Create a Separate Class that Inherits from Listbox.
I hope that this will help you.
using System;
using System.Windows.Forms;
public class myScrollingListBox : ListBox
{
// Event declaration
public delegate void myScrollingListBoxDelegate(object Sender, myScrollingListBoxScrollArgs e);
public event myScrollingListBoxDelegate Scroll;
// WM_VSCROLL message constants
private const int WM_VSCROLL = 0x0115;
private const int SB_THUMBTRACK = 5;
private const int SB_ENDSCROLL = 8;
protected override void WndProc(ref Message m)
{
// Trap the WM_VSCROLL message to generate the Scroll event
base.WndProc(ref m);
if (m.Msg == WM_VSCROLL)
{
int nfy = m.WParam.ToInt32() & 0xFFFF;
if (Scroll != null && (nfy == SB_THUMBTRACK || nfy == SB_ENDSCROLL))
Scroll(this, new myScrollingListBoxScrollArgs(this.TopIndex, nfy == SB_THUMBTRACK));
}
}
public class myScrollingListBoxScrollArgs
{
// Scroll event argument
private int mTop;
private bool mTracking;
public myScrollingListBoxScrollArgs(int top, bool tracking)
{
mTop = top;
mTracking = tracking;
}
public int Top
{
get { return mTop; }
}
public bool Tracking
{
get { return mTracking; }
}
}
}

Categories