C# ListView 不更新

标签 c# winforms multithreading user-interface invoke

我认为 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...

然后尝试这个:

    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());
        }
    }

它构建但没有效果。在 Debug模式下,永远不会进入“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。”

我必须在这里遗漏什么明显的东西?或者我的问题实际上不是我想的那样?

谢谢大家!

最佳答案

你是对的,问题出在 updateView() 代码上。您确实需要调用 UI 线程,但问题是尚未为控件创建句柄。使用 WinForms 时的一个问题是,如果尚未创建句柄,则 InvokeRequired 实际上将返回 false。请参阅 MSDN documentation 中的解释:

如果控件的句柄尚不存在,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(列表)实例已替换为新的 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
    }
}

这里真正的关键是您正在尝试在控件完全初始化之前对其进行更新。

关于C# ListView 不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8149787/

相关文章:

javascript - 如何在代码隐藏中访问 session 变量

c# - 错误 :Index (zero based) must be greater than or equal to zero and less than the size of the argument list

c# - 解决应用程序失去焦点时桌面崩溃的问题

vb.net - Visual Basic 2008 - 不同线程上的新窗体

python - 将多个变量传递给PoolExecutor

c# - 如何从 URL、用户名和密码获取远程服务器的 SPSite?

c# - ASP.NET 网络 API : any downsides to asynchronous operations?

c# - 防止 WebBrowser 在导航时导致 UI 卡住?

c# - 如何处理在 Windows 7 上启用中/大字体时看起来很差的 winforms 应用程序?

c# - 异常: Object synchronization method was called from an unsynchronized block of code