滚动并单击后,DataGridView跳转到顶部

时间:2022-11-22 14:46:31

I have a DataGridView with filtering. When the filter is applied the scroll position is returned to the top and the data in the grid is updated as expected. When I then scroll down using the scroll bar then click on a row, the scroll jumps back up to the top again and the wrong row gets selected. Then I scroll down again and click, no reset. This is the behavior I expected.

我有一个带过滤的DataGridView。应用过滤器后,滚动位置将返回到顶部,并且网格中的数据将按预期更新。当我然后使用滚动条向下滚动然后单击一行时,滚动会再次跳回到顶部并选择错误的行。然后我再次向下滚动并单击,不重置。这是我期望的行为。

I understand why the first jump occurs because the grid has effectively been rebound to a new source. However, why it jumps back up again after scrolling and clicking is a bit vexing. I have tried using both a BindingList and a BindingSource. I have tried calling all sorts of update and refreshes and position resets on the DataGridView to try to induce the second reset programatically instead of when the user clicks. Any ideas?

我理解为什么第一次跳跃发生是因为网格已经有效地反弹到新的来源。但是,为什么它在滚动和点击后再次跳起来有点令人烦恼。我尝试过使用BindingList和BindingSource。我已经尝试在DataGridView上调用各种更新和刷新以及位置重置,以尝试以编程方式而不是在用户单击时引发第二次重置。有任何想法吗?

The only other wrinkle is that I'm using the MVVM pattern with databinding to approximate the knockout.js we use on the web. Functionally, this shouldn't differ from something like txtPartNumberQuery_OnChanged(){RebindGrid();}

唯一的另一个问题是我正在使用带有数据绑定的MVVM模式来近似我们在网络上使用的knockout.js。从功能上讲,这应该与txtPartNumberQuery_OnChanged(){RebindGrid();}不同。

I figured this out right after I took the time to write it up, so... I suppose I'll answer my own question.

我花了很多时间把它写出来之后就把它想出来了,所以...我想我会回答我自己的问题。

View Model:

public string PartQueryString { 
    get
    {
        return _partQueryString;
    } 
    set
    {
        _partQueryString = value;
        //observe this value and trigger a search when it changes
        this.PartMaster.DataSource = _model.SearchPartMaster(_partQueryString);
    }
}

View:

private void ConfigureGridView()
{
    gvPartMaster.AutoGenerateColumns = false;            
    gvPartMaster.Columns.Add(Common.Helper.GetBasicGridViewColumn<DataGridViewTextBoxColumn>("Number", "Part Number"));
    gvPartMaster.Columns.Add(Common.Helper.GetBasicGridViewColumn<DataGridViewTextBoxColumn>("Description", "Part Description" ));
    gvPartMaster.Columns.Add(Common.Helper.GetBasicGridViewColumn<DataGridViewTextBoxColumn>("ManufacturerDescription", "Manufacturer" ));
    gvPartMaster.Columns.Add(Common.Helper.GetBasicGridViewColumn<DataGridViewTextBoxColumn>("CategoryDescription", "Category" ));
    gvPartMaster.Columns.Add(Common.Helper.GetBasicGridViewColumn<DataGridViewTextBoxColumn>("Specs", "Specs"));
    gvPartMaster.ColumnHeadersDefaultCellStyle.WrapMode = DataGridViewTriState.False;

    gvPartMaster.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
}

private void ApplyBindings()
{
    txtPartNumberQuery.DataBindings.Add("Text", _viewModel, "PartQueryString", false, DataSourceUpdateMode.OnPropertyChanged);
    gvPartMaster.DataSource = _viewModel.PartMaster;
}

1 个解决方案

#1


0  

There are two problems here:

这里有两个问题:

  • The databinding on the textbox is firing an extra time when it loses focus, even though the value hasn't really changed.
  • 即使值没有真正改变,文本框上的数据绑定也会在失去焦点时触发额外的时间。

  • Clicking on a scroll bar does not constitute a loss of focus.
  • 单击滚动条不会导致失去焦点。

If you put 2 and 2 together, you get that the datagrid is being rebound not when they adjust the scroll but when they try to click the row. As we all know, rebinding a datagridview resets the scroll back to the top.

如果你把2和2放在一起,你会得到数据网格反弹,而不是当他们调整滚动但是当他们试图点击该行时。众所周知,重新绑定datagridview会将滚动重置为顶部。

My solution is to ensure the filter condition has changed before rebinding the gridview.

我的解决方案是在重新绑定gridview之前确保过滤条件已更改。

public string PartQueryString { 
    get
    {
        return _partQueryString;
    } 
    set
    {
        if (_partQueryString != value)
        {
            _partQueryString = value;
            //observe this value and trigger a search when it changes
            this.PartMaster.DataSource = _model.SearchPartMaster(_partQueryString);
            _view.UpdateGridViewPosition();
        }
    }
}

Functionally, this shouldn't differ from something like txtPartNumberQuery_OnChanged(){RebindGrid();}

从功能上讲,这应该与txtPartNumberQuery_OnChanged(){RebindGrid();}不同。

This is actually a poor assumption because the TextChanged event would not have fired twice the way the databinding does.

这实际上是一个不好的假设,因为TextChanged事件不会像数据绑定那样触发两次。

#1


0  

There are two problems here:

这里有两个问题:

  • The databinding on the textbox is firing an extra time when it loses focus, even though the value hasn't really changed.
  • 即使值没有真正改变,文本框上的数据绑定也会在失去焦点时触发额外的时间。

  • Clicking on a scroll bar does not constitute a loss of focus.
  • 单击滚动条不会导致失去焦点。

If you put 2 and 2 together, you get that the datagrid is being rebound not when they adjust the scroll but when they try to click the row. As we all know, rebinding a datagridview resets the scroll back to the top.

如果你把2和2放在一起,你会得到数据网格反弹,而不是当他们调整滚动但是当他们试图点击该行时。众所周知,重新绑定datagridview会将滚动重置为顶部。

My solution is to ensure the filter condition has changed before rebinding the gridview.

我的解决方案是在重新绑定gridview之前确保过滤条件已更改。

public string PartQueryString { 
    get
    {
        return _partQueryString;
    } 
    set
    {
        if (_partQueryString != value)
        {
            _partQueryString = value;
            //observe this value and trigger a search when it changes
            this.PartMaster.DataSource = _model.SearchPartMaster(_partQueryString);
            _view.UpdateGridViewPosition();
        }
    }
}

Functionally, this shouldn't differ from something like txtPartNumberQuery_OnChanged(){RebindGrid();}

从功能上讲,这应该与txtPartNumberQuery_OnChanged(){RebindGrid();}不同。

This is actually a poor assumption because the TextChanged event would not have fired twice the way the databinding does.

这实际上是一个不好的假设,因为TextChanged事件不会像数据绑定那样触发两次。