Discussion of application development using iX Developer, including but not limited to getting started, using the functions tab, properties, objects and installation.
AMitchneck
Posts: 137 Joined: Mon Jun 11, 2012 2:10 pm
Post
by AMitchneck » Wed Dec 11, 2013 3:56 pm
I'm trying to make a listbox control scroll when a user clicks and drags anywhere on the control. Everything works (I used buttons to simulate mouse movement) except for some reason the listbox control never raises the mousedown, mousemove, or mouseup events. Is there something else I can use to capture mouse events or how can I get the listbox to fire the events?
Listbox control code:
Code: Select all
ListDrag listDrag = new ListDrag();
void Screen_Opened(System.Object sender, System.EventArgs e)
{
this.m_List.MouseMove += new System.Windows.Forms.MouseEventHandler(List_MouseMove);
}
void List_MouseDown(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
listDrag.BeginDrag(this.m_List, e.Y);
}
void List_MouseMove(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
if (listDrag.DragActive)
listDrag.DoDrag(this.m_List, e.Y);
}
void List_MouseUp(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
if (listDrag.DragActive)
{
listDrag.DoDrag(this.m_List, e.Y);
listDrag.EndDrag();
}
}
private class ListDrag
{
#region Externals
[DllImport("coredll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SIF_RANGE = 0x1;
private const int SIF_PAGE = 0x2;
private const int SIF_POS = 0x4;
private const int SIF_DISABLENOSCROLL = 0x8;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS;
private struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private SCROLLINFO GetInfo(IntPtr Handle)
{
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (uint)SIF_ALL;
if (GetScrollInfo(Handle, SB_VERT, ref si))
{
if (si.nPage == 0)
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
}
else
{
int size = (int)si.nPage - 1;
if (size < (si.nMax - si.nMin))
si.nMax -= size;
else
si.nMax = si.nMin;
}
}
else
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
si.nPage = 0;
}
return si;
}
#endregion
private bool Drag;
private int dragY;
private int dragPos;
public ListDrag()
{
Drag = false;
}
public bool DragActive
{
get { return Drag; }
}
public bool BeginDrag(Neo.ApplicationFramework.Controls.Controls.ListBox listBox, int Y)
{
if (Drag) return false;
dragY = Y;
dragPos = GetInfo(listBox.Handle).nPos;
Drag = true;
return true;
}
public bool DoDrag(Neo.ApplicationFramework.Controls.Controls.ListBox listBox, int Y)
{
if (!Drag) return false;
SCROLLINFO si = GetInfo(listBox.Handle);
if (si.nPage == 0) return true;
int startPos = GetPos(listBox, (int)si.nPage, dragY);
int endPos = GetPos(listBox, (int)si.nPage, Y);
int pos = dragPos + startPos - endPos;
if (pos < si.nMin)
pos = si.nMin;
else if (pos > si.nMax)
pos = si.nMax;
listBox.TopIndex = pos;
return true;
}
public void EndDrag()
{
Drag = false;
}
private int GetPos(Neo.ApplicationFramework.Controls.Controls.ListBox listBox, int nPage, int Y)
{
int pos;
pos = Y - listBox.ClientRectangle.Y;
pos *= nPage;
pos /= listBox.ClientRectangle.Height;
if (Y < listBox.ClientRectangle.Y) pos--;
return pos;
}
}
Adam M.
Controls Engineer
FlexEnergy
AMitchneck
Posts: 137 Joined: Mon Jun 11, 2012 2:10 pm
Post
by AMitchneck » Wed Dec 18, 2013 3:21 pm
I discovered a solution to my problem...
It seems .NETCF does not directly support mouse events for the ListBox control. The only way to get the event is to capture the windows message directly.
Note: This code sample hooks into the windows message handler and must be unhooked before the window closes. The sample code assumes the name of the listbox in iX Dev. is "listbox".
Code: Select all
MsgProc msgProc;
void Screen_Opened(System.Object sender, System.EventArgs e)
{
msgProc = new MsgProc(this.m_listbox); // hook into windows message handler
}
void Screen_Closing(System.Object sender, System.ComponentModel.CancelEventArgs e)
{
// unhook from windows message handler
msgProc.Dispose();
}
Code: Select all
using System.Runtime.InteropServices;
private class MsgProc : IDisposable
{
#region Externals
[DllImport("coredll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
[DllImport("coredll.dll")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("coredll.dll")]
private static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, uint wParam, int lParam);
private const int GWL_WNDPROC = -4;
private delegate int WndProc(IntPtr hwnd, uint msg, uint wParam, int lParam);
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SIF_RANGE = 0x1;
private const int SIF_PAGE = 0x2;
private const int SIF_POS = 0x4;
private const int SIF_DISABLENOSCROLL = 0x8;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS;
private struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private SCROLLINFO GetInfo(IntPtr Handle)
{
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (uint)SIF_ALL;
if (GetScrollInfo(Handle, SB_VERT, ref si))
{
if (si.nPage == 0)
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
}
else
{
int size = (int)si.nPage - 1;
if (size < (si.nMax - si.nMin))
si.nMax -= size;
else
si.nMax = si.nMin;
}
}
else
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
si.nPage = 0;
}
return si;
}
#endregion
private IntPtr ListDefProc = IntPtr.Zero;
private Neo.ApplicationFramework.Controls.Controls.ListBox list;
private WndProc callback = null;
private bool dragActive;
private int dragY;
private int dragPos;
public MsgProc(Neo.ApplicationFramework.Controls.Controls.ListBox listBox)
{
dragActive = false;
list = listBox;
callback = new WndProc(ListWinProc);
ListDefProc = SetWindowLong(list.Handle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(callback));
}
~MsgProc()
{
Dispose();
}
public void Dispose()
{
if (ListDefProc.ToInt32() != 0)
{
SetWindowLong(list.Handle, GWL_WNDPROC, ListDefProc);
ListDefProc = IntPtr.Zero;
dragActive = false;
}
}
private int ListWinProc(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
int result = CallWindowProc(ListDefProc, hwnd, msg, wParam, lParam);
try
{
if (hwnd == list.Handle)
{
short Y = (short)(lParam >> 16);
switch (msg)
{
case 0x0201: // WM_LBUTTONDOWN
if (!dragActive)
{
dragY = (int)Y;
dragPos = list.TopIndex;
dragActive = true;
}
break;
case 0x0202: // WM_LBUTTONUP
if (dragActive)
{
DoDrag((int)Y);
dragActive = false;
}
break;
case 0x0200: // WM_MOUSEMOVE
if (dragActive)
{
DoDrag((int)Y);
}
break;
}
}
}
catch { }
return result;
}
private void DoDrag(int Y)
{
SCROLLINFO si = GetInfo(list.Handle);
if (si.nPage == 0) return;
int pos = dragPos + GetPos((int)si.nPage, dragY) - GetPos((int)si.nPage, Y);
if (pos < si.nMin)
pos = si.nMin;
else if (pos > si.nMax)
pos = si.nMax;
list.TopIndex = pos;
}
private int GetPos(int nPage, int Y)
{
int delta = Y - list.ClientRectangle.Y;
int pos = (delta * nPage) / list.ClientRectangle.Height;
if (delta < 0) pos--;
return pos;
}
}
Adam M.
Controls Engineer
FlexEnergy
robkwan
Posts: 14 Joined: Mon Aug 19, 2013 1:32 pm
Post
by robkwan » Tue Aug 12, 2014 8:28 am
Nice trick. But my project is unable to run/simulate on the iX IDE because it throws exception during project opens, due to the WinCE stuffs. The project has multiple screens. I can ignore the screen with the WinCE specific code but want to test other screen from the IDE.
Is there a CF version directive in iX that can be used for conditional compilation?
AMitchneck
Posts: 137 Joined: Mon Jun 11, 2012 2:10 pm
Post
by AMitchneck » Tue Aug 12, 2014 8:46 am
Sadly, I don't know of any flag in iX dev. to indicate debug/run version that can be used to change dll references. I'd also be interested to know if one exists as some script modules in my code reference coredll.dll thus disabling it from running anywhere but on the panel itself.
At least for testing purposes, to enable the listbox scrolling code on a non-WinCE computer you can change the [DllImport("coredll.dll")] references to [DllImport("user32.dll")].
Adam M.
Controls Engineer
FlexEnergy
AMitchneck
Posts: 137 Joined: Mon Jun 11, 2012 2:10 pm
Post
by AMitchneck » Tue Aug 12, 2014 9:03 am
Just an FYI - I found my listbox scrolling code stopped working when the panel firmware was updated. My solution was to disable the listbox built-in mouse control and make my code solely control mouse events.
Code: Select all
using System.Runtime.InteropServices;
private class MsgProc : IDisposable
{
#region Externals
[DllImport("coredll.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
[DllImport("coredll.dll")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("coredll.dll")]
private static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hwnd, uint msg, uint wParam, int lParam);
private const int GWL_WNDPROC = -4;
private delegate int WndProc(IntPtr hwnd, uint msg, uint wParam, int lParam);
private const int SB_HORZ = 0;
private const int SB_VERT = 1;
private const int SIF_RANGE = 0x1;
private const int SIF_PAGE = 0x2;
private const int SIF_POS = 0x4;
private const int SIF_DISABLENOSCROLL = 0x8;
private const int SIF_TRACKPOS = 0x10;
private const int SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS;
private struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private SCROLLINFO GetInfo()
{
SCROLLINFO si = new SCROLLINFO();
si.cbSize = (uint)Marshal.SizeOf(si);
si.fMask = (uint)SIF_ALL;
if (GetScrollInfo(list.Handle, SB_VERT, ref si))
{
if (si.nPage == 0)
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
}
else
{
int size = (int)si.nPage - 1;
if (size < (si.nMax - si.nMin))
si.nMax -= size;
else
si.nMax = si.nMin;
}
}
else
{
si.nMin = 0;
si.nMax = 0;
si.nPos = 0;
si.nPage = 0;
}
return si;
}
#endregion
private IntPtr ListDefProc = IntPtr.Zero;
private Neo.ApplicationFramework.Controls.Controls.ListBox list;
private WndProc callback = null;
private int dragY;
private int dragPos;
public MsgProc(Neo.ApplicationFramework.Controls.Controls.ListBox listBox)
{
list = listBox;
list.Capture = false;
callback = new WndProc(ListWinProc);
ListDefProc = SetWindowLong(list.Handle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(callback));
}
public void Dispose()
{
if (ListDefProc.ToInt32() != 0)
{
SetWindowLong(list.Handle, GWL_WNDPROC, ListDefProc);
ListDefProc = IntPtr.Zero;
list.Capture = false;
}
}
private int ListWinProc(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
try
{
if (hwnd == list.Handle)
{
short Y = (short)(lParam >> 16);
switch (msg)
{
case 0x0201: // WM_LBUTTONDOWN
case 0x0203: // WM_LBUTTONDBLCLK
// begin drag
dragY = (int)Y;
dragPos = GetInfo().nPos;
list.Capture = true;
// comment line below if you don't want row to be selected in listbox
list.SelectedIndex = dragPos;
return 0;
case 0x0202: // WM_LBUTTONUP
if (list.Capture)
{
// end drag
DoDrag((int)Y);
list.Capture = false;
}
return 0;
case 0x0200: // WM_MOUSEMOVE
if (list.Capture)
{
// drag
DoDrag((int)Y);
}
return 0;
}
}
}
catch { }
return CallWindowProc(ListDefProc, hwnd, msg, wParam, lParam);
}
private void DoDrag(int Y)
{
SCROLLINFO si = GetInfo();
if (si.nPage == 0) return;
int pos = dragPos + GetPos((int)si.nPage, dragY) - GetPos((int)si.nPage, Y);
if (pos < si.nMin)
pos = si.nMin;
else if (pos > si.nMax)
pos = si.nMax;
list.TopIndex = pos;
}
private int GetPos(int nPage, int Y)
{
int delta = Y - list.ClientRectangle.Y;
int pos = (delta * nPage) / list.ClientRectangle.Height;
if (delta < 0) pos--;
return pos;
}
}
Adam M.
Controls Engineer
FlexEnergy