3

我有一个数据中继器,在数据中继器的每一行都有一个数据网格视图。

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

滚动前

然后,如果我要向下滚动到页面下方的数据中继器行,然后滚动到顶部,我们会得到这个 -

滚动后

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

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

4

1 回答 1

1

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

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

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

我想你可能会遇到同样的问题。我没有证据证明我在说什么,当时我也找不到关于 SO 的答案,但我认为这与中继器的工作方式有关。

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

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

出于这个原因(我再次猜测,这些是我的假设,因为文档非常稀缺 - 正如您现在可能知道的那样)中继器只为适合您的中继器区域的项目创建控件并回收它们。

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

好的,现在我已经回答了您关于发生了什么的问题,让我们看看如何解决它:

首先,添加一个LabelItemTemplate您的中继器。此标签将用于绑定您的项目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));
}

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

把它们加起来:

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

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

于 2013-09-11T13:38:07.827 回答