Обновление списка .NET - PullRequest
5 голосов
/ 21 июля 2010

У меня есть следующий код, который в основном берет значения из базы данных и заполняет представление списка.

using (IDataReader reader = cmd.ExecuteReader())
{                    
    lvwMyList.Items.Clear();
    while (reader.Read())
    {
        ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
        lvi.SubItems.Add(reader["Value2"].ToString());                    
    }
}

Проблема, с которой я столкнулся, состоит в том, что это многократно выполняется через короткие промежутки времени (каждую секунду) и приводит к тому, что элементы в списке постоянно исчезают и снова появляются. Есть ли способ остановить обновление списка до тех пор, пока это не будет сделано с обновлениями? Примерно так:

using (IDataReader reader = cmd.ExecuteReader())
{                    
    lvwMyList.Items.Freeze(); // Stop the listview updating
    lvwMyList.Items.Clear();
    while (reader.Read())
    {
        ListViewItem lvi = lvwMyList.Items.Add(reader["Value1"].ToString());
        lvi.SubItems.Add(reader["Value2"].ToString());                    
    }
    lvwMyList.Items.UnFreeze(); // Refresh the listview
}

Ответы [ 3 ]

9 голосов
/ 21 июля 2010

Вот так:

try
{
    lvwMyList.BeginUpdate();
    //bla bla bla

}
finally
{
    lvwMyList.EndUpdate();
}

Убедитесь, что вы вызываете lvwMyList.Items.Clear() после BeginUpdate, если хотите очистить список перед его заполнением.

1 голос
/ 30 июля 2016

Это моя первая публикация в StackOverflow, так что извините за грязное форматирование кода ниже.

Чтобы предотвратить блокировку формы при обновлении ListView, вы можете использовать метод, описанный ниже, для решения этой проблемы.

Примечание: Этот метод долженне будет использоваться, если вы ожидаете заполнить ListView более чем 20000 элементов.Если вам нужно добавить более 20 000 элементов в ListView, рассмотрите возможность запуска ListView в виртуальном режиме.

 public static async void PopulateListView<T>(ListView listView, Func<T, ListViewItem> func, 
        IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
    {
        if (listView != null && listView.IsHandleCreated)
        {
            var conQue = new ConcurrentQueue<ListViewItem>();

            // Clear the list view and refresh it
            if (listView.InvokeRequired)
            {
                listView.BeginInvoke(new MethodInvoker(() =>
                    {
                        listView.BeginUpdate();
                        listView.Items.Clear();
                        listView.Refresh();
                        listView.EndUpdate();
                    }));
            }
            else
            {
                listView.BeginUpdate();
                listView.Items.Clear();
                listView.Refresh();
                listView.EndUpdate();
            }

            // Loop over the objects and call the function to generate the list view items
            if (objects != null)
            {
                int objTotalCount = objects.Count();

                foreach (T obj in objects)
                {
                    await Task.Run(() =>
                        {
                            ListViewItem item = func.Invoke(obj);

                            if (item != null)
                                conQue.Enqueue(item);

                            if (progress != null)
                            {
                                double dProgress = ((double)conQue.Count / objTotalCount) * 100.0;

                                if(dProgress > 0)
                                    progress.Report(dProgress > int.MaxValue ? int.MaxValue : (int)dProgress);
                            }
                        });
                }

                // Perform a mass-add of all the list view items we created
                if (listView.InvokeRequired)
                {
                    listView.BeginInvoke(new MethodInvoker(() =>
                        {
                            listView.BeginUpdate();
                            listView.Items.AddRange(conQue.ToArray());
                            listView.Sort();
                            listView.EndUpdate();
                        }));
                }
                else
                {
                    listView.BeginUpdate();
                    listView.Items.AddRange(conQue.ToArray());
                    listView.Sort();
                    listView.EndUpdate();
                }
            }
        }

        if (progress != null)
            progress.Report(100);
    }

Вам не нужно предоставлять объект IProgress, просто используйте null, и метод будет работать простоа также.

Ниже приведен пример использования метода.

Сначала определите класс, содержащий данные для ListViewItem.

public class TestListViewItemClass
{
    public int TestInt { get; set; }

    public string TestString { get; set; }

    public DateTime TestDateTime { get; set; }

    public TimeSpan TestTimeSpan { get; set; }

    public decimal TestDecimal { get; set; }
}

Затем создайтеметод, который возвращает ваши элементы данных.Этот метод может запрашивать базу данных, вызывать API веб-службы и т. Д., Если он возвращает IEnumerable вашего типа класса.

public IEnumerable<TestListViewItemClass> GetItems()
{
    for (int x = 0; x < 15000; x++)
    {
        yield return new TestListViewItemClass()
        {
            TestDateTime = DateTime.Now,
            TestTimeSpan = TimeSpan.FromDays(x),
            TestInt = new Random(DateTime.Now.Millisecond).Next(),
            TestDecimal = (decimal)x + new Random(DateTime.Now.Millisecond).Next(),
            TestString = "Test string " + x,
        };
    }
}

Наконец, в форме, где находится ваш ListView, вы можете заполнитьListView.В демонстрационных целях я использую событие Load формы для заполнения ListView.Скорее всего, вы захотите сделать это в другом месте формы.

Я включил функцию, которая генерирует ListViewItem из экземпляра моего класса, TestListViewItemClass.В производственном сценарии вы, вероятно, захотите определить функцию в другом месте.

private async void TestListViewForm_Load(object sender, EventArgs e)
{     
    var function = new Func<TestListViewItemClass, ListViewItem>((TestListViewItemClass x) =>
    {
        var item = new ListViewItem();

        if (x != null)
        {
            item.Text = x.TestString;
            item.SubItems.Add(x.TestDecimal.ToString("F4"));
            item.SubItems.Add(x.TestDateTime.ToString("G"));
            item.SubItems.Add(x.TestTimeSpan.ToString());
            item.SubItems.Add(x.TestInt.ToString());
            item.Tag = x;

            return item;
        }

        return null;
    });

       PopulateListView<TestListViewItemClass>(this.listView1, function, GetItems(), progress);

 }

В приведенном выше примере я создал объект IProgress в конструкторе формы следующим образом:

progress = new Progress<int>(value =>
{
    toolStripProgressBar1.Visible = true;

    if (value >= 100)
    {
        toolStripProgressBar1.Visible = false;
        toolStripProgressBar1.Value = 0;
    }
    else if (value > 0)
    {
        toolStripProgressBar1.Value = value;
    }
 });

Я использовал этот метод заполнения ListView много раз в проектах, где мы заполняли до 12 000 элементов в ListView, и это очень быстро.Главное, чтобы ваш объект был полностью построен из базы данных, прежде чем вы даже коснетесь ListView для обновлений.

Надеюсь, это полезно.

Я включил ниже асинхронную версиюметод, который вызывает основной метод, показанный в верхней части этого поста.

public static Task PopulateListViewAsync<T>(ListView listView, Func<T, ListViewItem> func,
        IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
    return Task.Run(() => PopulateListView<T>(listView, func, objects, progress));
}
0 голосов
/ 22 июля 2010

Вы также можете попробовать установить для видимых или включенных свойств значение false во время обновления и посмотреть, нравятся ли вам эти результаты лучше.Конечно, сбросьте значения в true, когда обновление будет выполнено.

Другой подход заключается в создании панели для наложения списка.Установите его свойства left, right, height и width так же, как ваш список, и установите для свойства visible значение true во время обновления, false после завершения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...