Any way to optimize populating tableLayoutPanel? - c#

I have a tableLayoutPanel that I use with a Windows Form. The control is populated from a datatable which holds sql server data. I've already confirmed that the select statement is not the issue.
The datatable is frequently updated, so the tableLayoutPanel is also frequently updated. It's essentially working well, but it gets to a point that it becomes a little slower and that flickering is more noticeable.
Everytime I need to refresh the control, this code is executed:
public void FillTlp()
{
tableLayoutPanel1.Controls.Clear();
tableLayoutPanel1.ColumnStyles.Clear();
foreach (DataRow r in DT.Rows)
{
UcColor button = new UcColor(r);
tableLayoutPanel1.Controls.Add(button);//, colNumNew, rowNum);
}
this.Controls.Add(tableLayoutPanel1);
}
Since the will always have 8 rows, I execute the following code in the Form constructor only once, but I don't see much benefit:
public FormDoctorMonitor()
{
tableLayoutPanel1.RowStyles.Clear();
tableLayoutPanel1.RowCount = 8;
FillTlp();
}
How else can I optimize populating the tableLayoutPanel?
Thanks.

When i have some display freezing, i use those extension methode of control :
public static class ExtensionOfControl
{
private const int WM_SETREDRAW = 11;
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParmam);
public static void SuspendDrawing(this Control parent)
{
SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
}
public static void ResumeDrawing(this Control parent)
{
SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
parent.Refresh();
}
public static void RunWithDrawingSuspended(this Control ctrl, Action code)
{
ctrl.SuspendDrawing();
try
{
code();
}
catch (Exception)
{
throw;
}
finally
{
ctrl.ResumeDrawing();
}
}
}
After try this :
this.RunWithDrawingSuspended(() =>
{
tableLayoutPanel1.Controls.Clear();
tableLayoutPanel1.ColumnStyles.Clear();
foreach (DataRow r in DT.Rows)
{
UcColor button = new UcColor(r);
tableLayoutPanel1.Controls.Add(button);//, colNumNew, rowNum);
}
this.Controls.Add(tableLayoutPanel1);
});
Maybe you can replace "this" by your tablelayoutpanel if already exist on "this"

Related

How can i update in live real time listBox adding items from another class?

I have this code in a new class:
private static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
bool specialCapturing = false;
if (hWnd == IntPtr.Zero) return false;
if (!IsWindowVisible(hWnd)) return true;
if (!countMinimizedWindows)
{
if (IsIconic(hWnd)) return true;
}
else if (IsIconic(hWnd) && useSpecialCapturing) specialCapturing = true;
if (GetWindowText(hWnd) == PROGRAMMANAGER) return true;
windowSnaps.Add(new WindowSnap(hWnd, specialCapturing));
doStuff(new WindowSnap(hWnd, specialCapturing));
return true;
}
public static void doStuff(object objtoadd)
{
foreach (Form frm in Application.OpenForms)
{
if (frm.GetType() == typeof(Form1))
{
Form1 frmTemp = (Form1)frm;
frmTemp.addItemToListBox(objtoadd);
}
}
}
When running the program it's getting to the line:
doStuff(new WindowSnap(hWnd, specialCapturing));
But it's never pass the line:
if (frm.GetType() == typeof(Form1))
And in form1 i did:
public void addItemToListBox(object item)
{
listBoxSnap.Items.Add(item);
}
But it's never called the addItemToListBox since it didn't pass the line in the class.
My main goal is to update the listBox in form1(listBoxSnap) in libe real time once in the new class it's adding one item:
frmTemp.addItemToListBox(objtoadd);
Then show/display in the listBoxSnap the item then the next item and so on.
Untill now i did:
this.listBoxSnap.Items.AddRange(WindowSnap.GetAllWindows(true, true).ToArray());
But this took time to since it have to wait untill all items added before showing them in the listBoxSnap.
WindowSnap is the new class and GetAllWindows is:
public static WindowSnapCollection GetAllWindows(bool minimized, bool specialCapturring)
{
windowSnaps = new WindowSnapCollection();
countMinimizedWindows = minimized;//set minimized flag capture
useSpecialCapturing = specialCapturring;//set specialcapturing flag
EnumWindowsCallbackHandler callback = new EnumWindowsCallbackHandler(EnumWindowsCallback);
EnumWindows(callback, IntPtr.Zero);
return new WindowSnapCollection(windowSnaps.ToArray(), true);
}
In this method it's calling the EnumWindowsCallback where i want to update the listBoxSnap.
Maybe i need somehow to update the listBoxSnap in the public static WindowSnapCollection GetAllWindows method.
Way too much code for me to gork. But to answer the question.
Since you said real time I have to assume that the callback and DoStuff are running on a non-UI thread. To do anything with a Control you must be on the UI thread. You can use Control.Invoke to do this.
public static void doStuff(object objtoadd)
{
foreach (Form1 frm in Application.OpenForms.OfType<Form1>())
{
frm.Invoke(() => frm.addItemToListBox(objtoadd));
}
}
Note that OfType<Form1>() is defined in System.Linq.

