C# ListView doesn't update

本文关键字:update ListView doesn | 更新日期: 2023-09-27 18:14:53

我认为c# win表单在某些情况下没有被很好地重新绘制的问题在不同的地方被覆盖,然而,我没有设法通过使用我在网上找到的简单代码片段来解决我的问题。

我的问题:在一个表单上,我有一个listView,我关联到一个自定义数据持有人(2列,一个键和最后更新日期)。在不同的地方,我需要调用updateTime(key)方法,然后在GUI中复制更改。模型被改变了,但我的listView从来没有。

我有一个包含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()。在调试中,代码肯定会到达那里。相信这个问题可以通过异步"调用"来解决,我参考了这篇似乎是参考的文章:Stack overflow: Automating the invoke…

Then try this

    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不能在控件上调用,直到窗口句柄被创建。"

我在这里错过了什么明显的东西?还是我的问题其实不是我想的那样?

C# ListView doesn't update

你是正确的,问题在于updateView()代码。您确实需要调用UI线程,但问题是还没有为控件创建句柄。使用WinForms时的一个问题是,如果句柄尚未创建,invokerequirequired实际上会返回false。请参阅MSDN文档中的解释:

如果控件的句柄还不存在,invokerrequirequired将搜索该控件的父链,直到找到具有窗口句柄的控件或窗体。如果找不到合适的句柄,invokerrequirequired方法返回false

这就是为什么你的invokerrequired_check总是失败的原因。我看到这个问题在几个方面得到了解决。一种解决方案是将回调附加到控件的句柄创建事件:

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
    }
}
这里真正的关键是您试图在控件完全初始化之前对其进行更新。

首先

不构建,调用不存在于我的列表模型。

据我所知,Invoke是Control类的方法。所以你不能在ListModel类中调用它没有任何从Control继承的类实例。使用

this.list.Invoke( 

在调试模式下,永远不要进入'if'分支,总是进入'else'。

这可能意味着This .list. invokerrequired在GUI头部被调用了。

但是也可能意味着列表。invokerrequired在 this之前被调用。名单至少被画过一次。这是个棘手的时刻。如果控件类的实例还没有绘制,那么gdi+(或者c# WinForm绘制的底层)还没有初始化。所以没有什么需要同步的。