.ShowDialog() shows twice - c#

I have an annoying problem...
I just wanted to show a dialog, but it's always showing twice... Here's the code:
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
frm_lockscreen login= new frm_lockscreen();
tmr_sysdt.Enabled = false;
if (login.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
The tmr_sysdt.Interval is 1000.
The problem is simple - but unsolvable for me - the dialog is showing in duplicate (the second is coming up AT THE SAME TIME as the first).
I have no idea, so any idea will be appreciated :)
Thank you, and if you need more details please comment!
Ps.: Sorry for bad eng
EDIT: GetLastInputTime()
[DllImport("user32.dll")]
static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
static uint GetLastInputTime()
{
uint idleTime = 0;
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = (uint)Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
uint envTicks = (uint)Environment.TickCount;
if (GetLastInputInfo(ref lastInputInfo))
{
uint lastInputTick = lastInputInfo.dwTime;
idleTime = envTicks - lastInputTick;
}
return ((idleTime > 0) ? (idleTime / 1000) : 0);
}
[StructLayout(LayoutKind.Sequential)]
struct LASTINPUTINFO
{
public static readonly int SizeOf = Marshal.SizeOf(typeof(LASTINPUTINFO));
[MarshalAs(UnmanagedType.U4)]
public UInt32 cbSize;
[MarshalAs(UnmanagedType.U4)]
public UInt32 dwTime;
}

Could you try this code? Just to make sure the logic in your handler isn't called twice at the same time:
static bool busy = false;
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
if (busy)
{
return;
}
busy = true;
try
{
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
frm_lockscreen login = new frm_lockscreen();
tmr_sysdt.Enabled = false;
if (login.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
finally
{
busy = false;
}
}
The problem you were having was that the code to check the last activity of the user and show the login box, was in a form that is created multiple times.
So in fact, each of these 'main forms' were checking the user activity and showing the login box.
By putting the busy bool as static, each instance of the 'main form' read this bool as the same value. Therefore, the check+show login only gets executed one time.
I would suggest moving this code to a form you only create once, and keep open during the full lifetime of the application. (If you have such a form)

You want to reuse your frm_lockscreen and prevent reentry to your Tick event
private frm_lockscreen _lockScreen;
private frm_lockscreen LockScreen
{
get { return _lockScreen ?? (_lockScreen = new frm_lockscreen()); }
}
private void tmr_sysdt_Tick(object sender, EventArgs e)
{
// prevent reentry
if (!Monitor.TryEnter(tmr_sysdt)) return;
try {
lbl_time.Text = System.DateTime.Now.ToLongTimeString();
lbl_date.Text = System.DateTime.Now.ToLongDateString();
if (GetLastInputTime() > Program.timeout)
{
tmr_sysdt.Enabled = false;
if (LockScreen.ShowDialog(this) == DialogResult.OK) tmr_sysdt.Enabled = true;
}
}
finally {
Monitor.Exit(tmr_sysdt);
}
}

The reason is that your outer if condition is returning true twice:
if (GetLastInputTime() > Program.timeout)
To find the culprit you have to check the values returned by GetLastInputTime() and Program.Timeout

Related

How to record while the window is not active?

I am making a recorder that records a specific window the user chooses. While I am recording, the program forces the window to be active all the time. It does not let me minimize it or even move it. It forced me to use alt + f4(that kills the active window) because I was not able to stop the recording. Is there a way to fix that?
Combobox code:
private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
if (Handlebox.SelectedIndex == 0)
{
handle_name = Handlebox.Items[0].ToString();
}
else if (Handlebox.SelectedIndex == 1)
{
handle_name = Handlebox.Items[1].ToString();
}
else if (Handlebox.SelectedIndex == 2)
{
handle_name = Handlebox.Items[2].ToString();
}
else if (Handlebox.SelectedIndex == 3)
{
handle_name = Handlebox.Items[3].ToString();
}
else if (Handlebox.SelectedIndex == 4)
{
handle_name = Handlebox.Items[4].ToString();
}
else if (Handlebox.SelectedIndex == 5)
{
handle_name = Handlebox.Items[5].ToString();
}
}
The way I add all the processes in the combobox:
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle))
{
Handlebox.Items.Add(process.ProcessName);
}
}
The script that the whole recording takes place:
// Record video:
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern IntPtr ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref Rect rect);
public Bitmap CaptureApplication(string procName)
{
Process proc;
// Cater for cases when the process can't be located.
try
{
proc = Process.GetProcessesByName(procName)[0];
}
catch (IndexOutOfRangeException e)
{
return null;
}
// You need to focus on the application
SetForegroundWindow(proc.MainWindowHandle);
ShowWindow(proc.MainWindowHandle, SW_RESTORE);
// Delay
Thread.Sleep(1000);
Rect rect = new Rect();
IntPtr error = GetWindowRect(proc.MainWindowHandle, ref rect);
// sometimes it gives error.
while (error == (IntPtr)0)
{
error = GetWindowRect(proc.MainWindowHandle, ref rect);
}
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics.FromImage(bmp).CopyFromScreen(rect.left,rect.top,0,0,new Size(width, height),CopyPixelOperation.SourceCopy);
return bmp;
}
public void RecordVideo()
{
// Keep track of time:
watch.Start();
// Save screenshot:
string name = tempPath + "//screenshot-" + fileCount + ".png";
CaptureApplication(handle_name).Save(name, ImageFormat.Png);
inputImageSequence.Add(name);
fileCount++;
// Dispose of bitmap:
CaptureApplication(handle_name).Dispose();
}
Apologies for the brevity and style of this reply; I'm currently on mobile.
Could you express your combobox event handler as:
private void Handlebox_SelectedIndexChanged(object sender, EventArgs e)
{
if (Handlebox.SelectedIndex == -1) return;
handle_name = Handlebox.SelectedItem.ToString();
}
I'm not sure what exactly you want this application to do eventually, but If all you want is to gracefully exit, I would look into registering an event handler that watches for a key combination, and stops and saves the recording when it is pressed. I can't easily type out how to do that, but some concerted googling should help you.

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.

