i create a subclass datagridview to override the mousewheel event to catch mouse scroll then send key UP or DOWN. i create a datatable to be bind as datasource for mydatagridview by button click
private void button1_Click(object sender, EventArgs e)
{
DataTable myDataTable = new DataTable();
int NUM_ROWS = 150;
int NUM_COLS_TO_CREATE = 10;
for (int i = 0; i < NUM_COLS_TO_CREATE; i++)
{
myDataTable.Columns.Add("x" + i, typeof(string));
}
for (int i = 0; i < NUM_ROWS; i++)
{
var theRow = myDataTable.NewRow();
for (int j = 0; j < NUM_COLS_TO_CREATE; j++)
{
theRow[j] = "whatever";
}
//add the row *after* populating it
myDataTable.Rows.Add(theRow);
}
MyDataGridView1.DataSource = myDataTable;
}
the code that override the mousewheel event as this
public partial class MyDataGridView : DataGridView
{
protected override void OnMouseWheel(MouseEventArgs e)
{
if (e.Delta < 0)
SendKeys.Send("{DOWN}");
else
SendKeys.Send("{UP}");
}
}
Its working fine if we use mouse wheel to scroll each item in a SLOW way, but if you scroll too FAST using the mouse wheel, somewhat the datagridview becomes lagging.
As example from row 1 to 5, it will jump the row from 1 to 3,then 3 to 5 something like that, here come another weird issue. i use "Navicat" a lot in my daily basis..
so if i open both my application and Navicat. the mouse wheel scrolling now become very smooth on my application even if i scroll too fast. but then if i close Navicat then scrolling become lagging again. what was causing this? i am very sorry if i cant explained it well, all i want is just want to makes the scrolling each item smooth. Any suggestion?
as #Bozhidar mentioned that i should better handling the MouseWheel event instead or overriding it. so i've come up with the solution just in case anyone need it too.
in Form_Load add
MyDataGridView1.MouseWheel += new MouseEventHandler(MyDataGridView1_MouseWheel);
then place this anywhere inside your class
private void MyDataGridView1_MouseWheel(object sender, MouseEventArgs e)
{
HandledMouseEventArgs hme = (HandledMouseEventArgs)e;
hme.Handled = true;
int rowIndex = MyDataGridView1.CurrentCell.RowIndex;
int cellIndex = MyDataGridView1.CurrentCell.ColumnIndex;
MyDataGridView1.CurrentCell = MyDataGridView1.Rows[e.Delta < 0 ? Math.Min(rowIndex + 1, MyDataGridView1.RowCount - 1) : Math.Max(rowIndex - 1, 0)].Cells[cellIndex];
}
The SendKeys method is not as reliable when it comes to precise timing - see the official documentation. Try setting the "SendKeys" app setting to "SendInput" in order to force the new behavior.
But you'd be better off handling the MouseWheel event instead of overriding it. You need to hook it by hand - not sure why it isn't present into the Property Window. Given your DataGridView is named dgv:
private void Form1_Load(object sender, EventArgs e)
{
dgv.MouseWheel += Dgv_MouseWheel;
}
Next, have you considered FirstDisplayedScrollingRowIndex? Just set it as appropriate in the event, and set the Handled flag, like this:
private void dgv_MouseWheel(object sender, MouseEventArgs e)
{
dgv.FirstDisplayedScrollingRowIndex += 3;
var he = (HandledMouseEventArgs);
he.Handled = true;
}
Here's an approach that preserves the default scrolling behavior of scrolling the viewport, but not changing the row selection.
It also performs much faster than the built-in mouse wheel handler when millions of rows are being handled in a virtual grid:
private void Form1_Load(object sender, EventArgs e)
{
DataGridView1.MouseWheel += DataGridView1_MouseWheel;
}
private void DataGridView1_MouseWheel(object sender, MouseEventArgs e)
{
var hme = e as HandledMouseEventArgs;
hme.Handled = true;
int displayedRowIndex = DataGridView1.FirstDisplayedScrollingRowIndex;
// each "detente" scroll appears to create a delta of 120
// dividing delta by 120 to get the number of rows scrolled
// taking the negative of the delta so that it can be added to the displayedRowIndex intuitively as negative is down, positive is up
var rowDelta = -(e.Delta / 120);
var newDisplayedRowIndex = e.Delta < 0 ? Math.Min(displayedRowIndex + rowDelta, DataGridView1.RowCount - 1) : Math.Max(displayedRowIndex + rowDelta, 0);
DataGridView1.FirstDisplayedScrollingRowIndex = newDisplayedRowIndex;
}
Related
I'm having problem with binding Hscroll to datagridview. I need to have bigger scoll, than default DGV one. So i need to bind custom scroll to dgv or increase height of default one. I'm using WinForms.
I have tried the following code, but it doesnt fit my needs, the scroll stopped in middle of my dgv, manipulating values had no effect on it.
private void dataGridView1_Scroll(object sender, ScrollEventArgs e)
{
int totalwidth = dataGridView1.RowHeadersWidth;
for (int i = 0; i < dataGridView1.Columns.Count; i++)
{
totalwidth += dataGridView1.Columns[i].Width ;
}
hScrollBar1.LargeChange = dataGridView1.Width;
hScrollBar1.SmallChange = dataGridView1.Columns[gsKodTowaruDataGridViewTextBoxColumn.Index].Width;
if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
{
hScrollBar1.Value = e.NewValue;
}
}
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
dataGridView1.HorizontalScrollingOffset = e.NewValue;
}
Thanks for answers.
You have to bind these values to react to change right?
You are using a maxWidth value without using it.
Also, you should react to Resize Event on the DataGridView to make sure the HScroll values do change.
When playing with clickevents in visual studio i came accross this error:
private void pictureBox1_Click(object sender, EventArgs e)
{
Testcounter = 0;
pictureBox1.MouseClick += myMouseClickEventFunction;
}
private void myMouseClickEventFunction(object sender, MouseEventArgs e)
{
int x = colors.GetUpperBound(0) + 1;
int y = colors.GetUpperBound(1) + 1;
Testcounter++;
var point = new Point(e.X - pictureBox1.Width/2, e.Y - pictureBox1.Height/2);
for (int i = 0; i < x; i++)
{
for (int u = 0; u < y; u++)
{
if (cirkles[i, u].Contains(point))
{
changeIndex(i, u);
}
}
}
this.Refresh();
}
The first time i click my picturebox the counters value is 1, the second time the value is 2, 3th time 3,... Does anyone has any idea why this happends? thnx
pic1
pic2
Because by executing this
pictureBox1.MouseClick += myMouseClickEventFunction;
You're adding the handler one more time with each click. Which should mean, that if you click it once, you add it once and it executes once. But with the second click, you add it one more time, so this time it will execute two times and that's why your counter is increasing to 2. What you need is to move your click handler somewhere else and register it only one time, which means that the best place to move it should be in the initialization of the form. (In public MainForm(){} or whatever form you're using the code in)
P.S.: Sorry for the poor english, I hope you understood me.
Im trying to make an event of 8 picturebox together this is my code, but when i click it put always the same image, the event its been doing twice, i put a message on the event of pictureboxes and it appears twice.
public partial class Form1 : Form
{
int jug=1;
PictureBox[] PicBox = new PictureBox[9];
Image circu = Image.FromFile("Circulo1.png");
Image cruz = Image.FromFile("Cruz1.png");
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
PicBox[0] = this.pcb0;
PicBox[1] = this.pcb1;
PicBox[2] = this.pcb2;
PicBox[3] = this.pcb3;
PicBox[4] = this.pcb4;
PicBox[5] = this.pcb5;
PicBox[6] = this.pcb6;
PicBox[7] = this.pcb7;
PicBox[8] = this.pcb8;
for (int i = 0; i < 9; i++)
{
PicBox[i].Click += new System.EventHandler(PictureBoxes_Click);
}
}
private void PictureBoxes_Click(object sender, EventArgs e)
{
PictureBox p = (PictureBox)sender;
if (jug == 1)
{
jug = 2;
p.Image = cruz;
}
else
{
jug = 1;
p.Image = circu;
}
}
Try refreshing the pictureboxes with built in function. If that not solve the problem, set picturebox image property to null, then refresh and set the image you want.
Or you try setting click event on the design page of your ide, bind it the same function(in this case Pictureboxes_click)
There is no reason to enter the event twice. Your code actually works. I think maybe you have some other controls in your form which uses the same event. Just make sure that the event is being used only by the pictureboxes.
Another thing to do: Put breakpoint at the event and see what controls appear as sender. This will help you fix your problem.
Use pictureboxes' Tag property to understand which one is entering the event:
for (int i = 0; i < 9; i++)
{
PicBox[i].Tag = i;
}
When code enters the event, you can look at p.Tag to see which one triggered the event.
You have only 2 different images with single instances. Try to clone the images, so each PictureBox gets it's own instance of the image:
private void PictureBoxes_Click(object sender, EventArgs e)
{
PictureBox p = (PictureBox)sender;
if (jug == 1)
{
jug = 2;
p.Image = (Image)cruz.Clone();
}
else
{
jug = 1;
p.Image = (Image)circu.Clone();
}
}
Or you can replace the cloning by using the "FromFile"-method, which automatically creates the new instances:
p.Image = Image.FromFile("Circulo1.png");
I want to implement Nine Men's Morris Game.
I have a board with 24 pictureboxes and on the left and right side, 9 red pictureboxes and 9 green pictureboxes.
I want to add them in a list:
List<PictureBox> ls = new List<PictureBox>();
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 1; i <= 24; i++)
{
PictureBox p = new PictureBox();
p.Name = "pictureBox" + i;
ls.Add(p);
}
}
is it ok?
and is it possible to do something like this: I want to click on one of the 24 pictureboxes, and make the background of that picturebox to become one time green and one time red?
I mean recursive function or something like that that can recognize when i click on a picturebox, search in the list for that picturebox and changes his backcolor?
You don't need any pictureBox list here.
for (int i = 1; i <= 24; i++)
{
PictureBox p = new PictureBox();
p.Click += p_Click;
//of course, somecontrol.Controls.Add(p);
//for ex: this.Controls.Add(p);
}
-
void p_Click(object sender, EventArgs e)
{
((PictureBox)sender).BackColor = Color.Green;
}
EDIT
It seems you are trying to add an event handler to all pictureBoxes
**parentControl**.Controls.OfType<PictureBox>()
.ToList().ForEach(p => p.Click+=p_Click);
I assume the list of 24 PictureBoxes is supposed to represent the points on a nine man morris board where the player's men can be positioned.
I4V is right that all you need to do is add a click handler to each picture box. If you want to have the background alternate between green and red, keep your original list, but add the click handler in it
for (int i = 1; i <= 24; i++)
{
PictureBox p = new PictureBox();
p.Name = "pictureBox" + i;
p.Click += p_Click; // <----------
ls.Add(p);
}
And modify i4v's click handler to use the current background color to determine the new background color.
void p_Click(object sender, EventArgs e)
{
PictureBox p = (PictureBox)sender);
p.BackColor = p.BackColor == Color.Green ? Color.Red : Color.Green;
}
A couple of other points.
You don't set an initial background color, so it will be the default color until clicked on, when it will be set to Green (as Green isn't the default background color).
Why name your pictureboxes w/ their List index + 1? Why not just use the List index and the natural C# iteration from 0: for (int i = 0; i < 24; i++)?
The method you have outlined would not work, however there is another way to do the same thing using the sender object passed into the event handler:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 1; i <= 24; i++)
{
PictureBox p = new PictureBox();
p.Name = "pictureBox" + i;
p.Click += PictureBox_Click;
}
}
void PictureBox_Click(object sender, EventArgs e)
{
PictureBox event_picturebox = (PictureBox)sender;
event_picturebox.BackColor = Color.White;
}
You just have to map every picture box you want to run this event to the same event, the event will then be able to perform actions on this pictureBox because a reference to it was passed in.
If you already have the picture boxes defined in the form, you just need to do something like:
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Click += PictureBox_Click;
pictureBox2.Click += PictureBox_Click;
// and keep going
// OR
// this is a bit dangerous if you don't want ALL
// your picture boxes to have this event
// also assumes that you know picturebox1 exists.
foreach (object f in this.Controls)
{
if (f.GetType().Equals(pictureBox1.GetType()))
{
((PictureBox)f).Click += button_Click;
}
}
}
Can someone help about create a winform animation like in Win7 Calculator when you hover mouse over button, currently i use bunch of image then looping it in backgroundworker, but i think its wrong, this is my code:
this occur when mouse enter,
private void bgTurnOn_DoWork(object sender, DoWorkEventArgs e)
{
Label labelSender = (Label)e.Argument;
int ii = labelSender.ImageIndex;
for (int i = ii + 4; i <= 11; i++)
{
if (labelSender.AllowDrop)
{
labelSender.ImageIndex = i;
Thread.Sleep(40);
}
}
}
and this when mouse leave
private void bgTurnOff_DoWork(object sender, DoWorkEventArgs e)
{
Label labelSender = (Label)e.Argument;
int ii = labelSender.ImageIndex;
for (int i = ii; i >= 0; i--)
{
if (!labelSender.AllowDrop)
{
labelSender.ImageIndex = i;
Thread.Sleep(80);
}
}
}
note: I just use AllowDrop so I do not bother to declare new variable, i have 42 button, so i think i need more efficient solution.
It seems that you want a glow effect, so you can use the next idea:
Make an OpacityPictureBox : PictureBox which supports opacity (in levels of 1-100 or double 0-1). See this for more information.
Add two public const int values of MaxOpacity and MinOpacity to the OpacityPictureBox class, for easy and safe range checks from the outside. The values might be 0, 100 or 0, 1, or something else, depending on your implementation of opacity.
Make an AnimatedPictureBox : UserControl which holds 1 PictureBox named pbNormal and 1 OpacityPictureBox named opbHover, both Dock = DockStyle.Fill, and one timer named timer. Make sure that pbNormal is below opbHover.
Have three public properties:
Normal of type Image which delegates into pbNormal.Image
Hover of type Image which delegates into opbHover.Image
AnimationInterval of type int which delgates into timer.Interval
In the constructor of the AnimatedPictureBox, after calling InitializeComponents, do opbHover.Opacity = 0;. You can also do this.Cursor = Cursors.Hand; if you want the cursor to change into a hand when hovering over it.
Have a private members: _animationDirection of type int, which will be -1 or 1.
Have a private method that starts an animation in a given direction:
Code:
private void Animate(int animationDirection)
{
this._animationDirection = animationDirection;
this.timer.Start();
}
Override OnMouseEnter and OnMouseLeave:
Code:
protected override void OnMouseEnter(EventArgs e)
{
this.Animate(1);
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
this.Animate(-1);
base.OnMouseEnter(e);
}
Listen to timer.Tick event and with this:
Code:
private void timer_Tick(object sender, EventArgs e)
{
var hoverOpacity = this.opbHover.Opacity + this._animationDirection;
if (hoverOpacity < OpacityPictureBox.MinOpacity ||
hoverOpacity > OpacityPictureBox.MaxOpacity)
{
this.timer.Stop();
return;
}
this.opbHover.Opacity = hoverOpacity;
}