Inserting a control into another control at specific location? - c#

I am working with win forms. I have two forms : my main form, Form1 and a Form I made called TextBlock. I am making a text editor, where you can place text boxes around a page, and edit them (think word).
Here are my two forms.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
saveFileDialog.Filter = "Text File|*.txt";
var result = saveFileDialog.ShowDialog();
if (result == DialogResult.OK)
{
StreamWriter writer = new
StreamWriter(saveFileDialog.OpenFile());
writer.Write(tb_Main.Text);
writer.Dispose();
writer.Close();
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
private void button_tb_Click(object sender, EventArgs e)
{
TextBlock tb_edit = new TextBlock();
tb_edit.Text = "New Text Block";
// tb_edit.Multiline = true;
// tb_edit.Size = new Size(100,100);
// tb_edit.MinimumSize = new Size(50, 50);
tb_edit.form1 = this;
tb_edit.TopLevel = false;
tb_edit.btn_accepttb.BringToFront();
tb_Main.Controls.Add(tb_edit);
tb_edit.Show();
tb_edit.BringToFront();
}
}
and my custom form here:
public partial class TextBlock : Form
{
public Form1 form1;
public TextBlock()
{
InitializeComponent();
}
private void btn_accepttb_Click(object sender, EventArgs e)
{
TextBox tb_edit = new TextBox();
tb_edit.Text = "New Text Block";
tb_edit.Multiline = true;
tb_edit.Size = this.Size;
int dif = form1.tb_Main.Lines.Count()*(int)tb_edit.Font.Size;
Point loca = new Point(this.Location.X,this.Location.Y+dif);
tb_edit.Location = this.Location;
form1.tb_Main.Controls.Add(tb_edit);
tb_edit.Show();
tb_edit.BringToFront();
form1.tb_Main.Controls.Remove(this);
}
}
What it does: it makes a copy my TextBlock, for placement and resizing purposes. when you have it where you want and how big you want, you click the button, and it replaces itself with a regular textbox, of that size, in that position.
What I want it to do: currently it is working with one exception. I am adding it to the controls, of tb_Main (my main textbox, it takes up the whole form) in Form1, it shows up. its the right size, except when i fill tb_Main with text, and i scroll, the new textbox stays where it is while its parent scrolls behind it.
Question : if I scroll down into my document and decide I want this textbox here, how do I ensure its position relative to the scroll of the textbox im putting it in. so when I scroll on, it basically stays embedded in the page where I put it (when I say "page" im referring to my tb_Main).

