0

Basically, I have a TabControl. I am drawing the text for the headers myself so they can be coloured when required. The calls to change colour are on a different thread then the one the TabControl is, so I am using delegates and such for cross-thread operations. Unfortunatly my method is not exactly reliable.

Here is the cross-threading part:

delegate TabControl getTabDelegate();
private TabControl getTab()
{
    if (this.channelTabs.InvokeRequired)
    {
        getTabDelegate d = new getTabDelegate(getTab);
        this.Invoke(d);
        return null;
    }
    else
    {
        return channelTabs;
    }
}

and here is the drawing code:

private void channelTabs_DrawItem(object sender, DrawItemEventArgs e)
{
    try
    {
        TabControl ct = getTab();
        using (Brush br = new SolidBrush(TabColors[ct.TabPages[e.Index]]))
        {
            e.Graphics.FillRectangle(br, e.Bounds);
            SizeF sz = e.Graphics.MeasureString(ct.TabPages[e.Index].Text, e.Font);
            e.Graphics.DrawString(ct.TabPages[e.Index].Text, e.Font, Brushes.Black, e.Bounds.Left + (e.Bounds.Width - sz.Width) / 2, e.Bounds.Top + (e.Bounds.Height - sz.Height) / 2 + 1);

            Rectangle rect = e.Bounds;
            rect.Offset(0, 1);
            rect.Inflate(0, -1);
            e.Graphics.DrawRectangle(Pens.DarkGray, rect);
            e.DrawFocusRectangle();
        }
    }
    catch(Exception err)
    {
        MessageBox.Show(err.Message, "1");
    }
}

As you can see, in some cases getTab(); returns null, which isn't exactly helpful. Is there any more...reliable method of doing this?

Here is the method that is called from the second thread to change the header colour:

private void SetTabHeader(TabPage page, Color color)
{
    TabColors[page] = color;
    channelTabs.Invalidate();
}

Without the cross-threading part, of course, I get exceptions thrown.

And as you can probably imagine, channelTabs is the GUI tab control.

Any help is apperciated, thanks!

-- Oh yea, and if it is actually helpful: private Dictionary TabColors = new Dictionary();

Bizzycola
  • 119
  • 8

3 Answers3

0

All of your drawing is going to need to be done in the UI thread anyway so what I would do is to modify channelTabs_DrawItem(object sender, DrawItemEventArgs e) like so:

channelTabs_DrawItem(object sender, DrawItemEventArgs e)
{
    if(this.InvokeRequired)
    {
        this.BeginInvoke(() => channelTabs_DrawItem(sender,e));
        return;
    }
    TabControl ct = channelTabs;
    ...

which will ensure that you are always running in the right thread and avoids using the potentially dangerous call to Invoke. If you really want to fix the code you are using, what you need to do is to return the value from Invoke like so:

private TabControl getTab()
{
    if (this.channelTabs.InvokeRequired)
    {
        getTabDelegate d = new getTabDelegate(getTab);
        return (TabControl)this.Invoke(d);
    }
    else
    {
        return channelTabs;
    }
}
Yaur
  • 7,333
  • 1
  • 25
  • 36
0

You are overcomplicating things - You need to be using Invoke for the SetTabHeader function, not the drawing event or getTab. Those will only naturally be called from the UI thread as long as you don't do anything silly like call Invalidate from a different thread.

private void SetTabHeader(TabPage page, Color color)
{
    if(this.InvokeRequired) {
        this.Invoke(new Action<TabPage,Color>(SetTabHeader),page,color);
    } else {
        TabColors[page] = color;
        channelTabs.Invalidate();
    }
}
Random832
  • 37,415
  • 3
  • 44
  • 63
0

It is failing because of this...

delegate TabControl getTabDelegate(); 
private TabControl getTab() 
{ 
    if (this.channelTabs.InvokeRequired) 
    { 
        getTabDelegate d = new getTabDelegate(getTab); 
        this.Invoke(d); 
        return null; // <-- The return from your invoke is ignored
    } 
    else 
    { 
        return channelTabs; 
    } 
}

Instead do this:

delegate TabControl getTabDelegate(); 
private TabControl getTab() 
{ 
    if (this.channelTabs.InvokeRequired) 
    { 
        getTabDelegate d = new getTabDelegate(getTab); 
        return this.Invoke(d); // Don't lose the return value from the invoked call
        //return null; 
    } 
    else 
    { 
        return channelTabs; 
    } 
}
Steve Sheldon
  • 6,421
  • 3
  • 31
  • 35