BindingSource MoveFirst/MoveLast doesn't work
Asked Answered
F

2

8

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?

Fairfield answered 11/3, 2013 at 12:42 Comment(11)
could you try bs.EndEdit(); after the moveFirst/Last?Zhao
@NDJ: Thanks, but doesn't work either.Fairfield
maybe something in the grid overrides the bs position. try using a "later" event like KeyUpUnpractical
@JensKloster: Hmm, now the current item "jumps" two positions if i hit Keys.Up/Down. What can cause it?Fairfield
@TimSchmelter perhaps the grid is set to do a "row select" instead of a "cell select". if that is the case - the the default behavior then using e.g. Keys.Up is to focus the previos row - therby automaticly updating the bs.Position.Unpractical
try comment out all the shown code, and see if it works. :)Unpractical
@JensKloster: Omg , it's already builtin, thanks. So i just have to implement the code for the MoveFirst and MoveLast. But the next problem is that i cannot detect when the last item is selected since BindingSource.Position is set before the KeyUp event is triggered. You may want to answer it anyway so i can upvote/accept it.Fairfield
@TimSchmelter What needs to happen if the last (or first) item is selected? you could use bs.PositionChanged event instead of the grid.Unpractical
@JensKloster: If the first item is selected and Keys.Up is pressed, the last item should be selected and if the last item was selected and the user hits Keys.Down then first should be selected. How would PositionChanged help to identify if the user wants the next or previous item? It would be triggered only if the position was already changed and not on the first or last position(which is the only relevant event).Fairfield
@JensKloster: I have solved it with your suggested approach using PositionChanged, so you might want to post an answer with your comments. I can add my code afterwards. Maybe there's something easier but it works.Fairfield
@TimSchmelter hmm after some tests - i see what you mean. I beliver there might be a smarter way but I can't see it right now.. maybe later i well. I don't feel like posting an answer, becuase i don't feel i have a solution. But thanks. :)Unpractical
U
2

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.

Unpractical answered 11/3, 2013 at 19:13 Comment(1)
Thanks for your help. It's too bad that there's no approach without this ugly variable :)Fairfield
F
2

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.

Fairfield answered 11/3, 2013 at 14:58 Comment(0)
U
2

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.

Unpractical answered 11/3, 2013 at 19:13 Comment(1)
Thanks for your help. It's too bad that there's no approach without this ugly variable :)Fairfield

© 2022 - 2024 — McMap. All rights reserved.