It is better to create a richtextbox instead of textbox. It has all capabilities of textbox and way more. This example works only with a richtextbox.
Subclassed richtextbox:
class MyRichTextBox : RichTextBox {
[DllImport( "user32.dll" )]
private static extern bool GetScrollInfo( IntPtr hwnd, SBOrientation fnBar,
ref SCROLLINFO lpsi );
[StructLayout( LayoutKind.Sequential )]
private struct SCROLLINFO {
public uint cbSize;
public ScrollInfoMask fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private enum ScrollInfoMask : uint {
SIF_RANGE = 0x1,
SIF_PAGE = 0x2,
SIF_POS = 0x4,
SIF_DISABLENOSCROLL = 0x8,
SIF_TRACKPOS = 0x10,
SIF_ALL = ( SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS ),
}
private enum SBOrientation : int {
SB_HORZ = 0x0,
SB_VERT = 0x1,
}
private const int WM_VSCROLL = 0x115;
private const int SB_LINEUP = 0;
private const int SB_LINEDOWN = 1;
private const int SB_PAGEUP = 2;
private const int SB_PAGEDOWN = 3;
private const int SB_THUMBPOSITION = 4;
private const int SB_THUMBTRACK = 5;
private const int SB_TOP = 6;
private const int SB_BOTTOM = 7;
private const int SB_ENDSCROLL = 8;
private bool isThumbTrack = false;
private TextBox childTextbox = null;
private int scrollPos = 0;
public MyTextBox( TextBox textbox ) { //here you pass the child textbox you want to scroll
childTextbox = textbox;
}
protected override void OnVScroll( EventArgs e ) {
if( childTextbox == null ) { base.OnVScroll( e ); return; }
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf( si );
si.fMask = ScrollInfoMask.SIF_ALL;
GetScrollInfo( this.Handle, SBOrientation.SB_VERT, ref si );
//there is a difference when the user uses the thumb to get scroll pos.
//When user uses the thumb we get *nTrackPos* otherwise *nPos*
if( isThumbTrack == true ) {
childTextbox.Location = new Point( childTextbox.Location.X, childTextbox.Location.Y -
(si.nTrackPos - scrollPos ) );
isThumbTrack = false;
scrollPos = si.nTrackPos;
}
else {
childTextbox.Location = new Point( childTextbox.Location.X, childTextbox.Location.Y -
( si.nPos - scrollPos ) );
scrollPos = si.nPos;
}
base.OnVScroll( e );
}
protected override void WndProc( ref Message m ) {
int wParam;
if(m.Msg == WM_VSCROLL ) {
wParam = m.WParam.ToInt32();
wParam &= 0xFFFF; //get low 16 bits of wParam
//Check if user is using the thumb to scroll
if( wParam == SB_THUMBTRACK ) {
isThumbTrack = true;
}
}
base.WndProc( ref m );
}
}

Related

DateTimePicker control in ContextMenuStrip

I have added a DateTimePicker in ContextMenuStrip using ToolStripControlHost. I am using below code.
DateTimePicker startDate = new DateTimePicker();
(cms.Items["mnuStartDate"] as ToolStripMenuItem).DropDownItems.Add(new ToolStripControlHost(startDate));
Selecting of date is working but when I clicked on the arrows to go to the previous or next month, or if I clicked on the Month, the ToolStrip is closing. Is there any workaround here? Thanks.
Note: I am showing the cms(ContextMenuStrip ) when Right Clicking on a control.
Looks like the ToolStrip behavior closes when an item is selected. So I juts used CalendarMonth Control as a replacement. Link here
It seems to be related to Application.EnableVisualStyles(); When not-enabled, the menu doesn't unexpectedly close.
When enabled, to prevent closing, detect if the CalendarMonth menu is currently visible, and if the mouse was clicked on it.
public class DatePicker : DateTimePicker {
private const int DtmFirst = 0x1000;
private const int DtmGetmonthcal = (DtmFirst + 8);
private bool isDroppedDown = false;
private IntPtr hwndCalendarWindow = IntPtr.Zero;
protected override void OnDropDown(EventArgs e) {
isDroppedDown = true;
hwndCalendarWindow = SendMessage(Handle, DtmGetmonthcal, 0, 0);
base.OnDropDown(e);
}
protected override void OnCloseUp(EventArgs eventargs) {
isDroppedDown = false;
base.OnCloseUp(eventargs);
}
public bool CalendarMonthClicked(Point pt) {
if (!isDroppedDown)
return false;
RECT r = new RECT();
GetWindowRect(hwndCalendarWindow, out r);
return pt.X >= r.Left && pt.X <= r.Right && pt.Y >= r.Top && pt.Y <= r.Bottom;
}
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("User32.dll")]
private static extern IntPtr SendMessage(IntPtr h, int msg, int param, int data);
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int Left, Top, Right, Bottom;
}
}
static class Program {
//[DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
//public extern static Int32 SetWindowTheme(IntPtr hWnd, String textSubAppName, String textSubIdList);
[STAThread]
static void Main() {
Application.EnableVisualStyles(); // if you disable visual styles then the menu won't close
DatePicker dpStart = new DatePicker();
dpStart.MinimumSize = new Size(100, 50);
Form ff = new Form();
ContextMenuStrip cms = new ContextMenuStrip();
ToolStripMenuItem menu = new ToolStripMenuItem("Menu");
menu.DropDown.Closing += (o, e) => {
e.Cancel = (e.CloseReason == ToolStripDropDownCloseReason.AppClicked && dpStart.CalendarMonthClicked(Cursor.Position));
};
// sub-menu doesn't close properly, probably related to the ToolStripControlHost
cms.Opening += delegate {
menu.DropDown.Close(ToolStripDropDownCloseReason.CloseCalled);
};
cms.Closed += delegate {
menu.DropDown.Close(ToolStripDropDownCloseReason.CloseCalled);
};
ff.ContextMenuStrip = cms;
cms.Items.Add(menu);
ToolStripControlHost item = new ToolStripControlHost(dpStart) { AutoSize = true };
menu.DropDownItems.Add(item);
Application.Run(ff);
}
}

How to correct hit test area for list view items with only an image (null label text)

