views:

414

answers:

2

I have a text filter where in the TextChanged event I launch a listview populate code this way:

ThreadPool.QueueUserWorkItem(new WaitCallback(populate));

Then in the populate method I have code like this

listView1.BeginUpdate();
listView1.Clear();

// rest of the code

listView1.EndUpdate();

but the listView1.BeginUpdate() call gives the following exception:

System.NotSupportedException was unhandled
  Message="An error message cannot be displayed because an optional resource assembly containing it cannot be found"
  StackTrace:
    at Microsoft.AGL.Common.MISC.HandleAr()
    at System.Windows.Forms.ListView.ntvSetStyleEx()
    at System.Windows.Forms.ListView.BeginUpdate()
    at App.frmSelectClient.populate()
    at WorkItem.doWork()
    at System.Threading.Timer.ring()
  InnerException: 

What I am doing wrong?

I would like to issue the populate of the ListView in a background thread.

+5  A: 

You can't update UI elements from any thread other than the UI thread. Use Control.Invoke/BeginInvoke to do this.

You can do all your data loading etc in the background thread, but you'll need to marshal to the UI thread to actually populate the UI controls themselves.

This is the case in most UI frameworks - and certainly Windows Forms (desktop and CF) and Windows Presentation Foundation (where you use a Dispatcher rather than Control.Invoke/BeginInvoke).

One point to note: if I remember rightly, the Compact Framework only supports the EventHandler delegate for Control.Invoke/BeginInvoke. That may have changed in more recent versions, admittedly.

Jon Skeet
can you please post a simple code fragment to handle the invoke for CF 2.0 ?
Pentium10
CF 3.5 allows any delegate. 2.0 and 1.0 were restricted to using an EventHandler
ctacke
@Pentium10: Find any tutorial about Control.Invoke/BeginInvoke and it will give sample code. So long as the delegate is EventHandler, it should work fine on the Compact Framework.
Jon Skeet
@Pentium10: here's an old CF 1.0-compatible example: http://blogs.msdn.com/davidklinems/archive/2005/03/21/400102.aspx
ctacke
A: 

Hello,

Like it was mentioned in other posts to update ListView from another thread you have to do it the proper way using BeginInvoke/Invoke. I've created for myself couple of helper functions that are thread safe (for both ListView and more Controls). Whenever i wanna do something with listview or control itself i just use one of those mentioned below. Of course it misses a lot of other options but so far I haven't had a need for something else so i didn't created more, but it should show you the right way to eventually add them up to the list.

internal class GlobalFunctions {
    public static void changeControlStatus(Control varControl, bool varState) {
        if (varControl.InvokeRequired) {
            varControl.BeginInvoke(new ControlStateChange(changeControlStatus), new object[] {varControl, varState});
        } else {
            varControl.Enabled = varState;
        }
    }
    public static void changeControlText(Control varControl, string varText) {
        if (varControl.InvokeRequired) {
            varControl.BeginInvoke(new ControlTextChange(changeControlText), new object[] {varControl, varText});
        } else {
            varControl.Text = varText;
        }
    }
    public static string readControlText(Control varControl) {
        if (varControl.InvokeRequired) {
            return (string) varControl.Invoke(new Func<String>(() => readControlText(varControl)));
        } else {
            string varText = varControl.Text;
            return varText;
        }
    }
    public static int listViewCountItems(ListView varControl) {
        if (varControl.InvokeRequired) {
            return (int) varControl.Invoke(new Func<int>(() => listViewCountItems(varControl)));
        } else {
            return varControl.Items.Count;
            //string varText = varControl.Text;
            //return varText;
        }
    }
    public static void comboBoxClearItems(ComboBox varControl) {
        if (varControl.InvokeRequired) {
            varControl.BeginInvoke(new MethodInvoker(() => comboBoxClearItems(varControl)));
        } else {
            varControl.Items.Clear();
        }
    }
    public static void listViewClearItems(ListView varListView) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new ListViewHandler(listViewClearItems), new object[] {varListView});
        } else {
            varListView.Items.Clear();
        }
    }
    public static void listViewClearColumns(ListView varListView) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new ListViewHandler(listViewClearColumns), new object[] {varListView});
        } else {
            varListView.Clear();
        }
    }
    public static void listViewAddItem(ListView varListView, ListViewItem item) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewAddItem(varListView, item)));
        } else {
            varListView.Items.Add(item);
        }
    }
    public static void listViewEditItem(ListView varListView, int varRow, int varColumn, string varText) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewEditItem(varListView, varRow,varColumn, varText )));
        } else {
            varListView.Items[varRow].SubItems[varColumn].Text = varText;
        }
    }
    public static void listViewEditItemColor(ListView varListView, int varRow, Color varColor) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewEditItemColor(varListView, varRow, varColor)));
        } else {
            varListView.Items[varRow].BackColor = varColor;
        }
    }
    public static void listViewChangeBackColor(ListView varListView, ListViewItem item, Color varColor) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewChangeBackColor(varListView, item, varColor)));
        } else {
            for (int i = 0; i < varListView.Columns.Count; i++) {
                item.UseItemStyleForSubItems = false;
                item.SubItems[i].BackColor = varColor;
            }
        }
    }
    public static void listViewChangeHeaderStyle(ListView varListView, ColumnHeaderStyle varColumnHeaderStyle) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewChangeHeaderStyle(varListView, varColumnHeaderStyle)));
        } else {
            varListView.HeaderStyle = varColumnHeaderStyle;
        }
    }
    public static void listViewAddItemRange(ListView varListView, ListViewItem item) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewAddItemRange(varListView, item)));
        } else {
            //varListView.Items.Add(item);
            varListView.Items.AddRange(new[] {item});
        }
    }
    public static void listViewAddColumn(ListView varListView, string varColumnName, int varColumnSize) {
        if (varListView.InvokeRequired) {
            varListView.BeginInvoke(new MethodInvoker(() => listViewAddColumn(varListView, varColumnName, varColumnSize)));
        } else {
            varListView.Columns.Add(varColumnName, varColumnSize, HorizontalAlignment.Left);
        }
    }
    #region Nested type: ControlStateChange
    private delegate void ControlStateChange(Control varControl, bool varState);
    #endregion
    #region Nested type: ControlTextChange
    private delegate void ControlTextChange(Control varControl, string varText);
    #endregion
    private delegate string ControlTextRead(Control varControl);
    #region Nested type: ListViewHandler
    private delegate void ListViewHandler(ListView varListView);
    #endregion
    #region Nested type: ListViewHandlerItem
    private delegate void ListViewHandlerItem(ListView varListView, ListViewItem item);
    #endregion
}

Usage is simple: GlobalFunctions.listViewClearItems(yourListView);
Hope this helps :)

Edit: i have missed compact framework tag so not sure this is applicable to it. Someone should be able to confirm it thou.

MadBoy
For 3.5 these would work (though why not use extension methods in taht case?). For 2.0, which I think the questioner is using, these won't as only EventHandler is supported.
ctacke
I am not at that level of programming yet :-) Learning every day thou so now gonna read up on extension methods and how to use them. Care to shoot an example on one of my methods?
MadBoy