Cross-thread operation not valid

May 14th, 2008

Sometimes you need to create a new thread in a windows form to do a parallel job, and (usually) want to update some windows controls based on what the worker thread is doing (typically incrementing a progress bar). There are two simple ways to access a control from a worker thread:

1) Passing the control to the thread:

Thread workerThread = new Thread(WorkerMethod);
workerThread.Start(myProgressBar);

The WorkerMethod being:

void WorkerMethod(object myProgressBar)
{...}

2)Providing an event which the worker method fires and the windows form handles - preferred:
Event Declaration (in the class containing the WorkerMethod):

event EventHandler UpdateUI;

Event firing (in WorkerMethod whenever needed):

if(UpdateUI != null)
UpdateUI(this, new EventArgs());

Subscribing to the event (in the windows form):

UpdateUI += new EventHandler(MyForm_UpdateUI);

Handling the event (in the windows form):

private void MyForm_UpdateUI(object sender, EventArgs e)
{ // update the control (progress bar) here }

Main Point: Now when you try to access your windows control in the WorkerMethod or the event handler, you get the following exception:

Cross-thread operation not valid: Control ‘progressBar’ accessed from a thread other than the thread it was created on.

Without talking about the what and why, here is how to get to your control:

progressBar.Invoke(new MethodInvoker(
delegate { progressBar.Increment(); }));

Mahdieh

dOOdads MarkAsDeleted might mislead you.

May 14th, 2008

For everyone who starts using MyGeneration.dOOdads (and suffices it to read the quick reference), a problem might happen when deleting multiple rows through iteration (as shown below) - some rows don’t get deleted.

Places places = new Places();
places.Where.Name.Value = "ABC%";
places.Where.Name.Operator = WhereParameter.Operand.Like;
places.Query.Load();
if(places.RowCount > 0)
{
   places.Rewind();
   do
   {
      places.MarkAdDeleted();
   } while( places.MoveNext()) ;
   places.Save();
}

Reason: You should not use MarkAsDeleted in a loop through a collection, because although this method does not physically remove a row (as the quick reference states), it does make the row invisible to a foreach (this is what the reference don’t tell you and is hard to guess). And it is obvious that you shouldn’t do anything inside a foreach that changes the contents of the collection.

Solution: You may instead use the DeleteAll() method, which deletes all rows resulted by your query (or filter) not all rows in the table. And don’t forget to call Save(), by the way.

Places places = new Places();
places.Where.Name.Value = "ABC%";
places.Where.Name.Operator = WhereParameter.Operand.Like;
places.Query.Load();
places.DeleteAll();
places.Save();

Mahdieh

Two Little Problems

February 5th, 2008

Two little problems that you may encounter in a Windows Application project:

1- You are using a DataGridView and want to change some (all) rows’ (cells) style, for example their color. Setting the Style property of rows or cells is the way to do so, but it won’t change anything unless done in dataGridView_RowsAdded event handler - at least I couldn’t find any other way to make it work.

2- You have a TabControl and usually want to make TabPages closable (using a ContextMenu)
and ,of course, don’t want the context menu to open inside the client area of the tab page, but just on the small header rectangle, as in VisualStudio tab pages.

Solution: ContextMenu has an Opening event and TabControl has a MouseDown event which fires before the ‘Opening’ event, so you’re gonna use them to cancel the opening whenever the click is not in the right place. Obviously, you should use a private bool allowContextMenuOpening.

private void tabControlPrograms_MouseDown(object sender, MouseEventArgs e)
{
   if (e.Button == MouseButtons.Right)
      for (int i = 0; i < tabControlPrograms.TabCount; i++)
         if (tabControlPrograms.GetTabRect(i).Contains(e.Location))
         {
            allowContextMenuOpening = true;
            return;
         }
}

private void closeVisitorContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
   if (!allowContextMenuOpening)
      e.Cancel = true;
   else
      allowContextMenuOpening = false;
}

Mahdieh

Curiosity!

January 12th, 2008

I was testing to see what happens when you try to open a windows form in a web service (when run from the server machine or a client one), which I knew was wrong - but I wanted to see what happens, you know!

The web method:

[WebMethod()]
public void ShowMainForm()
{
    new MainForm().ShowDialog();
}

Not surprisingly, running this method from another machine (other than the web server) results in the exception “System.InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation.”, same as when running the deployed web service through a browser.

What is strange is that it works fine when the service is launched from the Visual Studio Debugger (F5 | Ctrl+F5) - can’t say “fine” but it shows the form and there’s no exception.

I searched a bit and it is written everywhere that doing such a thing is not possible and is against the client/server architecture. I mean if it’s that bad, not standard and not logical, why should Visual Studio let it run (actually make it run) properly?

Mahdieh

Name of the indexer property in C#

December 26th, 2007

Adding an indexer to a class that already owns a property named ‘Item’ makes the code impossible to compile. The compiler and Resharper both give the following error: The type ‘…’ already contains a definition for ‘Item’. Trying to disassemble a class with an indexer in Reflector shows the indexer is named ‘Item’ by default.

After a brief search I found that in VB.net you are capable of naming the indexer property and even this post claimed that in the past C# was also the same. However I found a workaround which lets you define a name for the indexer by assigning an attribute to it. Here is the attribute:


[System.Runtime.CompilerServices.IndexerName("MoreAppropriateName")]


Ilia

Cross-Browser Transparent Image Display

December 18th, 2007

Problem:
I am currently working on a map web site (www.eghlim.com) - city map of Tehran, Iran. The main problem is the big loading time. One thing I found, was the way we handled png images containing transparent parts: Firefox draws them properly, given the background image source, but IE needs a ‘filter’, griven the background image ‘null’.
The previous solution was to search for *.png images by javascript on page load, checking the browser and creating appropriate objects. As shown below:

var sheets = document.styleSheets;
Browser.Detect();  // Browser.js is provided at the end of the post   

if (sheets && Browser.Name == 'Internet Explorer' && Browser.Version <= 6 && Browser.OS == "Windows")
   for (i = 0; i < sheets.length; i++)
      if(sheets[i].rules)
         for(j=0; j < sheets[i].rules.length; j++)
            if(sheets[i].rules.item(j).style.backgroundImage.indexOf(".png") != -1)
            {
               var imageUrl = sheets[i].rules.item(j).style.backgroundImage;
               imageUrl = imageUrl.substring(imageUrl.indexOf('(../') + 4, imageUrl.lastIndexOf(')'));
               sheets[i].rules.item(j).style.backgroundImage = "none";
               sheets[i].rules.item(j).style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + imageUrl + "', sizingMethod='scale')";
            }

The result was disappointing! IE freezes until the scripts are done, and then renders the whole page in 1 sec.

Answer:
I searched for 2 long days to find a better solution, tested several ways and finally found a nice work-around, By Rafael Lima. The essence is to use a css hack to define different styles for different browsers. In this case (showing a transparent png image in Firefox and IE):

.TabMenu .Left
{
   filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='Images/Tab/Left.png', sizingMethod='scale'); background-image:url("../Images/Tab/Left.png");
}
.win.ie .TabMenu .Left
{
   background-image:none;
}

Just adding css_browser_selector.js (as little as 10 lines of code) makes this possible. Fast and clean, isn’t it?

Mahdieh

P.S. Browser.js

Hello world!

December 12th, 2007