I have a System.Windows.Forms.ListView with icons and empty string labels. Here is the code that creates and fills the list view:
listView1 = new System.Windows.Forms.ListView();
listView1.BackColor = System.Drawing.Color.Blue;
listView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
listView1.HideSelection = false;
listView1.Location = new System.Drawing.Point(93, 66);
listView1.MultiSelect = false;
listView1.Name = "listView1";
listView1.ShowItemToolTips = true;
listView1.Size = new System.Drawing.Size(434, 332);
listView1.TabIndex = 0;
listView1.UseCompatibleStateImageBehavior = false;
var images = Directory.GetFiles
(#"my path")
.Where((where) => Path.GetExtension(where)
.Equals(".PNG", StringComparison.CurrentCultureIgnoreCase))
.Select((selected)
=> Image.FromFile(selected));
var il = new ImageList();
il.ImageSize = new Size(75, 75);
il.Images.AddRange(images.ToArray());
listView1.LargeImageList = il;
for (int i = 0; i < il.Images.Count; i++)
{
var lvi = new ListViewItem("", i)
{
ToolTipText = i.ToString()
};
listView1.Items.Add(lvi);
}
Controls.Add(listView1);
Now I'm using the technique described in many places, like here, to set the list view item image spacing:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
public int MakeLong(short lowPart, short highPart)
{
return (int)(((ushort)lowPart) | (uint)(highPart << 16));
}
public void ListViewItem_SetSpacing(ListView listview, short leftPadding, short topPadding)
{
const int LVM_FIRST = 0x1000;
const int LVM_SETICONSPACING = LVM_FIRST + 53;
SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding));
}
The problem is that the hit test area still includes the label, even though there is no label, so the wrong item is returned by the hit test and the wrong tool tip is displayed when the mouse is over where the label of the adjacent item would be:
Here is what the "wrong" hit test looks like.
private void ListView1_MouseDown(object sender, MouseEventArgs e)
{
var hti = listView1.HitTest(e.Location);
}
Is there a way to remove the label from a list view item so the hit test area only includes the image? Or am I forced to go the OwnerDraw = true; route (which I understand is very tricky and/or impossible)?

A DateTimePicker to change only month and year

UI should remain same I don't want to do this:
dateTimePicker1.Format = DateTimePickerFormat.Custom;
dateTimePicker1.CustomFormat = "mm-yyyy";
dateTimePicker1.ShowUpDown = true;
A User can pick the year and month, change DateTimePicker to Month/Year picker, the MonthCalendar too.
I don't want to use a third-party control but I will if it will be the only answer point me to any free month/year picker control.
Here's a simple DateTimePicker custom control that pre-defines a custom DateTime format and modified the MonthCalendar view, configuring it show just the Months and the Year when is opened.
I've added a public property, ShowToday, that allows to specify whether the MonthCalendar popup should show the Today date at the bottom of the Calendar interface.
The custom control overrides OnDropDown to get the Handle of the MonthCalendar popup, sending a DTM_GETMONTHCAL message and changes its current view sending a MCM_SETCURRENTVIEW, specifying MCMV_YEAR as the value parameter.
This is how it look like:
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[DesignerCategory("Code")]
class DateTimePickerYearMonth : DateTimePicker
{
public DateTimePickerYearMonth() {
this.CustomFormat = "MM-yyyy";
this.Format = DateTimePickerFormat.Custom;
this.Value = DateTime.Now;
}
[
Browsable(true), EditorBrowsable(EditorBrowsableState.Always), DefaultValue(false),
Category("Appearance"),
Description("Shows or hides \"Today\" date at the bottom of the Calendar Control")
]
public bool ShowToday {
get => m_ShowToday;
set {
if (value != m_ShowToday) {
m_ShowToday = value;
ShowMonCalToday(m_ShowToday);
}
}
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
ShowMonCalToday(m_ShowToday);
}
protected override void OnDropDown(EventArgs e)
{
var hWnd = SendMessage(this.Handle, DTM_GETMONTHCAL, 0, 0);
if (hWnd != IntPtr.Zero) {
SendMessage(hWnd, MCM_SETCURRENTVIEW, 0, (int)MonCalView.MCMV_YEAR);
}
base.OnDropDown(e);
}
private void ShowMonCalToday(bool show)
{
int styles = SendMessage(this.Handle, DTM_GETMCSTYLE, 0, 0).ToInt32();
styles = show ? styles &~(int)MonCalStyles.MCS_NOTODAY : styles | (int)MonCalStyles.MCS_NOTODAY;
SendMessage(this.Handle, DTM_SETMCSTYLE, 0, styles);
}
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int DTM_FIRST = 0x1000;
private const int DTM_GETMONTHCAL = DTM_FIRST + 8;
private const int DTM_SETMCSTYLE = DTM_FIRST + 11;
private const int DTM_GETMCSTYLE = DTM_FIRST + 12;
private const int MCM_FIRST = 0x1000;
private const int MCM_GETCURRENTVIEW = MCM_FIRST + 22;
private const int MCM_SETCURRENTVIEW = MCM_FIRST + 32;
private bool m_ShowToday = false;
public enum MonCalView : int
{
MCMV_MONTH = 0,
MCMV_YEAR = 1,
MCMV_DECADE = 2,
MCMV_CENTURY = 3
}
public enum MonCalStyles : int
{
MCS_DAYSTATE = 0x0001,
MCS_MULTISELECT = 0x0002,
MCS_WEEKNUMBERS = 0x0004,
MCS_NOTODAYCIRCLE = 0x0008,
MCS_NOTODAY = 0x0010,
MCS_NOTRAILINGDATES = 0x0040,
MCS_SHORTDAYSOFWEEK = 0x0080,
MCS_NOSELCHANGEONNAV = 0x0100
}
}
The custom format is to be used to determine the way the date should be captured in the system/database irrespective of what the user selected, it is not used to restrict the user to choosing either month or year.