moving 2 controls (picturebox) moving parallel not sequence

the two virtual mouse image mission is move horizontal movement and click the destination point . in the following code is running successfully in Sequence (one by one ).
i want to edit this code to working parallel (the two image working in the same time ) .
the mouseimg1 refer to => mouse 1 and mouseimg2 refer to => mouse 2 .
public partial class Form1 : Form
{
public delegate void delMouse();
public delMouse delMouse1; enter code here
public delMouse delMouse2;
private Thread t1, t2;
public Form1()
{
InitializeComponent();
delMouse1 = new delMouse(mouse1);
delMouse2 = new delMouse(mouse2);
}
private void button1_Click(object sender, EventArgs e)
{
t1 = new Thread(new ThreadStart(delMouse1));
t1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
t2 = new Thread(new ThreadStart(delMouse2));
t2.Start();
}
void mouse2()
{
if (this.mouseimg2.InvokeRequired)
{
this.Invoke(delMouse2);
}
else
{
int destinval2 = int.Parse(textBox2.Text);
while (mouseimg2.Location.Y != destinval2)
{
if (mouseimg2.Location.Y == 250)
mouseimg2.Location = new Point(mouseimg2.Location.X, 15);
if (mouseimg2.Location.Y < destinval2)
mouseimg2.Location = new Point(mouseimg2.Location.X, mouseimg2.Location.Y + 1);
else
mouseimg2.Location = new Point(mouseimg2.Location.X, mouseimg2.Location.Y - 1);
}
LeftClick(mouseimg2.Location.X, mouseimg2.Location.Y);
}
}
void mouse1()
{
if (this.mouseimg1.InvokeRequired)
{
this.Invoke(delMouse1);
}
else
{
int destinval1 = int.Parse(textBox1.Text);
while (mouseimg1.Location.Y != destinval1)
{
if (mouseimg1.Location.Y < destinval1)
mouseimg1.Location = new Point(mouseimg1.Location.X, mouseimg1.Location.Y + 1);
else
mouseimg1.Location = new Point(mouseimg1.Location.X, mouseimg1.Location.Y - 1);
}
LeftClick(mouseimg1.Location.X, mouseimg1.Location.Y);
}
}
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
public static void LeftClick(int x, int y)
{
Cursor.Position = new System.Drawing.Point(x, y);
mouse_event((int)(MouseEventFlags.LEFTDOWN), 0, 0, 0, 0);
mouse_event((int)(MouseEventFlags.LEFTUP), 0, 0, 0, 0);
}
}
screenshot
In general in Windows you should not access a window from a thread other than the one which created it. WinForms enforces this rule, as you've found.
I'm not really clear what you want to do, but creating two threads is almost certainly the wrong way to go about it.
Behaviour that looks a bit like multithreading but isn't is often accomplished using timers, if that's at all helpful to you.

Taskbar location

