C# Winforms How to override scroll events? - c#

I have a TableLayoutPanel with autoscroll set to false, anchor to topleft. It's parent is a Panel with autoscroll set to true.
The TableLayoutPanel is being populated all the time with dynamic controls.
The scrolling itself works very good when there are few controls, just hundreds. However, when I get more and more controls the performance of scrolling downgrades and sometimes it freezes my PC.
Basically when I click on the vertical Scrollbar it fires an unlimited amount of ThumbPosition messages.
Part of my program
Using the Scroll event on the panel, not the tablelayoutpanel, I can catch some user events like LargeDecrement, ThumbTrack, ThumbPosition, etc but I can't see EndScroll being triggered which is the one I want. Or atleast I want to cancel scrolling on ThumbPosition.
private void FormaDinámicaGrisG_Scroll(object sender, ScrollEventArgs e)
{
MessageBox.Show(e.Type + "");
if (e.Type == ScrollEventType.ThumbPosition)
{
//I want to prevent scrolling here
}
else
{
// Here I want to let it scroll.
}
}
I try using the following code to catch messages when user is scrolling but nothing happens.
private const int WM_VSCROLL = 0x0115;
private const int WM_HSCROLL = 0x0114;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_VSCROLL)
{
MessageBox.Show("Vertical Scroll");
}
if (m.Msg == WM_HSCROLL)
{
MessageBox.Show("HorzScroll");
}
base.WndProc(ref m);
}
I believe the problem is that since it has so many dynamic controls, and the events triggers so rapidly in succession it hangs on drawing it.
So any idea to stop scrolling on certain events and let other events scroll?

Related

how to add buttons to a datagridview and be able to scroll up and down?

When I add buttons to a datagridview and then scroll up or down, buttons remain static and do not scroll together with the datagridview.
This is the code I have for the button click event:
private void button4_Click(object sender, EventArgs e)
{
button5.Location = new Point(dgvTurne.HitTest(posicionBoton.X, posicionBoton.Y).ColumnX, dgvTurne.HitTest(posicionBoton.X, posicionBoton.Y).RowY);
Size size = new Size(dgvTurne.SelectedCells[0].Size.Width, 0);
foreach (DataGridViewCell celda in dgvTurne.SelectedCells)
{
size.Height += celda.Size.Height;
}
button5.Size = size;
dgvTurne.Controls.Add(this.Controls["button5"]);
}
In the event above, when a button is clicked, I get the position of the selection in the datagridview by creating an instance of the Point class. Coordenates are taken using HitTest method from datagridview. Then the button is added to datagridview.
The problem is that after button is added, when I scroll up or down, button remains static and do not scroll together with datagridview.
I am trying this: Each button will contain information about appointments. For example: "Appointment with dentist"
I warn you now- getting this working perfectly will most likely ruin your brain. I know it did mine.
Creating a custom DataGridView with all the various events and missing events and everything else caught and handled is FAR too complicated to cover in this answer, but the starting point is here:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, int nBar);
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0115) // WM_VSCROLL
{
int scrollPos = MyDataGridView.GetScrollPos(this.Handle, 1); // SB_VERT
// get the vertical scroll position
this.button1.Y = desiredY + scrollPos;
// you'll need to calculate where all the buttons should be, then offset them by the scroll pos
}
base.WndProc(ref m);
}
That should cover scrolling with the wheel, grabbing the bar, pressing the arrow buttons next to the bar, using up, down, home and end on the keyboard and any other ways I might have forgotten.
Once that's done though you'll likely find another situation that's not properly covered, or it's not redrawing properly, or any number of other issues and it's back to the Win32 messages again..
Disclaimer:
Edits to the OP invalidated this answer. This will add a button for each row in a single column, but by the new image posted, this may not completely meet the needs of the original question. It won't extend a button over multiple rows like for the time slots imaged above.
See Octopoid's answer.
It sounds like you want a button associated with each row. Manually adding buttons and moving their location is far too much work. What you want to do is add a column of buttons to the dgv. After you've bound your DataSource, add a button column like so:
DataGridViewButtonColumn btnCol = new DataGridViewButtonColumn();
btnCol.HeaderText = "Foo";
btnCol.Name = "Bar";
btnCol.Text = "Click Me";
btnCol.UseColumnTextForButtonValue = true;
this.dataGridView1.Columns.Add(btnCol);
this.dataGridView1.CellClick += new DataGridViewCellEventHandler(col_Click);
Then, to handle the click event for each button, you handle the CellClick event.
public void col_Click(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == this.dataGridView1.Columns["Bar"].Index)
{
// Whatever you want the button to do
}
}