How to get the windows messages according to the child?

I am creating a form inside another form. In the child form I have created taskbar button. I am overriding WndProc in the button.I am referring https://www.codeproject.com/Articles/10171/Adding-a-Minimize-to-tray-button-to-a-Form-s-caption bar. But whenever I am getting messages(mouse coordinates) , it is given with respect to the parent form. This makes trouble in calculations .I want those with respect to child form.How to achieve this?
This is the code for form1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Form2 form2 = new Form2();
form2.TopLevel = false;
form2.AutoScroll = true;
form2.Anchor = AnchorStyles.Top;
form2.Dock = DockStyle.Fill;
this.splitContainer1.Panel2.Controls.Add(form2);
form2.Show();
}
}
this is form2.
public partial class Form2 : Form
{
TyronM.MinTrayBtn Pin_Button;
public Form2()
{
InitializeComponent();
Pin_Button = new TyronM.MinTrayBtn(this);
Pin_Button.MinTrayBtnClicked += new TyronM.MinTrayBtnClickedEventHandler(this.Pin_Button_Clicked);
}
public void Pin_Button_Clicked(object sender, EventArgs e)
{
MessageBox.Show("Pin button got Clicked");
}
protected override void WndProc(ref Message message)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (message.Msg)
{
case WM_SYSCOMMAND:
int command = message.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref message);
}
}
And this is for button.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Diagnostics;
using System.Resources;
using System.Runtime.InteropServices;
namespace TyronM {
public delegate void MinTrayBtnClickedEventHandler(object sender, EventArgs e);
/// <summary>
/// Summary description for Class.
/// </summary>
public class MinTrayBtn : NativeWindow {
bool pinned = false;
bool pressed = false;
Size wnd_size = new Size();
public bool captured;
Form parent;
public event MinTrayBtnClickedEventHandler MinTrayBtnClicked;
#region Constants
const int WM_SIZE = 5;
const int WM_SYNCPAINT = 136;
const int WM_MOVE = 3;
const int WM_ACTIVATE = 6;
const int WM_LBUTTONDOWN =513;
const int WM_LBUTTONUP =514;
const int WM_LBUTTONDBLCLK =515;
const int WM_MOUSEMOVE = 512;
const int WM_PAINT = 15;
const int WM_GETTEXT = 13;
const int WM_NCCREATE =129;
const int WM_NCLBUTTONDOWN = 161;
const int WM_NCLBUTTONUP = 162;
const int WM_NCMOUSEMOVE = 160;
const int WM_NCACTIVATE =134;
const int WM_NCPAINT = 133;
const int WM_NCHITTEST = 132;
const int WM_NCLBUTTONDBLCLK = 163;
const int VK_LBUTTON = 1;
const int SM_CXSIZE = 30;
const int SM_CYSIZE = 31;
#endregion
#region Extra Constants
const int WM_MBUTTONUP = 0x0208;
#endregion
#region WinAPI Imports
[DllImport("user32")]
public static extern int GetWindowDC(int hwnd);
[DllImport("user32")]
public static extern short GetAsyncKeyState(int vKey);
[DllImport("user32")]
public static extern int SetCapture(int hwnd);
[DllImport("user32")]
public static extern bool ReleaseCapture();
[DllImport("user32")]
public static extern int GetSysColor(int nIndex);
[DllImport("user32")]
public static extern int GetSystemMetrics(int nIndex);
#endregion
#region Constructor and Handle-Handler ^^
public MinTrayBtn(Form parent) {
parent.HandleCreated += new EventHandler(this.OnHandleCreated);
parent.HandleDestroyed+= new EventHandler(this.OnHandleDestroyed);
parent.TextChanged+= new EventHandler(this.OnTextChanged);
this.parent = parent;
}
// Listen for the control's window creation and then hook into it.
internal void OnHandleCreated(object sender, EventArgs e){
// Window is now created, assign handle to NativeWindow.
AssignHandle(((Form)sender).Handle);
}
internal void OnHandleDestroyed(object sender, EventArgs e) {
// Window was destroyed, release hook.
ReleaseHandle();
}
// Changing the Text invalidates the Window, so we got to Draw the Button again
private void OnTextChanged(object sender, EventArgs e) {
DrawButton();
}
#endregion
#region WndProc
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m){
//label3.Text = "Button pressed: " + pressed;
//label4.Text = "Mouse captured: " + captured;
// Change the Pressed-State of the Button when the User pressed the
// left mouse button and moves the cursor over the button
if (m.Msg == WM_LBUTTONDBLCLK ||m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONUP
||m.Msg == WM_MBUTTONUP
)
MessageBox.Show("click happened");
if(m.Msg==WM_MOUSEMOVE) {
Point pnt2 = new Point((int)m.LParam);
Size rel_pos2 = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
// Not needed because SetCapture seems to convert the cordinates anyway
//pnt2 = PointToClient(pnt2);
pnt2-=rel_pos2;
//label2.Text = "Cursor #"+pnt2.X+"/"+pnt2.Y;
if(pressed) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
//pnt = PointToClient(pnt);
pnt-=rel_pos;
if(!MouseinBtn(pnt)) {
pressed = false;
DrawButton();
}
} else {
if((GetAsyncKeyState(VK_LBUTTON)&(-32768))!=0) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
//pnt = PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
pressed = true;
DrawButton();
}
}
}
}
// Ignore Double-Clicks on the Traybutton
if(m.Msg==WM_NCLBUTTONDBLCLK) {
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt = parent.PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
return;
}
}
#region NOT WORKING
// Button released and eventually clicked
if(m.Msg==WM_LBUTTONUP)
{
ReleaseCapture();
captured = false;
if(pressed) {
pressed = false;
DrawButton();
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
//TrayButton_clicked();
EventArgs e = new EventArgs();
if (MinTrayBtnClicked != null)
MinTrayBtnClicked(this, e);
return;
}
}
}
#endregion
// Clicking the Button - Capture the Mouse and await until the Uses relases the Button again
if(m.Msg==WM_NCLBUTTONDOWN) {
//MessageBox.Show("clicked");
Point pnt = new Point((int)m.LParam);
Size rel_pos = new Size(parent.PointToClient(new Point(parent.Location.X, parent.Location.Y)));
pnt = parent.PointToClient(pnt);
pnt-=rel_pos;
if(MouseinBtn(pnt)) {
MessageBox.Show("clicked");
pressed = true;
DrawButton();
SetCapture((int)parent.Handle);
captured = true;
return;
}
}
// Drawing the Button and getting the Real Size of the Window
if(m.Msg == WM_ACTIVATE || m.Msg==WM_SIZE || m.Msg==WM_SYNCPAINT || m.Msg==WM_NCACTIVATE || m.Msg==WM_NCCREATE || m.Msg==WM_NCPAINT || m.Msg==WM_NCACTIVATE || m.Msg==WM_NCHITTEST || m.Msg==WM_PAINT) {
if(m.Msg==WM_SIZE) wnd_size = new Size(new Point((int)m.LParam));
DrawButton();
}
base.WndProc(ref m);
}
#endregion
#region Button-Specific Functions
public bool MouseinBtn(Point click) {
int btn_width = GetSystemMetrics(SM_CXSIZE);
int btn_height = GetSystemMetrics(SM_CYSIZE);
Size btn_size = new Size(btn_width, btn_height);
Point pos = new Point(wnd_size.Width - 3 * btn_width - 12 - (btn_width - 18)+7, 6+4);
return click.X>=pos.X && click.X<=pos.X+btn_size.Width &&
click.Y>=pos.Y && click.Y<=pos.Y+btn_size.Height;
}
public void DrawButton() {
Graphics g = Graphics.FromHdc((IntPtr)GetWindowDC((int)parent.Handle)); //m.HWnd));
DrawButton(g, pressed);
}
public void DrawButton(Graphics g, bool pressed) {
int btn_width = GetSystemMetrics(SM_CXSIZE);
int btn_height = GetSystemMetrics(SM_CYSIZE);
//Point pos = new Point(wnd_size.Width-3*btn_width-12-(btn_width-18),6);
Point pos = new Point(wnd_size.Width - 3 * btn_width - 12 - (btn_width - 18)+7, 6+4);
// real button size
btn_width-=2;
btn_height-=4;
Color light = SystemColors.ControlLightLight;
Color icon = SystemColors.ControlText;
Color background = SystemColors.Control;
Color shadow1 = SystemColors.ControlDark;
Color shadow2 = SystemColors.ControlDarkDark;
Color tmp1, tmp2;
if(pressed) {
tmp1 = shadow2;
tmp2 = light;
} else {
tmp1 = light;
tmp2 = shadow2;
}
g.DrawLine(new Pen(tmp1),pos, new Point(pos.X+btn_width-1,pos.Y));
g.DrawLine(new Pen(tmp1),pos, new Point(pos.X,pos.Y+btn_height-1));
if(pressed) {
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+1, pos.X+btn_width-2, pos.Y+1);
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+1, pos.X+1, pos.Y+btn_height-2);
} else {
g.DrawLine(new Pen(shadow1),pos.X+btn_width-2, pos.Y+1, pos.X+btn_width-2, pos.Y+btn_height-2);
g.DrawLine(new Pen(shadow1),pos.X+1, pos.Y+btn_height-2, pos.X+btn_width-2, pos.Y+btn_height-2);
}
g.DrawLine(new Pen(tmp2),pos.X+btn_width-1, pos.Y+0, pos.X+btn_width-1, pos.Y+btn_height-1);
g.DrawLine(new Pen(tmp2),pos.X+0, pos.Y+btn_height-1, pos.X+btn_width-1, pos.Y+btn_height-1);
g.FillRectangle(new SolidBrush(background),pos.X+1+Convert.ToInt32(pressed), pos.Y+1+Convert.ToInt32(pressed), btn_width-3,btn_height-3);
#region Added Code
g.FillRectangle(new SolidBrush(icon),pos.X+(float)0.5625*btn_width+Convert.ToInt32(pressed),pos.Y+(float)0.6428*btn_height+Convert.ToInt32(pressed),btn_width*(float)0.1875,btn_height*(float)0.143);
g.DrawImage(Image.FromFile("red_Pushpin.jfif"),new Rectangle( pos,new Size(btn_width,btn_height)));
#endregion
}
#endregion
}
}
This is code for main
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
This is the screenshot of the winform.
EDIT:
Upon investigation I found that WM_LBUTTONUP(Mouse left button up) is not triggered. Or it is not coming inside WndProc.
EDIT1:
It seems there was a problem in brackets.I changed the if conditions into switch case. This somewhat cleared the "Mouse left button up" problem. Now it is properly triggering. The remaining problem is mouse points based on parent form.