How to hide scroll bars without disabling them

Is there a way to scroll on a RichTextBox without the scroll bar visible? I searched Google, but only came up with the ScrollBars Property.
I am using Microsoft Visual C# Express, winforms.
EDIT:
Something that would fix my problem would be a void with a few parameters such as a RTB, which direction to scroll, and how far to go.
Some steps to do:
Put your RTB in a Panel (Dock: none)
Call panel.width -= 20; within your code.
Now we need a mouse wheel scrolling without focus, use my code below:
Here is a little workaround:
public Main()
{
InitializeComponent();
//Works for panels, richtextboxes, 3rd party etc..
Application.AddMessageFilter(new ScrollableControls(richTextBox1));
}
ScrollableControls.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//Let controls scroll without Focus();
namespace YOURNAMESPACE
{
internal struct ScrollableControls : IMessageFilter
{
private const int WmMousewheel = 0x020A;
private readonly Control[] _controls;
public ScrollableControls(params Control[] controls)
{
_controls = controls;
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg != WmMousewheel) return false;
foreach (var item in _controls)
{
ScrollControl(item, ref m);
}
return false;
}
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
private static void ScrollControl(Control control, ref Message m)
{
if (control.RectangleToScreen(control.ClientRectangle).Contains(Cursor.Position) && control.Visible)
{
SendMessage(control.Handle, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
}
}
}
}
Keep in mind that 3rd party controls often use nested container or similiar like radScrollablePanel1.PanelContainer, so you have to call them instead.

Mouse wheel event to work with hovered control

In my C# 3.5 Windows Forms application, I have a few SplitContainers. There is a list control inside each (dock fill). When the focus is on one of these controls and I move mouse wheel, the list, which is now focused, is scrolled.
My task is to scroll the list, which is currently hovered by mouse, not the one which is selected. Is it possible in Windows Forms? If not, is it possible with PInvoke?
It looks like you can use the IMessageFilter and PInvoke to handle this. An example in VB can be found at Redirect Mouse Wheel Events to Unfocused Windows Forms Controls. You should be able to easily convert this to C#.
Points of Interest
This class uses the following techniques for the given task:
Listen to the control's MouseEnter and MouseLeave events to determine when the mouse pointer is over the control.
Implement IMessageFilter to catch WM_MOUSEWHEEL messages in the application.
PInvoke the Windows API call SendMessage redirecting the WM_MOUSEWHEEL message to the control's handle.
The IMessageFilter object is implemented as a singleton of the MouseWheelRedirector class and accessed by the shared members Attach, Detach, and Active.
Using a VB.NET to C# converter, this is what you end up with:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MouseWheelRedirector : IMessageFilter
{
private static MouseWheelRedirector instance = null;
private static bool _active = false;
public static bool Active
{
get { return _active; }
set
{
if (_active != value)
{
_active = value;
if (_active)
{
if (instance == null)
{
instance = new MouseWheelRedirector();
}
Application.AddMessageFilter(instance);
}
else
{
if (instance != null)
{
Application.RemoveMessageFilter(instance);
}
}
}
}
}
public static void Attach(Control control)
{
if (!_active)
Active = true;
control.MouseEnter += instance.ControlMouseEnter;
control.MouseLeave += instance.ControlMouseLeaveOrDisposed;
control.Disposed += instance.ControlMouseLeaveOrDisposed;
}
public static void Detach(Control control)
{
if (instance == null)
return;
control.MouseEnter -= instance.ControlMouseEnter;
control.MouseLeave -= instance.ControlMouseLeaveOrDisposed;
control.Disposed -= instance.ControlMouseLeaveOrDisposed;
if (object.ReferenceEquals(instance.currentControl, control))
instance.currentControl = null;
}
private MouseWheelRedirector()
{
}
private Control currentControl;
private void ControlMouseEnter(object sender, System.EventArgs e)
{
var control = (Control)sender;
if (!control.Focused)
{
currentControl = control;
}
else
{
currentControl = null;
}
}
private void ControlMouseLeaveOrDisposed(object sender, System.EventArgs e)
{
if (object.ReferenceEquals(currentControl, sender))
{
currentControl = null;
}
}
private const int WM_MOUSEWHEEL = 0x20a;
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (currentControl != null && m.Msg == WM_MOUSEWHEEL)
{
SendMessage(currentControl.Handle, m.Msg, m.WParam, m.LParam);
return true;
}
else
{
return false;
}
}
[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr SendMessage(
IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}
I had similar question and found this thread... so posting my belated answer for others who might find this thread. In my case, I just want the mouse wheel events to go to whatever control is under the cursor... just like right-click does (it would be bewildering if right-click went to the focus control rather than the control under the cursor... I argue the same is true for the mouse wheel, except we've gotten used to it).
Anyway, the answer is super easy. Just add a PreFilterMessage to your application and have it redirect mouse wheel events to the control under the mouse:
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL: // 0x020A
case WM_MOUSEHWHEEL: // 0x020E
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
return false; // already headed for the right control
else
{
// redirect the message to the control under the mouse
SendMessage(hControlUnderMouse, m.Msg, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}
This is Brian Kennedy's answer completed with Hank Schultz comment:
First you should make a class implements IMessageFilter:
public class MessageFilter : IMessageFilter
{
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_MOUSEHWHEEL = 0x020E;
[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(Point p);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public bool PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_MOUSEWHEEL:
case WM_MOUSEHWHEEL:
IntPtr hControlUnderMouse = WindowFromPoint(new Point((int)m.LParam));
if (hControlUnderMouse == m.HWnd)
{
//Do nothing because it's already headed for the right control
return false;
}
else
{
//Send the scroll message to the control under the mouse
uint u = Convert.ToUInt32(m.Msg);
SendMessage(hControlUnderMouse, u, m.WParam, m.LParam);
return true;
}
default:
return false;
}
}
}
Example usage:
public partial class MyForm : Form
{
MessageFilter mf = null;
public MyForm
{
Load += MyForm_Load;
FormClosing += MyForm_FormClosing;
}
private void MyForm_Load(object sender, EventArgs e)
{
mf= new MessageFilter();
Application.AddMessageFilter(mf);
}
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Application.RemoveMessageFilter(mf);
}
}
Use Control.MouseEnter Event to set focus to to the control. E.g. using ActiveControl Property

clipboard Listener event is being called twice

i want to save the changes in my clipboard. so i registered my application to get all the changes that happens to the Clipboard.
Using
[DllImport("User32.dll")]
protected static extern bool AddClipboardFormatListener(int hwnd);
and then
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_CLIPBOARDUPDATE:
OnClipboardChanged();
break;
...
}
}
private void OnClipboardChanged()
{
if (Clipboard.ContainsText())
{
MessageBox.Show(Clipboard.GetText().ToString());
}
}
The Problem is:
When copying text from an application like visual studio or firefox, the OnClipboardChanged() function will be called twice or 3 times sometimes.
I think that those application will write the Data to the clipboard with different formats, this is why the function is called more than once.
But how would i prevent saving the same data because OnClipboardChanged() is being called more than once ?
Because they're opening/closing the clipboard multiple times. I've seen such madness before. Excel used to perform 24 separate operations when copying a chart.
Instead of this (pseudocode):
openClipboard
for each format {
place data on clipboard(format)
}
closeClipboard
they're probably doing this:
for each format {
openClipboard
place data on clipboard(format)
closeClipboard
}
Update: The usual mitigation strategy is to avoid reacting to every update, and react to the LAST update after a reasonable "settle time" has elapsed with no further clipboard notifications. 500ms will usually be more than adequate.
Prevent multiple calls to the clipboard
private int _i = 0;
private int i
{
get
{
async void setI()
{
await Task.Run(() =>
{
Thread.Sleep(20);
i = 0;
}
);
}
setI();
return _i;
}
set
{
_i = value;
}
}
private IntPtr HwndHandler(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == WM_CLIPBOARDUPDATE)
{
if(i<1)
{
this.ClipboardUpdate?.Invoke(this, new EventArgs());
i++;
}
}
handled = false;
return IntPtr.Zero;
}

