Autoclicker Visual studio CS - c#

I tried to make my own autoclicker. Everything went fine.
But when i press the button it only changes status from off to on.. but nothing happens.
I tried very long time to check for errors but i didnt find any.
This is my script so if you find any errors please tell me!
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace BetaClicker
{
public partial class form1 : Form
{
[DllImport(dllName:"user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);
private const int LEFTUP = 0x0004;
private const int LEFTDOWN = 0x0002;
public form1()
{
InitializeComponent();
}
private void clicktimer_Tick(object sender, EventArgs e)
{
Random rnd = new Random();
int maxcps = (int)Math.Round(1000.0 / (trackBar1.Value + trackBar2.Value * 0.2));
int mincps = (int)Math.Round(1000.0 / (trackBar1.Value + trackBar2.Value * 0.4));
try
{
clicktimer.Interval = rnd.Next(mincps, maxcps);
}
catch
{
//Ignored
}
bool mousdown = MouseButtons == MouseButtons.Left;
if (mousdown)
{
mouse_event(dwFlags:LEFTDOWN, dx:0,dy:0,cButtons:0,dwExtraInfo:0);
Thread.Sleep(millisecondsTimeout: rnd.Next(1, 6));
mouse_event(dwFlags: LEFTUP, dx: 0, dy: 0, cButtons: 0, dwExtraInfo: 0);
}
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text.Contains("on"))
{
clicktimer.Start();
button1.Text = "Toggle: off";
}
else
{
clicktimer.Stop();
button1.Text = "Toggle: on";
}
}
}
}

I can suggest you to change the portion of code where you click with the following:
int X = Cursor.Position.X;
int Y = Cursor.Position.Y;
mouse_event(LEFTDOWN | LEFTUP, X, Y, 0, 0);
I think that your problem is that you start clicking but always in the position of the screen at the coordinates (0, 0) so nothing happens.
Also you don't actually need a timeout from the LEFTDOWN action and the LEFTUP one
If you don't want to click where your cursor is just set X and Y with your desired values (coordinates)

Related

How can I enable input microphone when I select on the combo box

I am making Speech to text application in C# window form with Microsoft Azure. I would like to have a combo box on my form which show the user enable microphone. When I select the enable microphone and press the ok button the microphone will be select as input which I selected. Which I have include in combo box which they would like to use.
How can I implement such a feature?
Code:
using System;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.IO;
using System.ComponentModel;
using Microsoft.CognitiveServices.Speech;
namespace WindowsFormsApp2
{
public partial class Form2 : Form
{
[DllImport("winmm.dll", SetLastError = true)]
static extern uint waveInGetNumDevs();
[DllImport("winmm.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint waveInGetDevCaps(uint hwo, ref WAVEOUTCAPS pwoc, uint cbwoc);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WAVEOUTCAPS
{
public ushort wMid;
public ushort wPid;
public uint vDriverVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string szPname;
public uint dwFormats;
public ushort wChannels;
public ushort wReserved1;
public uint dwSupport;
}
public void FillSoundDevicesInCombobox()
{
uint devices = waveInGetNumDevs();
WAVEOUTCAPS caps = new WAVEOUTCAPS();
for (uint i = 0; i < devices; i++)
{
waveInGetDevCaps(i, ref caps, (uint)Marshal.SizeOf(caps));
CB1.Items.Add(caps.szPname);
}
}
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
FillSoundDevicesInCombobox();
// Only select the device if there is a value to load
if (!String.IsNullOrWhiteSpace(Properties.Settings.Default.SelectedAudioDevice))
{
// find the device, matching on the value, not index
var item = CB1.Items.Cast<string>().FirstOrDefault(x => x.Equals(Properties.Settings.Default.SelectedAudioDevice));
// only select the device if we found one that matched the previous selection.
if (item != null)
CB1.SelectedItem = item;
}
checkBox1.Checked = Properties.Settings.Default.Punctuation;
//CB1.SelectedItem = Properties.Settings.Default.FillSoundDevicesInCombobox;
}
private void button1_Click(object sender, EventArgs e)
{
Properties.Settings.Default.SelectedAudioDevice = CB1.SelectedItem?.ToString();
Properties.Settings.Default.Punctuation = checkBox1.Checked;
//Properties.Settings.Default.FillSoundDevicesInCombobox = CB1.SelectedItem?.ToString();
Properties.Settings.Default.Save();
Close();
}
private void button2_Click(object sender, EventArgs e)
{
Close();
}
}
}
You can use use Lync2013SDK by installing below nuget package:
Install-Package Lync2013SDK -Version 15.0.4466.1000
Then call below function:
private void ComboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
ComboBox comboBox = (ComboBox) sender;
string selected = (string) ComboBox1.SelectedItem;
int i = ComboBox1.FindStringExact(selected);
var manager = LyncClient.GetClient().DeviceManager;
manager.ActiveAudioDevice = (AudioDevice)manager.AudioDevices[i]; //🠔 This is what you want
}
I would recommend using Microsoft.Xna.Framework.Audio instead of interop.
Here's an example of a program which starts recording if a volume threshold on a microphone is breached, I just pulled it from a much larger project so it is incomplete but if one can read the code, it should show one how to get started.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Windows.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Yeti.MMedia;
using Yeti.MMedia.Mp3;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Threading;
using LoudSoundMonitor;
using System.ComponentModel;
using System.Security;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Collections;
namespace LoudSoundMonitor
{
public partial class MainForm : Form
{
Microphone microphone = Microphone.Default;
byte[] buffer;
byte[] backBuffer;
private int minimumThreshold = 759990;
private int thresholdCount = 0;
private int thresholdCountMax = 3;
private int delayStartSeconds = 4;
private Mp3Writer mp3Writer;
private const int LSM_NotStarted = 1;
private const int LSM_Started = 2;
private const int LSM_Recording = 3;
private const int LSM_Paused = 4;
private const char LSM_Client_Transmission_Complete = '\u0004';
private int LSM_State = LSM_NotStarted;
private int prev_LSM_State;
private string loudSoundDir = System.Windows.Forms.Application.StartupPath + "\\LoudSounds\\";
private string baseDir = System.Windows.Forms.Application.StartupPath;
private static readonly Regex HttpPostParameterDelimiterRegex = new Regex(#"&", RegexOptions.Compiled);
private static readonly Regex HttpPostParameterRegex = new Regex(#"^(?<ParameterName>\S+)=(?<ParameterValue>\S*)$", RegexOptions.Compiled);
private const int ERROR_SHARING_VIOLATION = 32;
private const int ERROR_ALREADY_EXISTS = 183;
private object lameMp3EncoderLock = new object();
void microphone_BufferWrite(object sender, EventArgs e)
{
lock (lameMp3EncoderLock)
{
if (backBuffer != null)
{
mp3Writer.Write(backBuffer, 0, backBuffer.Length);
backBuffer = null;
}
else
{
mp3Writer.Write(buffer, 0, buffer.Length);
}
microphone.GetData(buffer);
if (LSM_State != LSM_Recording)
{
mp3Writer.Write(buffer, 0, buffer.Length);
mp3Writer.Close();
backBuffer = new byte[buffer.Length * thresholdCountMax];
microphone.BufferReady -= new EventHandler<EventArgs>(microphone_BufferWrite);
microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferMonitor);
}
}
}
void microphone_BufferMonitor(object sender, EventArgs e)
{
// Retrieve audio data
microphone.GetData(buffer);
// RMS Method
double rms = 0;
ushort byte1 = 0;
ushort byte2 = 0;
short value = 0;
int volume = 0;
rms = (short)(byte1 | (byte2 << 8));
for (int i = 0; i < buffer.Length - 1; i += 2)
{
byte1 = buffer[i];
byte2 = buffer[i + 1];
value = (short)(byte1 | (byte2 << 8));
rms += Math.Pow(value, 2);
}
rms /= (double)(buffer.Length / 2);
volume = (int)Math.Floor(Math.Sqrt(rms));
System.Diagnostics.Debug.WriteLine("buffer.Length" + buffer.Length + " Volume:" + volume);
if ((volume > minimumThreshold))
{
System.Buffer.BlockCopy(buffer, 0, backBuffer, thresholdCount * buffer.Length, buffer.Length);
thresholdCount++;
if (thresholdCount == thresholdCountMax)
{
thresholdCount = 0;
button1.Invoke(new System.Action(delegate()
{
button1.Text = "Recording...Press To Stop";
button1.Enabled = true;
}));
String fileName = "LoudSound_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".mp3";
if (!Directory.Exists(loudSoundDir))
{
Directory.CreateDirectory(loudSoundDir);
}
lock (lameMp3EncoderLock)
{
mp3Writer = new Mp3Writer(new FileStream(loudSoundDir + fileName, FileMode.Create), new WaveLib.WaveFormat(microphone.SampleRate, 16, 1));
}
lock (lockLSM_State)
{
LSM_State = LSM_Recording;
microphone.BufferReady -= new EventHandler<EventArgs>(microphone_BufferMonitor);
microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferWrite);
}
}
System.Diagnostics.Debug.WriteLine("Threshold exceeded");
System.Diagnostics.Debug.WriteLine("buffer.Length" + buffer.Length + " Volume:" + volume);
}
}
else
{
thresholdCount = 0;
}
}
public MainForm()
{
InitializeComponent();
microphone.BufferDuration = TimeSpan.FromMilliseconds(100);
buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
backBuffer = new byte[buffer.Length * thresholdCountMax];
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(50);
dt.Tick += delegate(object sender, EventArgs e)
{
try { FrameworkDispatcher.Update(); }
catch { }
};
microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferMonitor);
int curInterval = 0;
System.Threading.Timer delayStart = null;
delayStart =
new System.Threading.Timer(
delegate
{
if (curInterval == delayStartSeconds)
{
dt.Start();
microphone.Start();
delayStart.Dispose();
button1.Invoke(new System.Action(delegate()
{
button1.Text = "Monitoring...";
}));
LSM_State = LSM_Started;
}
else
{
button1.Invoke(new System.Action(delegate()
{
button1.Text = "Beginning Monitoring in " + (delayStartSeconds - curInterval) + " seconds";
}));
}
curInterval++;
}
);
this.Load += delegate
{
delayStart.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
};
}
}
private void button1_Click(object sender, EventArgs e)
{
button1.Text = "Monitoring...";
button1.Enabled = false;
lock (lockLSM_State)
{
LSM_State = LSM_Started;
prev_LSM_State = 0;
if (microphone.State == MicrophoneState.Stopped)
{
microphone.Start();
}
}
}