Multiline combobox dropdown list

I am trying to figure out the best way to populate address fields from a select list (vague but read on)..
The layout:
When I select the Address dropdown, I would like to see a nice list of full addresses, ie, with street name, country, postcode, etc. but as Im sure you are aware, combo's are one liners only.
Ideal scenario:
The result:
Has anyone a method for doing this?
Here is the full solution and as you can see, its perfectly what I wanted.
The ComboBoxEx is a class derived from a ComboBox which I have copied last. The reason for it is to set the height of the Items container (DropDownHeight). Without it, the container is calculated on the size of the first item x no. of items, and since the first item is zero height, the container would be zero height. So it needed a new class.
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitializeComboBox();
}
private ComboxBoxEx cbox1 = new ComboxBoxEx();
private DataTable items = new DataTable();
private void InitializeComboBox()
{
items.Columns.AddRange(new DataColumn[] { new DataColumn("id"), new DataColumn("name"), new DataColumn("address") });
items.Rows.Add(new object[] { 0, "[Please choose an address]", "" });
items.Rows.Add(new object[] { 1, "Country", "Country" });
items.Rows.Add(new object[] { 2, "House name", "House name\nStreet name\nTown name\nPostcode\nCountry" });
items.Rows.Add(new object[] { 3, "House name", "House name\nStreet name\nTown name\nPostcode\nCountry" });
cbox1.Location = new Point(39, 20);
cbox1.Size = new System.Drawing.Size(198, 21);
cbox1.DrawMode = DrawMode.OwnerDrawVariable;
cbox1.DrawItem += new DrawItemEventHandler(comboBox2_DrawItem);
cbox1.MeasureItem += new MeasureItemEventHandler(comboBox2_MeasureItem);
cbox1.SelectedIndexChanged += new EventHandler(comboBox2_SelectedIndexChanged);
//cbox1.DropDownWidth = 250;
//cbox1.DropDownHeight = 300;
//cbox1.MaxDropDownItems = 6;
this.Controls.Add(cbox1);
cbox1.ValueMember = "id";
cbox1.DisplayMember = "name";
cbox1.DataSource = new BindingSource(items, null);
//cbox1.SelectedIndex = -1;
}
private void comboBox2_MeasureItem(object sender, MeasureItemEventArgs e)
{
ComboxBoxEx cbox = (ComboxBoxEx)sender;
DataRowView item = (DataRowView)cbox.Items[e.Index];
string txt = item["address"].ToString();
int height = Convert.ToInt32(e.Graphics.MeasureString(txt, cbox.Font).Height);
e.ItemHeight = height + 4;
e.ItemWidth = cbox.DropDownWidth;
cbox.ItemHeights.Add(e.ItemHeight);
}
private void comboBox2_DrawItem(object sender, DrawItemEventArgs e)
{
ComboxBoxEx cbox = (ComboxBoxEx)sender;
DataRowView item = (DataRowView)cbox.Items[e.Index];
string txt = item["address"].ToString();
e.DrawBackground();
e.Graphics.DrawString(txt, cbox.Font, System.Drawing.Brushes.Black, new RectangleF(e.Bounds.X + 2, e.Bounds.Y + 2, e.Bounds.Width, e.Bounds.Height));
e.Graphics.DrawLine(new Pen(Color.LightGray), e.Bounds.X, e.Bounds.Top + e.Bounds.Height - 1, e.Bounds.Width, e.Bounds.Top + e.Bounds.Height - 1);
e.DrawFocusRectangle();
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
ComboxBoxEx cbox = (ComboxBoxEx)sender;
if (cbox.SelectedItem == null) return;
DataRowView item = (DataRowView)cbox.SelectedItem;
//label1.Text = item["id"].ToString();
}
}
}
The ComboBoxEx Class
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace WindowsFormsApplication1
{
public partial class ComboxBoxEx : ComboBox
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
public const int SWP_NOZORDER = 0x0004;
public const int SWP_NOACTIVATE = 0x0010;
public const int SWP_FRAMECHANGED = 0x0020;
public const int SWP_NOOWNERZORDER = 0x0200;
public const int WM_CTLCOLORLISTBOX = 0x0134;
private int _hwndDropDown = 0;
internal List<int> ItemHeights = new List<int>();
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_CTLCOLORLISTBOX)
{
if (_hwndDropDown == 0)
{
_hwndDropDown = m.LParam.ToInt32();
RECT r;
GetWindowRect((IntPtr)_hwndDropDown, out r);
int newHeight = 0;
int n = (Items.Count > MaxDropDownItems) ? MaxDropDownItems : Items.Count;
for (int i = 0; i < n; i++)
{
newHeight += ItemHeights[i];
}
newHeight += 5; //to stop scrollbars showing
SetWindowPos((IntPtr)_hwndDropDown, IntPtr.Zero,
r.Left,
r.Top,
DropDownWidth,
newHeight,
SWP_FRAMECHANGED |
SWP_NOACTIVATE |
SWP_NOZORDER |
SWP_NOOWNERZORDER);
}
}
base.WndProc(ref m);
}
protected override void OnDropDownClosed(EventArgs e)
{
_hwndDropDown = 0;
base.OnDropDownClosed(e);
}
}
}

Categories