2011-11-16 62 views
3

我认为C#win表格在某些情况下没有被重新绘制的问题在不同的地方被覆盖了,但是,我没有设法通过使用我发现的简单片段来解决我的问题网页。C#ListView不更新

我的问题:在窗体上我有一个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()); 
     } 
    } 

它建立,但没有效果。在调试模式下,永远不会进入'if'分支,总是'else'。

那么这个:

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

我得到一个“:调用或BeginInvoke可直到窗口句柄已创建不能在一个控件调用InvalidOperationException异常。”

我必须在这里失踪的显而易见的事情是什么?或者,我的问题实际上不是我认为的问题?

谢谢你们!

回答

3

你是对的,问题在于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 
    } 
} 

这里真正的关键是,你正在尝试做一个更新控制已经完全初始化之前。

1

首先

不建,调用我的列表模式不存在。

据我记得,Invoke是Control类的方法。 因此,如果没有从Control继承的任何类实例,就无法在ListModel类中调用它。 使用

this.list.Invoke( 

在调试模式,永不熄灭的“如果”分支,永远是“其他”。

这可能意味着this.list.InvokeRequired在GUI thead中被调用。

它也可能意味着list.InvokeRequired被称为之前this.list被漆成至少一次。 这是棘手的时刻。如果Control类的实例还没有绘制成gdi +(或者C#WinForm绘画的底层)尚未初始化。所以没有什么可以同步。 请仔细检查。

+0

嗯,对我不是很清楚,对不起 – Jerome

+0

做了更改,但我仍然得到一个异常 – Jerome

+0

@jerome G:Again:“InvalidOperationException:Invoke或BeginInvoke不能在控件上调用,直到窗口句柄被创建。 “ 这意味着控制尚未创建。我不能提供任何建议 这是问题的根源。 – VMykyt