C # ListView не обновляется - PullRequest
       1

C # ListView не обновляется

3 голосов
/ 16 ноября 2011

Я считаю, что проблема неэффективной перерисовки форм C # в определенных обстоятельствах рассматривается в разных местах, однако мне не удалось решить свои проблемы с помощью простых фрагментов, которые я нашел в Интернете.

Моя проблема: в форме у меня есть listView, который я связываю с пользовательским держателем данных (2 столбца, ключ и дата последнего обновления). Из разных мест мне нужно вызвать метод updateTime (ключ), который затем повторил изменения в графическом интерфейсе. Модель меняется, но мой список никогда не отображается.

У меня есть форма, содержащая ListView, которая выглядит так:

partial class VolsPane : UserControl, IGUIPane 
{
   private ListView listView1;
   private ListModel listModel1;     //ListModel is 100% homemade
   ...
   public VolsPane()
   {
       ...
       listModel1.setList(listView1);
   }
}

И класс, содержащий данные для моего listView, выглядит так:

class ListModel
{
    private Dictionary<string, DateTime> Underlying;
    private ListView list;

    ...

    public ListModel(string nexusKey)
    {
        ...
    }

    ...

    public void setList(ListView list)
    {
        this.list = list;
    }


    public void updateTime(string ric)
    {
        Underlying[ric] = DateTime.UtcNow;
        updateView();
    }

    public void updateView()
    {
        this.list.Clear();
        this.list.Items.AddRange(this.underlyingToListItems());
    }

    ...

    public ListViewItem[] underlyingToListItems()
    {
        ListViewItem[] res = new ListViewItem[Underlying.Keys.Count];
        int i = 0;
        foreach (string ric in Underlying.Keys)
        {
            res[i] = new ListViewItem(new string[] { ric, Underlying[ric].ToString("MMM-dd hh:mm:ss") });
            i++;
        }
        return res;
    }

}

Я понимаю, что проблема в моем updateView (). В отладке код идет туда определенно. Полагая, что проблема должна была быть решена с помощью асинхронного «вызова», я ссылался на этот пост, который выглядел как ссылка: Переполнение стека: автоматизация вызова…

Тогда попробовал это:

    private void updateView()
    {
        if (this.list.InvokeRequired)
        {
            this.list.Invoke(new MethodInvoker(() => { updateView(); }));
        }
        else
        {
            this.list.Items.Clear();
            //this.list.Clear();
            this.list.Items.AddRange(this.underlyingToListItems());
        }
    }

Он строит, но не имеет никакого эффекта. В режиме отладки никогда не идет в ветке 'if', всегда в 'else'.

Тогда это:

    private void updateView()
    {
        this.list.Invoke((MethodInvoker)delegate
        {
            this.list.Items.Clear();
            //this.list.Clear();
            this.list.Items.AddRange(this.underlyingToListItems());
        });
    }

Я получаю «InvalidOperationException: Invoke или BeginInvoke не могут быть вызваны для элемента управления, пока не будет создан дескриптор окна».

Что такое очевидное, чего мне здесь не хватает? Или моя проблема на самом деле не та, о которой я думаю?

Спасибо, ребята!

Ответы [ 2 ]

3 голосов
/ 18 ноября 2011

Вы правы, проблема заключается в коде updateView ().Вы должны вызвать Invoke для потока пользовательского интерфейса, но проблема в том, что дескриптор еще не создан для элемента управления.Одна хитрость при работе с WinForms заключается в том, что InvokeRequired на самом деле вернет false, если дескриптор еще не создан.См. Это объяснение в документации MSDN :

Если дескриптор элемента управления еще не существует, InvokeRequired выполняет поиск в родительской цепочке элемента управления, пока не найдет элемент управления или форму, которая имеетоконная ручкаЕсли подходящий дескриптор не найден, метод InvokeRequired возвращает false .

. Поэтому проверка InvokeRequired всегда не удалась.Я видел эту проблему исправленной несколькими способами.Одним из решений является прикрепление обратного вызова к событию, созданному для дескриптора элемента управления:

public class HandleHookedListView: ListView
{
    private EventHandler _handleCreatedEvent;

    public HandleHookedListView(): base()
    {
        _handleCreatedEvent = new EventHandler(HandleHookedControl_HandleCreated);
        this.HandleCreated += _handleCreatedEvent;
    }

    private bool _handleIsCreated;

    public bool HandleIsCreated
    {
        get { return _handleIsCreated; }
        set { _handleIsCreated = value; }
    }

    void HandleHookedControl_HandleCreated(object sender, EventArgs e)
    {
        Debug.Print("Handle Created");
        this.HandleIsCreated = true;

        // Unhook the delegate
        if (_handleCreatedEvent != null)
            this.HandleCreated -= _handleCreatedEvent;
    }
}

Затем вам нужно будет изменить свой updateView, чтобы проверить, был ли создан дескриптор.В этом случае ваш экземпляр ListView (list) был заменен новым HandleHookedListView

private void updateView()
{
    var handleCreated = this.list.HandleIsCreated;
    if (this.list.InvokeRequired && handleCreated)
    {
        // Handle is created and invoke is required.
        this.list.Invoke(new MethodInvoker(() => { updateView(); }));
    }
    else if (handleCreated)
    {
        // In this case your control's handle has been created and invoke really 
        // isn't required go ahead and do the update

        this.list.Items.Clear();
        this.list.Items.AddRange(this.underlyingToListItems());
    }
    else
    {
        // You cannot update yet.  The handle has not been created.  Depending on if
        // you need to "queue" these updates you can either collect them or just 
        // ignore them if a subsequent call to updateView() after the handle has been
        // created will be sufficient
    }
}

Реальный ключ здесь заключается в том, что вы пытаетесь обновить элемент управления до его полной инициализации.

1 голос
/ 16 ноября 2011

Прежде всего

Не создает, invoke не существует для моей модели списка.

Насколько я помню, Invoke - это метод Controlучебный класс.Поэтому вы не можете вызывать его в классе ListModel без какого-либо экземпляра класса, унаследованного от Control.Используйте

this.list.Invoke( 

В режиме отладки никогда не идет в ветке 'if', всегда в 'else'.

Это может означать, что this.list.InvokeRequiredбыл вызван в GUI thead.

Но это также может означать, что list.InvokeRequired был вызван до того, как this.list был нарисован хотя бы один раз.Это сложный момент.Если экземпляр класса Control еще не нарисован, то gdi + (или то, что находится под капотом рисования WinForm на C #) еще не инициализировано.Так что нечего синхронизировать.Пожалуйста, проверьте это дважды.

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