How can i detect where the taskbar is located? I need to know for displaying my notification in the right corner. Thanks
Edit:
Thank you Hans Passant. I used that with this to get location. I hope is ok.
GetTaskbarLocation(TaskbarPosition.GetTaskbarPosition());
private void GetTaskbarLocation(Rectangle rc)
{
if (rc.X == rc.Y)
{
if (rc.Right < rc.Bottom)
taskbarLocation = TaskbarLocation.Left;
if (rc.Right > rc.Bottom)
taskbarLocation = TaskbarLocation.Top;
}
if (rc.X > rc.Y)
taskbarLocation = TaskbarLocation.Right;
if (rc.X < rc.Y)
taskbarLocation = TaskbarLocation.Bottom;
}
public static Rectangle GetTaskbarPosition() {
var data = new APPBARDATA();
data.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero) throw new Win32Exception("Please re-install Windows");
return new Rectangle(data.rc.left, data.rc.top,
data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
// P/Invoke goo:
private const int ABM_GETTASKBARPOS = 5;
[System.Runtime.InteropServices.DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref APPBARDATA data);
private struct APPBARDATA {
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private struct RECT {
public int left, top, right, bottom;
}
SHAppBarMessage(ABM_GETTASKBARPOS)
See the SHAppBarMessage Function and the ABM_GETTASKBARPOS Message for more info and the pinvoke page for SHAppBarMessage has a VB.Net sample that shouldn't be too difficult to translate.
The SHAppBarMessage function will return you information about the taskbar if you pass in the ABM_GETTASKBARPOS message. It has an out parameter which is a pointer to APPBARDATA that contains the screen cooridinates of the task bar. You can use to work out where on screen it is.
It's probably best to use the available API: NotifyIcon.ShowBalloonTip:
void Form1_DoubleClick(object sender, EventArgs e)
{
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(20000, "Information", "This is the text",
ToolTipIcon.Info );
}
In Java, using JNA (adapted from other C# solutions above)
public static Rectangle getTaskbarPosition() throws Exception {
APPBARDATA data = new APPBARDATA();
data.cbSize = new WinDef.DWORD(data.size());
WinDef.UINT_PTR retval = Shell32.INSTANCE.SHAppBarMessage(ABM_GETTASKBARPOS, data);
if (retval == null) {
throw new Exception("Please re-install Windows");
}
return new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}

C# Implementing Auto-Scroll in a ListView while Drag & Dropping

How do I implement Auto-Scrolling (e.g. the ListView scrolls when you near the top or bottom) in a Winforms ListView? I've hunted around on google with little luck. I can't believe this doesn't work out of the box!
Thanks in advance
Dave
Thanks for the link (http://www.knowdotnet.com/articles/listviewdragdropscroll.html), I C#ised it
class ListViewBase:ListView
{
private Timer tmrLVScroll;
private System.ComponentModel.IContainer components;
private int mintScrollDirection;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
const int WM_VSCROLL = 277; // Vertical scroll
const int SB_LINEUP = 0; // Scrolls one line up
const int SB_LINEDOWN = 1; // Scrolls one line down
public ListViewBase()
{
InitializeComponent();
}
protected void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tmrLVScroll = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// tmrLVScroll
//
this.tmrLVScroll.Tick += new System.EventHandler(this.tmrLVScroll_Tick);
//
// ListViewBase
//
this.DragOver += new System.Windows.Forms.DragEventHandler(this.ListViewBase_DragOver);
this.ResumeLayout(false);
}
protected void ListViewBase_DragOver(object sender, DragEventArgs e)
{
Point position = PointToClient(new Point(e.X, e.Y));
if (position.Y <= (Font.Height / 2))
{
// getting close to top, ensure previous item is visible
mintScrollDirection = SB_LINEUP;
tmrLVScroll.Enabled = true;
}else if (position.Y >= ClientSize.Height - Font.Height / 2)
{
// getting close to bottom, ensure next item is visible
mintScrollDirection = SB_LINEDOWN;
tmrLVScroll.Enabled = true;
}else{
tmrLVScroll.Enabled = false;
}
}
private void tmrLVScroll_Tick(object sender, EventArgs e)
{
SendMessage(Handle, WM_VSCROLL, (IntPtr)mintScrollDirection, IntPtr.Zero);
}
}
Scrolling can be accomplished with the ListViewItem.EnsureVisible method.
You need to determine if the item you are currently dragging over is at the visible boundary of the list view and is not the first/last.
private static void RevealMoreItems(object sender, DragEventArgs e)
{
var listView = (ListView)sender;
var point = listView.PointToClient(new Point(e.X, e.Y));
var item = listView.GetItemAt(point.X, point.Y);
if (item == null)
return;
var index = item.Index;
var maxIndex = listView.Items.Count;
var scrollZoneHeight = listView.Font.Height;
if (index > 0 && point.Y < scrollZoneHeight)
{
listView.Items[index - 1].EnsureVisible();
}
else if (index < maxIndex && point.Y > listView.Height - scrollZoneHeight)
{
listView.Items[index + 1].EnsureVisible();
}
}
Then wire this method to the DragOver event.
targetListView.DragOver += RevealMoreItems;
targetListView.DragOver += (sender, e) =>
{
e.Effect = DragDropEffects.Move;
};
Have a look at ObjectListView. It does this stuff. You can read the code and use that if you don't want to use the ObjectListView itself.
Just a note for future googlers: to get this working on more complex controls (such as DataGridView), see this thread.

Categories