Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BindingSource MoveFirst/MoveLast doesn't work

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?

like image 225
Tim Schmelter Avatar asked Mar 11 '13 12:03

Tim Schmelter


2 Answers

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.

like image 160
Jens Kloster Avatar answered Sep 27 '22 01:09

Jens Kloster


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.

like image 38
Tim Schmelter Avatar answered Sep 24 '22 01:09

Tim Schmelter