Datarepeater具有嵌入式datagridview的更改表

时间:2013-09-03 10:43:58

标签: c# winforms datarepeater

我有一个datarepeater,在datarepeater的每一行都有一个datagridview。

当我将数据添加到第一个datagridview和第二个时,我得到了这个 -

Before scrolling

然后,如果我向下滚动到页面下方的datarepeater行,然后滚动到顶部,我们得到这个 -

After scrolling

注意项目已改变位置。如果我要向许多datagridviews添加行,当我滚动时会变得非常混乱。

为什么会发生这种情况的任何想法?

1 个答案:

答案 0 :(得分:1)

我在以下设置中遇到了类似的问题:

  • 每行复选框
  • 搜索文本框(在转发器控制之前,用于过滤项目)
  • 转发器中的许多项目(至少是一个足以滚动的数字)

我会过滤(或不过滤)我的列表并从转发器中选择项目。但是当我滚动时,我的选定行将不会被跟踪。我滚动时选择了随机行。

我认为您可能遇到了同样的问题。我没有证据证明我说的是什么,我也不能在那个时候找到答案,但我认为这与转发器的工作方式有关。

它根据显示的项目(在特定时间可见的项目)指定您的项目(行)索引。这样,可以在重复控制的同时实现快速可滚动组件。

想象一下,如果每行有10个控件并且要显示30.000行,那会是怎样的。如果转发器必须创建300.000个控件,您认为会发生什么?

出于这个原因(而且我猜测,这些是我的假设,因为文档非常稀缺 - 正如您现在所知道的那样)转发器仅为适合您的转发器区域的项目创建控件并对其进行回收。

这意味着如果对索引XX上的项目进行某种操作并滚动,由于项目不在同一索引中,转发器将翻转,因为滚动时会重新计算索引。

好的,现在我回答了你的问题,让我们看看如何解决它:

首先,将Label添加到转发器的ItemTemplate。此标签将用于绑定您的项目Id属性(或类似的东西)。另外,将标签Visible属性设置为false以保持隐藏状态。

在我的表单上添加以下字段:

// I use an observable collection to be notified when it changes
private ObservableCollection<YourItem> _allItems = 
    new ObservableCollection<YourItem>(); 
private BindingSource _bindingSource;

然后将您的集合绑定到重复(我在OnLoad中执行此操作)

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    ...

    _bindingSource = new BindingSource();
    _bindingSource.DataSource = _allItems;

    // my hidden label       // All my items have an Id property
    _labelHiddenId.DataBindings.Add("Text", BindingSource, "Id");
    _dataRepeaterList.DataSource = _bindingSource;

    _allItems.CollectionChanged += AllItems_CollectionChanged;
}

我的听众:

protected override void AllItems_CollectionChanged(object sender, 
    NotifyCollectionChangedEventArgs e)
{
    RefreshRepeater();
}

我的刷新方法:

protected void RefreshRepeater(bool search = false)
{
    if (_dataRepeaterList.InvokeRequired)
    {
        _dataRepeaterList.Invoke((Action)(() => { RefreshRepeater(search); }));
        return;
    }

    _bindingSource.DataSource = null; // Clear binding source first
    _bindingSource.DataSource = _allItems.ToList();
    _dataRepeaterList.DataSource = null; // Clear datasource first
    _dataRepeaterList.DataSource = _bindingSource;
    _dataRepeaterList.Refresh();
 }

我的绘制项目方法,这是我填写大部分行信息的地方:

protected override void DataRepeater_DrawItem(object sender, 
    DataRepeaterItemEventArgs e)
{
    var dataSourceEntity = GetObjectFromDataSource(e.DataRepeaterItem);
    var checkedComponent = _checkedItems.SingleOrDefault(
         x => x.Equals(dataSourceEntity));

    // Get current item control to fill. Something like
    var grid = e.DataRepeaterItem.Controls["yourgridiew"] as DataGridView;

    // Do stuff, you are messing with the right object :)
}

我的最后一篇:

protected override T GetObjectFromDataSource(DataRepeaterItem dataRepeaterItem)
{
    if (dataRepeaterItem == null)
        return null;

    var hiddenIdLabel = (Label)dataRepeaterItem.Controls[_labelHiddenId.Name];
    return _allItems.FirstOrDefault((entity) => entity.Id.ToString().Equals(hiddenIdLabel.Text));
}

此代码没有看到编译器,但它应该让您走上正确的轨道。

总结一下:

  • 在商品模板上创建隐藏标签
  • 创建可观察的集合,并在修改后的事件上刷新转发器
  • 创建绑定源,将其数据源设置为您的集合。
  • 让您的项目共享一个公共标识符,并将该标识符绑定到转发器。
  • 使用新数据刷新转发器时,请重置数据源(以防万一)
  • 创建一种基于当前转发器行查找原始对象(数据源对象)的方法。
  • 获取当前对象(数据源对象)的控件并对其应用操作。

这需要很长时间才能找到并实施,但希望对您来说更容易:)