Continuously desktop region capture failed after some time - Unhandled exception in System.Drawing.dll (parameter is not valid)

I am making a an app where I have to capture selected desktop region continuously(using "Timer(CaptureTimer)" with interval 100) , resize it , make it to dither image & show it to "PictureBox(PreviewPictureBox)".
My app has two "Form"s.
One is "MainForm" contains "PreviewPictureBox" & "Button(StartButton)" & other is "CaptureForm" contains "CapturePictureBox".
"CaptureForm(size is 646x326)" is transparent & FromBorderStyle = none.
"CapturePictureBox" is "dock in parent container" & has a side bordered & middle transparent png picture in it.
"CaptureForm" can be move by click & dragging the "CapturePictureBox".
This is "MainForm"
Here is "MainForm" Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace _128x64_GLCD_Monitor
{
public partial class MainForm : Form
{
CaptureForm CapF = new CaptureForm();
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
}
public Bitmap ScreenCaptureBitmap(int DesktopX, int DesktopY, int CaptureWidth, int CaptureHeight)
{
Bitmap ScreenCaptureBmp = new Bitmap(CaptureWidth, CaptureHeight);
Graphics graphics = Graphics.FromImage(ScreenCaptureBmp as Image);
graphics.CopyFromScreen(DesktopX, DesktopY, 0, 0, ScreenCaptureBmp.Size);
return ScreenCaptureBmp;
}
public Bitmap ResizeBitmap(Bitmap ResizeBmp, int RBmpWidth, int RBmpHeight)
{
Bitmap RBmp = new Bitmap(RBmpWidth, RBmpHeight);
using (Graphics RBmpG = Graphics.FromImage((Image)RBmp))
RBmpG.DrawImage(ResizeBmp, 0, 0, RBmpWidth, RBmpHeight);
return RBmp;
}
public Bitmap DitherBitmap(Bitmap DitherBmp) // Not writing full method here
private void StartButton_Click(object sender, EventArgs e)
{
CapF.Show();
CaptureTimer.Start();
}
private void CaptureTimer_Tick(object sender, EventArgs e)
{
int windowLeft = CapF.Left + 3;
int windowTop = CapF.Top + 3;
int windowWidth = CapF.Width - 6;
int windowHeight = CapF.Height - 6;
Bitmap Pic = ScreenCaptureBitmap(windowLeft, windowTop, windowWidth, windowHeight);
Bitmap Pic1 = ResizeBitmap(Pic, 128, 64);
Bitmap Pic2 = DitherBitmap(Pic1);
PreviewPictureBox.Image = Pic2;
}
}
}
This is "CaptureForm"
Here is "CaptureForm" Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace _128x64_GLCD_Monitor
{
public partial class CaptureForm : Form
{
Boolean TogMove;
int MValX, MValY;
public CaptureForm()
{
InitializeComponent();
}
private void CaptureForm_Load(object sender, EventArgs e)
{
TransparencyKey = BackColor;
}
private void CapturePictureBox_MouseDown(object sender, MouseEventArgs e)
{
TogMove = true;
MValX = e.X;
MValY = e.Y;
}
private void CapturePictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (TogMove == true)
{
this.SetDesktopLocation(MousePosition.X - MValX, MousePosition.Y - MValY);
}
}
private void CapturePictureBox_MouseUp(object sender, MouseEventArgs e)
{
TogMove = false;
}
}
}
When I start debugging, after some time(after completing some loop) it getting this message
Visual Studio 2010 pointed this line(at a 1 time debugging 1 line)
Some time this line
Bitmap ScreenCaptureBmp = new Bitmap(CaptureWidth, CaptureHeight);
Or some time this line
graphics.CopyFromScreen(DesktopX, DesktopY, 0, 0, ScreenCaptureBmp.Size);
From this method
public Bitmap ScreenCaptureBitmap(int DesktopX, int DesktopY, int CaptureWidth, int CaptureHeight)
What I have to do to prevent this error?
Make sure the following 4 variables are within the size of the Form. When you click the numbers can be negative or greater than the size (width , height) of the form. :
int windowLeft = CapF.Left + 3;
int windowTop = CapF.Top + 3;
int windowWidth = CapF.Width - 6;
int windowHeight = CapF.Height - 6;
It looks like the values for windowLeft, WindowTop, windowWidth or windowHeight in your timer methode, went negative.
If you only want to prevent this error, you can simply set the values in an if-clause before you call the method ScreenCaptureBitmap(...) or you prevent the dragging to negative values in the LocationChanges-Event.