Implementing a drag over window option

Note:
Using Windows Forms
preferably C# .NET
Question:
Best method for implementing a drag-over-window tool, similar (or identical) to that featured in process explorer, to obtain the process ID corresponding to the selected Window
I think the easiest way is to put a control on your form that acts as a starting point; you press a mouse button there, and then you move it over the screen while the button is pressed, and pick up the process ID of whatever you are pointing at. I my example I have used a panel (called _aim).
First we set up the mouse events:
private void Panel_MouseDown(object sender, MouseEventArgs e)
{
// make all mouse events being raised in the _aim panel
// regardless of whether the mouse is within the control's
// bounds or not
_aim.Capture = true;
}
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
if (_aim.Capture)
{
// get the process id only if we have mouse capture
uint processId = GetProcessIdFromPoint(
_aim.PointToScreen(e.Location)).ToString();
// do something with processId (store it for remembering the
// last processId seen, to be used as MouseUp for instance)
}
}
private void Panel_MouseUp(object sender, MouseEventArgs e)
{
if (_aim.Capture)
{
// release capture if we have it
_aim.Capture = false;
// perhaps do something more (fetch info about last seen
// process id, if we stored it during MouseMove, for instance)
}
}
The GetProcessIdFromPoint method looks like this:
private uint GetProcessIdFromPoint(Point point)
{
uint procId;
WinApi.GetWindowThreadProcessId(WinApi.WindowFromPoint(point), out procId);
return procId;
}
And finally the windows API things (from pinvoke.net):
public static class WinApi
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
public static implicit operator System.Drawing.Point(POINT p)
{
return new System.Drawing.Point(p.X, p.Y);
}
public static implicit operator POINT(System.Drawing.Point p)
{
return new POINT(p.X, p.Y);
}
}
[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(POINT Point);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}

Categories