How to detect mouse click outside winform control in C#

I have a custom control (custom ComboBox). It`s work well, when I press "arrow button" it deployed, if I press it again it deployed too, but if it are deployed and I press anywhere in my form - it close but then, when I'm trying to open it - I must press "arrow button" two times. So I need to detect this moment, when I click outside of my combobox.
Code to open ComboBox(call in ButtonClick)
private void OpenComboBox()
{
if (drop_flag)
{
...
popup.Show(this);
}
else
{
drop_flag = true;
}
}
And Close event
private void popup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
drop_flag = false;
}
So, I want something like this
private ClickedOutsideControl()
{
dropflag = true;
}
Thats not that easy.With basic .net you always need some kind of control / references to intercept clicks by binding click events. For Example if you have an MDI Parent (Mainwindow you can intercept its clicks).
Another way (using windows api) is to hook inso system events See the following stackoverflow post Global mouse event handler.
But you should think twice if you really need this.
In the form, you can pre-process messages sent to controls by using a message filter. Here is my FAILED attempt to implement the desired functionality:
public partial class frmAutoCloseDropDown : Form, IMessageFilter
{
int _lastMsg;
public frmAutoCloseDropDown()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
// THIS ATTEMPT DOES NOT WORK!
public bool PreFilterMessage(ref Message m)
{
const int WM_LBUTTONDOWN = 0x0201;
if (m.Msg!= _lastMsg) {
_lastMsg = m.Msg;
}
if (m.Msg == WM_LBUTTONDOWN) {
// You would have to do this recursively if the combo-boxes were nested inside other controls.
foreach (ComboBox cbo in Controls.OfType<ComboBox>()) {
cbo.DroppedDown = false;
}
}
return false;
}
// Note: Dispose is created inside *.Designer.cs and you have to move it manually to *.cs
protected override void Dispose(bool disposing)
{
if (disposing) {
Application.RemoveMessageFilter(this);
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
}
Why does it not work? Probably because the new messages generated by cbo.DroppedDown = false that are sent to the form to close the drop-down are appended to the message queue and are only processed after the left mouse button click has been processed. This means that even when with PreFilterMessage our attempt to close the drop-down comes too late.
A possible solution would be to re-send WM_LBUTTONDOWN to the right ComboBox. You would have to interpret the parameters of the message to get the mouse coordinates and look to which combobox they are pointing to get its HWnd. My attempt to do this also shows that the behavior also depends on the drop-down style. It also has the undesired effect to close the drown-down you just opened. And what happens if you click in the drop-down itself to select an entry?
Probably you could do it, but the code tends to become very complicated. It's not worth the effort.

Auto vertical scrolling of RichTextBox on panel scrolling c#

I have a RichTextBox inside a panel in a WinForm. I want to hide the vertical scroll bar of RichTextBox and synchronize its scrolling with the vertical scroll bar of the container panel; whenever text overflows in the textbox the scroll bar of the panel should show up and whenever I scroll the scroll bar of the panel the textbox should scroll.
How to achieve this?
As I said in my comment, we have to deal with win32 message and use some hack. I've used all my knowledge about win32 message and control hack/customization in winforms to make this demo for you. It's not complete and of course won't be as perfect as the standard scrollbars of the RichTextBox. The deficiency is if you keep holding down the arrow keys, the scrollbar thumb won't be moved right, however if you press the arrow keys normally the scrollbar thumb will move the caret into view as the standard scrollbars do. You can try the code yourself to see it in action:
public class Form1 : Form {
[DllImport("user32")]
private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
public Form1(){
InitializeComponent();
//initialize some properties for your richTextBox1 (this should be added as a child of your panel1)
richTextBox1.ScrollBars = RichTextBoxScrollBars.Horizontal;
richTextBox1.BorderStyle = BorderStyle.None;
richTextBox1.Dock = DockStyle.Top;
richTextBox1.MinimumSize = new Size(panel1.Width, panel1.Height - 2);
//initialize some properties for your panel1
panel1.AutoScroll = true;
panel1.BorderStyle = BorderStyle.FixedSingle;
//If the size of panel1 is changed, we have to update the MinimumSize of richTextBox1.
panel1.SizeChanged += (s,e) => {
richTextBox1.MinimumSize = new Size(panel1.Width, panel1.Height - 2);
};
new NativeRichTextBox() { Parent = panel1 }.AssignHandle(richTextBox1.Handle);
hidden.Parent = panel1;
}
//hidden control of panel1 is used to scroll the thumb when the KeyUp of richTextBox1 is raised.
Control hidden = new Control();
//this is used to hook into the message loop of the richTextBox1
public class NativeRichTextBox : NativeWindow
{
public Panel Parent;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x20a)//WM_MOUSEWHEEL = 0x20a
{
if (Parent != null)
{
SendMessage(Parent.Handle, m.Msg, m.WParam, m.LParam);
return;
}
}
base.WndProc(ref m);
}
}
//ContentsResized event handler of your richTextBox1
private void richTextBox1_ContentsResized(object sender, ContentsResizedEventArgs e)
{
richTextBox1.Height = e.NewRectangle.Height + 5;
}
//KeyUp event handler of your richTextBox1
private void richTextBox1_KeyUp(object sender, KeyEventArgs e)
{
Point p = richTextBox1.GetPositionFromCharIndex(richTextBox1.SelectionStart);
hidden.Top = panel1.PointToClient(richTextBox1.PointToScreen(p)).Y;
hidden.Height = (int) richTextBox1.SelectionFont.Height;
panel1.ScrollControlIntoView(hidden);
}
}
Note: you have to register the event handlers ContentsResized and KeyUp for your richTextBox1 either using code or by designer.
in order to hide the scroll bars you can do
richTextBox1.ScrollBars = RichTextBoxScrollBars.None;
but the problem with it is that it makes the text warp. so you also need
richTextBox1.WordWrap = false;
once you've done that the rest isn't that easy too.
register on the event of the panel scrolling and change the scroll on the rich text box. the problem is is that you can't just change is scrolling offset of the richTextBox, so you can use the Select method to jump to the place you need. you can use the length of the line to know what's the size of the scrollBar need to be.
at the end, it's gonna be a hard time work. good luck

Preventing WindowState.Normal

I have an application that I am working on (for myself), and I want to prevent the Window from showing at all.
So far, what I have got is WindowState.Minimized so that the Form's initial state is Minimized. And when I click the app's icon in the Taskbar, I want it to remain minimized - not just Hide() it when it has shown. So I thought that something like this might work:
protected override void OnGotFocus(EventArgs e)
{
this.WindowState = WindowState.Minimized;
}
But I was wrong. So what I am thinking is that I need something that happends before OnGotFocus. The reason that just hiding it when it gets focused is not enough, is because you can see, very faintly, that it does actually show itself when you click the icon in the taskbar before this.WindowState = WindowState.Minimize gets called.
My only requirement is that the application must not be shown when it's icon is clicked on the Taskbar. I will need to show the window programatically, at some point, though.
How can I make sure that clicking its icon never shows the window?
This seemed to work for me with no "hint" of it coming and going:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0112) //WM_SYSCOMMAND
{
if ((m.WParam.ToInt32() & 0xFFF0) == 0xF030 ||
(m.WParam.ToInt32() & 0xFFF0) == 0xF120)
m.WParam = new IntPtr(0xF020);
}
base.WndProc(ref m);
}
Just override the WndProc and this will catch both SC_MAXIMIZE (0xF030) and SC_RESTORE (0x0F120). SC_MINIMIZE is 0xF020.
As Hans has suggested, I have edited my post to mask out the low-order bits used by the system per this MSDN section: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646360(v=vs.85).aspx
Hi you can use the form_resize event to trigger windowstate to minimized
private void Form1_Resize ( object sender , EventArgs e )
{
if ( WindowState == FormWindowState.Maximized )
{
// minimize it here
}
}

RichTextBox & Disabling Mouse Scrolling

I want to use the mouse middle button to clear a RichTextBox, but it also activates a mouse scrolling functionality similar to what you find in web broswers. When the vertical scrollbar is visible (there's enough data) and you press the middle button in the mouse a scrolling cursor appears and you can scroll up or down by moving the cursor up or down. How do I disable the mouse scrolling?
Mouse scrolling seems to be a Windows (or mouse driver) feature, so how can I stop the MouseDown event (if the mouse middle button is pressed) from reaching whatever routine is responsible for the mouse scrolling?
Check for 0x207 and 0x208, middle button down and up:
using System;
using System.Windows.Forms;
class MyRtb : RichTextBox {
protected override void WndProc(ref Message m) {
if (m.Msg == 0x207) this.Clear();
else if (m.Msg != 0x208) base.WndProc(ref m);
}
}
No scrolling RichTextBox, just Inherit from RichTextBox and you done.
public class NoScrollRichTextBox : RichTextBox
{
const int WM_MOUSEWHEEL = 0x020A;
protected override void WndProc(ref Message m)
{
// This will completely ignore the mouse wheel, which will disable zooming as well
if (m.Msg != WM_MOUSEWHEEL)
base.WndProc(ref m);
}
}

Categories

Resources