C# windows forms application volume slider

I have an application that plays a .wav file using the soundplayer, I looked it up and couldn't find a way to change the volume it plays in. What I'm looking for is either to change the volume of the file independently through the program or have a slider to change the volume of the window itself in windows volume mixer. Thanks!
public void loadSound()
{
sp.Load();
sp.Play();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (BarTimer.Value < BarTimer.Maximum)
{
BarTimer.Value = BarTimer.Value + 1;
}
if(BarTimer.Value==BarTimer.Maximum)
{
loadSound();
timer1.Stop();
BarTimer.Value = BarTimer.Minimum;
}
}
I only found this on MSDN: Attenuating SoundPlayer Volume.
It uses waveOutGetVolume and waveOutSetVolume functiuons.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VolumeControl
{
public partial class Form1 : Form
{
[DllImport("winmm.dll")]
public static extern int waveOutGetVolume(IntPtr hwo, out uint dwVolume);
[DllImport("winmm.dll")]
public static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume);
public Form1()
{
InitializeComponent();
// By the default set the volume to 0
uint CurrVol = 0;
// At this point, CurrVol gets assigned the volume
waveOutGetVolume(IntPtr.Zero, out CurrVol);
// Calculate the volume
ushort CalcVol = (ushort)(CurrVol & 0x0000ffff);
// Get the volume on a scale of 1 to 10 (to fit the trackbar)
trackWave.Value = CalcVol / (ushort.MaxValue / 10);
}
private void trackWave_Scroll(object sender, EventArgs e)
{
// Calculate the volume that's being set. BTW: this is a trackbar!
int NewVolume = ((ushort.MaxValue / 10) * trackWave.Value);
// Set the same volume for both the left and the right channels
uint NewVolumeAllChannels = (((uint)NewVolume & 0x0000ffff) | ((uint)NewVolume << 16));
// Set the volume
waveOutSetVolume(IntPtr.Zero, NewVolumeAllChannels);
}
}
}
Hope it helped.

