My winforms skills are a bit rusty. I'm using a BindingSource
for a DataGridView
. On KeyDown
of the DataGridView
i want to select the next/previous record which works as desired.
I want to select the first if the user hits Keys.Down
when the last item is selected and select the last if he hits Keys.Up
when the first item is selected. But nothing happens then.
Here's the code:
private void Grid_Keydown(Object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
previousItem();
else if (e.KeyCode == Keys.Down)
nextItem();
}
private void previousItem()
{
BindingSource bs = null;
switch (this.Type) // a custom enum
{
case AdminType.Channel:
bs = channelBindingSource;
break;
default:
break;
}
if (bs.Position - 1 < 0)
bs.MoveLast();
else
bs.MovePrevious();
}
private void nextItem()
{
BindingSource bs = null;
switch (this.Type)
{
case AdminType.Channel:
bs = channelBindingSource;
break;
default:
break;
}
if (bs.Position + 1 >= bs.Count)
bs.MoveFirst();
else
bs.MoveNext();
}
Note that bs.MoveFirst()
/bs.MoveLast()
are called correctly but nothing happens.
Edit: Interesting, it works as expected when i trigger this from a button(previous/next) instead of the DataGridView
's OnKeyDown
, any ideas?
The grid automatically moves the underderlying bindingsource's position, when you move up and down.
If you want the selection to go from top to button and vice versa - you could handle the grid_KeyDown
event an check it's position. unfortunately if you try moving the posistion, it get overridden by the gridview's Row_Enter
Event. thus changing the bindingsource position.
In the Grid_Keyup
event the position is already set, so you dont know if the user just move to the row, or if he/she want to move away from the row. But setting the bindingSource.Position
in here actually work - and does not get overridden by the grid.
You could also use the DataGridViewRow.Selected = true
but his does not move the underlying bindingsource's position. Also it is not ideal for grids the multiselect is enabled.
The ugly truth is that you must use a boolean (just like you do in you own answer), to control if the row should jump or not. :(
however you dont need to control it from the PositionChanged
event, you can do it by just handeling the grid_Keydown
event:
private bool _changePost;
private void dataGridView1_KeyUp(object sender, KeyEventArgs e)
{
var view = sender as DataGridView;
var bs = bindingSource1;
if (e.KeyData == Keys.Up)
{
if (bs.Position == 0 && _changePost)
{
_changePost = false;
bs.MoveLast();
}
if (bs.Position == 0 && !_changePost)
_changePost = true;
}
else if (e.KeyData == Keys.Down)
{
if (bs.Position == bs.Count - 1 && _changePost)
{
bs.MoveFirst();
_changePost = false;
}
if (bs.Position == bs.Count - 1 && !_changePost)
_changePost = true;
}
}
This was the as clean as I could get it.
Thanks to Jens Kloster, i have found this workaround. As he mentioned the DataGridView
already supports moving of the position of it's BindingSource
. So if it has focus and you hit up/down-arrow, the BindingSource
's MoveNext
/MovePrevious
are called implicitely.
I've noticed that the selected item "jumped" two positions when i handled the KeyUp
event(which is later then the KeyDown
event) instead, one for the programmatical and one for the builtin move.
So i only need to find a way to move the position from the first to the last and vice-versa if the up/down-keys were pressed. Therefore i have handled the BindingSource.PositionChanged
event to set a bool variable which i can check later:
bool positionChanged = false;
private void Source_PositionChanged(object sender, EventArgs e)
{
positionChanged = true;
}
private void Grid_KeyUp(Object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Up)
previousItem();
else if (e.KeyCode == Keys.Down)
nextItem();
positionChanged = false;
}
private void previousItem()
{
BindingSource bs = null;
switch (this.Type)
{
case AdminType.Channel:
bs = channelBindingSource;
break;
default:
break;
}
if (!positionChanged && bs.Position == 0)
bs.MoveLast();
else if (!positionChanged)
bs.MovePrevious();
}
private void nextItem()
{
BindingSource bs = null;
switch (this.Type)
{
case AdminType.Channel:
bs = channelBindingSource;
break;
default:
break;
}
if (!positionChanged && bs.Position == bs.Count - 1)
bs.MoveFirst();
else if (!positionChanged)
bs.MoveNext();
}
I'm still open for better solutions since this is a bit clumsy and error-prone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With