Drawing lines behind textbox C#

I am working on creating a simple notebook application. I have been asked to make the input area look like a sheet of notebook paper, with the text sitting on light blue lines. I am trying to make this work, but it seems to be failing miserably.
So far, I have created a transparent RichTextBox that sits on top of a panel. The Text Box is:
using System;
using System.Windows.Forms;
public class TransparentTextBox : RichTextBox
{
public TransparentTextBox()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
The paint code for the panel:
private void paper_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.White);
g.DrawLine(new Pen(Brushes.LightPink, 2), 20, 0, 20, paper.Height);
int h = TextRenderer.MeasureText("Testj", txtBody.Font).Height;
for (int x = 2 + h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
}
The lines are static, and they will grow to fit any font size/family that is chosen. The problem is when the text box is scrolled. The lines won't move with the text. I have tried to link the handle of the scroll bar to the lines, but they don't seem to be linking properly.
The code to get the current scroll position:
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLINFO
{
public int cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
public enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
public enum ScrollInfoMask
{
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
}
...
public partial class Form1 : Form
{
[DllImport("User32.dll", EntryPoint = "GetScrollInfo")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo([In]IntPtr hwnd, [In]int fnBar, [In, Out]ref SCROLLINFO lpsi);
...
private void txtBody_VScroll(object sender, EventArgs e)
{
inf.cbSize = Marshal.SizeOf(inf);
inf.fMask = (int)ScrollInfoMask.SIF_ALL;
GetScrollInfo(txtBody.Handle, 1, ref inf);
Console.WriteLine(inf.nTrackPos + ":" + inf.nPos + ":" + TextRenderer.MeasureText("Testj", txtBody.Font).Height);
paper.Invalidate();
}
Then the paint above was modified to use this:
for (int x = inf.nPos % h; x < paper.Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, paper.Width, x);
}
I also tried to use nTrackPos, but neither seemed to follow the text like I want it to. I'm not too familiar with C#, so I wanted to know what I am missing/could do better. I am using Visual Studio 2008, with Visual C# 2008. .Net framework 3.5 SP1
So, here is what I came up with after some intensive googling. I decided to follow more into Gusman's comment on my question and look into drawing on the textbox again. After some playing, I realized I was improperly calculating the position of the start line. So, I reconfigured my custom RichTextBox to look like:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Journal
{
class CustomRichTextBox : RichTextBox
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int WM_MOUSEWHEEL = 0x20A;
private const int WM_PAINT = 0x00F;
private const int EM_GETSCROLLPOS = 0x4DD;
public int lineOffset = 0;
[DllImport("user32.dll")]
public static extern int SendMessage(
IntPtr hWnd,
int Msg,
IntPtr wParam,
ref Point lParam
);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
{
using (Graphics g = base.CreateGraphics())
{
Point p = new Point();
//get the position of the scrollbar to calculate the offset
SendMessage(this.Handle, EM_GETSCROLLPOS, IntPtr.Zero, ref p);
//draw the pink line on the side
g.DrawLine(new Pen(Brushes.LightPink, 2), 0, 0, 0, this.Height);
//determine how tall the text will be per line
int h = TextRenderer.MeasureText("Testj", this.Font).Height;
//calculate where the lines need to start
lineOffset = h - (p.Y % h);
//draw lines until there is no more box
for (int x = lineOffset; x < Height; x += h)
{
g.DrawLine(new Pen(Brushes.LightSkyBlue, 2), 0, x, Width, x);
}
//force the panel under us to draw itself.
Parent.Invalidate();
}
}
}
public CustomRichTextBox()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
}
}
I then set this box inside of a panel to get the padding I want. The panel is forced to redraw itself with the text box.

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