PC Review


Reply
Thread Tools Rate Thread

Borderless controls in a separate thread

 
 
Lionel Johnson
Guest
Posts: n/a
 
      5th Aug 2003
Hi:
I'm trying to put together a multi-threaded app where some
activity (actually moving waveform drawing) is happening
in a number of controls separate from the main thread. I
have tried 2 methods, both of which have some problems:

a. Start a new thread, create a Form containing the
control on which the moving waveform drawing is going to
take place and call Application.Run(frm1). Unfortunately,
if I set the FormBorderStyle to None (with the intention
of having the control fill the form), Application.Run
throws an "Out of Memory" Exception. If I leave the form
with a non-None border and try to set the FormBorderStyle
to None in the Load handler, I get the same exception.
However, if I see the border style to none some time later
(like in a Timer_Elapsed event), I can get rid of the
border with no ill effects.

b. Start a new thread, create a derived control in that
thread, call SetTopLevel(true) on that control and then
call Application.Run(). This works, except that the
resulting control has a Caption border.

Is there a way to create a control in a thread separate
from the main thread, that will receive messages and can
appear immediately without a border?

Thanks
Lionel
 
Reply With Quote
 
 
 
 
Ying-Shen Yu
Guest
Posts: n/a
 
      7th Aug 2003
hello, Lionel,
I wrote a test as you said, and it works properly without that
exception. Could you give me some more information to reproduce the problem?
The following is my test code. You may create a new Windows
Application and replace the code with the following one.
click the New Thread button ,you will see a new form with no border.
In addition , It seems that your problem has some relation with the data
updating mechanism. I hope you can look into it
and give me more information about that exception.
Thanks,

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;

namespace multithread_UI_update
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private Thread thread = null;
private Form2 form = null;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(16, 8);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(144, 32);
this.button1.TabIndex = 0;
this.button1.Text = "New Thread";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(184, 61);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.Closed += new System.EventHandler(this.Form1_Closed);
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}

private void button1_Click(object sender, System.EventArgs e)
{
thread = new Thread(new ThreadStart(ThreadProc));
thread.Start();
}

private void ThreadProc()
{
try
{
form = new Form2();
form.Show();
Application.Run(form);
}
finally
{
form.Dispose();
}
}

private void Form1_Closed(object sender, System.EventArgs e)
{
if (thread.IsAlive)
{
form.Close();
}
}
}
/// <summary>
/// Summary description for Form2.
/// </summary>
public class Form2 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

public Form2()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// TODO: Add any constructor code after InitializeComponent call
//
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(0, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(292, 273);
this.label1.TabIndex = 0;
this.label1.Text = "label1";
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 273);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Name = "Form2";
this.Text = "Form2";
this.ResumeLayout(false);

}
#endregion
}
}


regards,

Best regards,

Ying-Shen Yu[MSFT]
Microsoft Support Engineer

This posting is provided "AS IS" with no warranties, and confers no rights.
You assume all risk for your use. 2001 Microsoft Corporation. All rights
reserved.

 
Reply With Quote
 
Lionel Johnson
Guest
Posts: n/a
 
      7th Aug 2003

Ying-Shen:

Thanks for your prompt reply! Your code does indeed work
and is very close to what I had. Unfortunately, I had code
in the Resize event for the second form that assumed the
presence of a Graphics object that I create in the Load
event. It appears that when the form is created without a
border, a Resize event occurs without the Load event
occuring and hence the Graphics object is not created in
time. In the case of a form with a border, the load event
is executed first and no resize event occurs until after
that point, by which time my Graphics object is available
and all is OK.

Now my only problem is that the individual forms (16 of
them in my application) show up in the Alt-Tab list
although I can successfully stop them appearing in the
task bar (setting the ShowInTaskbar property to false).

Please note that the reason that I am doing this is to
avoid having to marshal the realtime updates in these "sub-
forms" through the main UI thread since user interaction
could (and does) interfere with the realtime drawing.

I had another thought - although high level operations on
controls must be marshalled through the thread on which
the controls have been created, is it permissible to use
control.CreateGraphics()(say on a Panel control) and then
use the returned Graphics object in a separate thread? Of
course this would need to be coupled with an override of
the OnPaint method of the control with no contents
including no call to the base.OnPaint() to avoid
unexpected redrawing of the control. This appears to work,
but I wonder if it is legal. This approach would achieve
the same as with the separate forms previously discussed
without the disadvantage of having them appear in Alt-tab
list.

Once again, thanks for your help

Lionel


>-----Original Message-----
>hello, Lionel,
> I wrote a test as you said, and it works properly

without that
>exception. Could you give me some more information to

reproduce the problem?
> The following is my test code. You may create a new

Windows
>Application and replace the code with the following one.
>click the New Thread button ,you will see a new form with

no border.
>In addition , It seems that your problem has some

relation with the data
>updating mechanism. I hope you can look into it
>and give me more information about that exception.
>Thanks,
>
>using System;
>using System.Drawing;
>using System.Collections;
>using System.ComponentModel;
>using System.Windows.Forms;
>using System.Data;
>using System.Threading;
>
>namespace multithread_UI_update
>{
> /// <summary>
> /// Summary description for Form1.
> /// </summary>
> public class Form1 : System.Windows.Forms.Form
> {
> private System.Windows.Forms.Button

button1;
> private Thread thread = null;
> private Form2 form = null;
> /// <summary>
> /// Required designer variable.
> /// </summary>
> private System.ComponentModel.Container

components = null;
>
> public Form1()
> {
> //
> // Required for Windows Form

Designer support
> //
> InitializeComponent();
>
> //
> // TODO: Add any constructor code

after InitializeComponent call
> //
> }
>
> /// <summary>
> /// Clean up any resources being used.
> /// </summary>
> protected override void Dispose( bool

disposing )
> {
> if( disposing )
> {
> if (components != null)
> {
> components.Dispose

();
> }
> }
> base.Dispose( disposing );
> }
>
> #region Windows Form Designer generated

code
> /// <summary>
> /// Required method for Designer support -

do not modify
> /// the contents of this method with the

code editor.
> /// </summary>
> private void InitializeComponent()
> {
> this.button1 = new

System.Windows.Forms.Button();
> this.SuspendLayout();
> //
> // button1
> //
> this.button1.Location = new

System.Drawing.Point(16, 8);
> this.button1.Name = "button1";
> this.button1.Size = new

System.Drawing.Size(144, 32);
> this.button1.TabIndex = 0;
> this.button1.Text = "New Thread";
> this.button1.Click += new

System.EventHandler(this.button1_Click);
> //
> // Form1
> //
> this.AutoScaleBaseSize = new

System.Drawing.Size(5, 13);
> this.ClientSize = new

System.Drawing.Size(184, 61);
> this.Controls.Add(this.button1);
> this.Name = "Form1";
> this.Text = "Form1";
> this.Closed += new

System.EventHandler(this.Form1_Closed);
> this.ResumeLayout(false);
>
> }
> #endregion
>
> /// <summary>
> /// The main entry point for the

application.
> /// </summary>
> [STAThread]
> static void Main()
> {
> Application.Run(new Form1());
> }
>
> private void button1_Click(object sender,

System.EventArgs e)
> {
> thread = new Thread(new ThreadStart

(ThreadProc));
> thread.Start();
> }
>
> private void ThreadProc()
> {
> try
> {
> form = new Form2();
> form.Show();
> Application.Run(form);
> }
> finally
> {
> form.Dispose();
> }
> }
>
> private void Form1_Closed(object sender,

System.EventArgs e)
> {
> if (thread.IsAlive)
> {
> form.Close();
> }
> }
> }
> /// <summary>
> /// Summary description for Form2.
> /// </summary>
> public class Form2 : System.Windows.Forms.Form
> {
> private System.Windows.Forms.Label label1;
> /// <summary>
> /// Required designer variable.
> /// </summary>
> private System.ComponentModel.Container

components = null;
>
> public Form2()
> {
> //
> // Required for Windows Form

Designer support
> //
> InitializeComponent();
>
> //
> // TODO: Add any constructor code

after InitializeComponent call
> //
> }
>
> /// <summary>
> /// Clean up any resources being used.
> /// </summary>
> protected override void Dispose( bool

disposing )
> {
> if( disposing )
> {
> if(components != null)
> {
> components.Dispose

();
> }
> }
> base.Dispose( disposing );
> }
> #region Windows Form Designer generated

code
> /// <summary>
> /// Required method for Designer support -

do not modify
> /// the contents of this method with the

code editor.
> /// </summary>
> private void InitializeComponent()
> {
> this.label1 = new

System.Windows.Forms.Label();
> this.SuspendLayout();
> //
> // label1
> //
> this.label1.Dock =

System.Windows.Forms.DockStyle.Fill;
> this.label1.Location = new

System.Drawing.Point(0, 0);
> this.label1.Name = "label1";
> this.label1.Size = new

System.Drawing.Size(292, 273);
> this.label1.TabIndex = 0;
> this.label1.Text = "label1";
> //
> // Form2
> //
> this.AutoScaleBaseSize = new

System.Drawing.Size(5, 13);
> this.ClientSize = new

System.Drawing.Size(292, 273);
> this.Controls.Add(this.label1);
> this.FormBorderStyle =

System.Windows.Forms.FormBorderStyle.None;
> this.Name = "Form2";
> this.Text = "Form2";
> this.ResumeLayout(false);
>
> }
> #endregion
> }
>}
>
>
>regards,
>
>Best regards,
>
>Ying-Shen Yu[MSFT]
>Microsoft Support Engineer
>
>This posting is provided "AS IS" with no warranties, and

confers no rights.
>You assume all risk for your use. 2001 Microsoft

Corporation. All rights
>reserved.
>
>.
>

 
Reply With Quote
 
Joe White
Guest
Posts: n/a
 
      8th Aug 2003
> is it permissible to use
> control.CreateGraphics()(say on a Panel control) and then
> use the returned Graphics object in a separate thread?


Absolutely. You can even call CreateGraphics() from the secondary
thread -- the docs point out that CreateGraphics() is one of only four
Control methods that can safely be called from other threads. I'm doing
this in my RPG development kit (still in its infancy, but the
painting-from-a-thread code is well established and working well).

However, you do need to be careful: the control you're painting to
needs to have a valid window handle, from the time you call
CreateGraphics() until the time you dispose the Graphics object.
Otherwise you'll get sporadic exceptions. From my early experience, it
also seems that the control needs to be visible. My code takes the
cautious approach, and doesn't start the thread-based painting until the
control receives its first Paint event, and stops again when the form is
minimized. This may not be necessary, but I don't get exceptions, so
I'm happy. I also have code in the Closing event that waits for any
in-progress paint events to finish, before allowing the form to close.
The devil's in the details, and it took me a while to hit on the right
approach, but my current code has been running successfully for a couple
of weeks with no exceptions on shutdown. So it can be done.

If you'd like to see my code, let me know, and I can e-mail it to you.

 
Reply With Quote
 
Ying-Shen Yu
Guest
Posts: n/a
 
      8th Aug 2003
Hello Lionel,
I think it's feasible to do drawing in a serperate UI worker thread,
but I suggest you creating the graphics object in new thread, because the
handle of the control(say panel) will not be changed after it created, and
the CreateGraphics is thread-safe according to the MSDN, then different
threads,can draw seperately with their own Graphics object. However, you
should be very careful if your worker thread uses any members of the
control,such as handling the resize event,closing event etc.
On your previous thought, I think it's possible to get rid of the border
by modifying the window style to toolwindow style (which will not show in
the taskbar or alt-tab list),and then process the WM_NCCALCSIZE and
WM_NCPAINT message of the form by yourself. In theory, there should be an
style set to make the toolwindow withou a border, but I haven't found it, I
will go on researching it and give you a example in the beginning of next
week, if I find it.

Thanks!


Kind regards,

Ying-Shen Yu [MSFT]
Microsoft Support Engineer

This posting is provided "AS IS" with no warranties, and confers no rights.
You assume all risk for your use. 2001 Microsoft Corporation. All rights
reserved.





From: "Lionel Johnson" <(E-Mail Removed)>
Subject: RE: Borderless controls in a separate thread
Date: Thu, 7 Aug 2003 14:01:11 -0700
Newsgroups: microsoft.public.dotnet.framework.windowsforms



Ying-Shen:

Thanks for your prompt reply! Your code does indeed work
and is very close to what I had. Unfortunately, I had code
in the Resize event for the second form that assumed the
presence of a Graphics object that I create in the Load
event. It appears that when the form is created without a
border, a Resize event occurs without the Load event
occuring and hence the Graphics object is not created in
time. In the case of a form with a border, the load event
is executed first and no resize event occurs until after
that point, by which time my Graphics object is available
and all is OK.

Now my only problem is that the individual forms (16 of
them in my application) show up in the Alt-Tab list
although I can successfully stop them appearing in the
task bar (setting the ShowInTaskbar property to false).

Please note that the reason that I am doing this is to
avoid having to marshal the realtime updates in these "sub-
forms" through the main UI thread since user interaction
could (and does) interfere with the realtime drawing.

I had another thought - although high level operations on
controls must be marshalled through the thread on which
the controls have been created, is it permissible to use
control.CreateGraphics()(say on a Panel control) and then
use the returned Graphics object in a separate thread? Of
course this would need to be coupled with an override of
the OnPaint method of the control with no contents
including no call to the base.OnPaint() to avoid
unexpected redrawing of the control. This appears to work,
but I wonder if it is legal. This approach would achieve
the same as with the separate forms previously discussed
without the disadvantage of having them appear in Alt-tab
list.

Once again, thanks for your help

Lionel


>-----Original Message-----
>hello, Lionel,
> I wrote a test as you said, and it works properly

without that
>exception. Could you give me some more information to

reproduce the problem?
> The following is my test code. You may create a new

Windows
>Application and replace the code with the following one.
>click the New Thread button ,you will see a new form with

no border.
>In addition , It seems that your problem has some

relation with the data
>updating mechanism. I hope you can look into it
>and give me more information about that exception.
>Thanks,
>
>using System;
>using System.Drawing;
>using System.Collections;
>using System.ComponentModel;
>using System.Windows.Forms;
>using System.Data;
>using System.Threading;
>
>namespace multithread_UI_update
>{
> /// <summary>
> /// Summary description for Form1.
> /// </summary>
> public class Form1 : System.Windows.Forms.Form
> {
> private System.Windows.Forms.Button

button1;
> private Thread thread = null;
> private Form2 form = null;
> /// <summary>
> /// Required designer variable.
> /// </summary>
> private System.ComponentModel.Container

components = null;
>
> public Form1()
> {
> //
> // Required for Windows Form

Designer support
> //
> InitializeComponent();
>
> //
> // TODO: Add any constructor code

after InitializeComponent call
> //
> }
>
> /// <summary>
> /// Clean up any resources being used.
> /// </summary>
> protected override void Dispose( bool

disposing )
> {
> if( disposing )
> {
> if (components != null)
> {
> components.Dispose

();
> }
> }
> base.Dispose( disposing );
> }
>
> #region Windows Form Designer generated

code
> /// <summary>
> /// Required method for Designer support -

do not modify
> /// the contents of this method with the

code editor.
> /// </summary>
> private void InitializeComponent()
> {
> this.button1 = new

System.Windows.Forms.Button();
> this.SuspendLayout();
> //
> // button1
> //
> this.button1.Location = new

System.Drawing.Point(16, 8);
> this.button1.Name = "button1";
> this.button1.Size = new

System.Drawing.Size(144, 32);
> this.button1.TabIndex = 0;
> this.button1.Text = "New Thread";
> this.button1.Click += new

System.EventHandler(this.button1_Click);
> //
> // Form1
> //
> this.AutoScaleBaseSize = new

System.Drawing.Size(5, 13);
> this.ClientSize = new

System.Drawing.Size(184, 61);
> this.Controls.Add(this.button1);
> this.Name = "Form1";
> this.Text = "Form1";
> this.Closed += new

System.EventHandler(this.Form1_Closed);
> this.ResumeLayout(false);
>
> }
> #endregion
>
> /// <summary>
> /// The main entry point for the

application.
> /// </summary>
> [STAThread]
> static void Main()
> {
> Application.Run(new Form1());
> }
>
> private void button1_Click(object sender,

System.EventArgs e)
> {
> thread = new Thread(new ThreadStart

(ThreadProc));
> thread.Start();
> }
>
> private void ThreadProc()
> {
> try
> {
> form = new Form2();
> form.Show();
> Application.Run(form);
> }
> finally
> {
> form.Dispose();
> }
> }
>
> private void Form1_Closed(object sender,

System.EventArgs e)
> {
> if (thread.IsAlive)
> {
> form.Close();
> }
> }
> }
> /// <summary>
> /// Summary description for Form2.
> /// </summary>
> public class Form2 : System.Windows.Forms.Form
> {
> private System.Windows.Forms.Label label1;
> /// <summary>
> /// Required designer variable.
> /// </summary>
> private System.ComponentModel.Container

components = null;
>
> public Form2()
> {
> //
> // Required for Windows Form

Designer support
> //
> InitializeComponent();
>
> //
> // TODO: Add any constructor code

after InitializeComponent call
> //
> }
>
> /// <summary>
> /// Clean up any resources being used.
> /// </summary>
> protected override void Dispose( bool

disposing )
> {
> if( disposing )
> {
> if(components != null)
> {
> components.Dispose

();
> }
> }
> base.Dispose( disposing );
> }
> #region Windows Form Designer generated

code
> /// <summary>
> /// Required method for Designer support -

do not modify
> /// the contents of this method with the

code editor.
> /// </summary>
> private void InitializeComponent()
> {
> this.label1 = new

System.Windows.Forms.Label();
> this.SuspendLayout();
> //
> // label1
> //
> this.label1.Dock =

System.Windows.Forms.DockStyle.Fill;
> this.label1.Location = new

System.Drawing.Point(0, 0);
> this.label1.Name = "label1";
> this.label1.Size = new

System.Drawing.Size(292, 273);
> this.label1.TabIndex = 0;
> this.label1.Text = "label1";
> //
> // Form2
> //
> this.AutoScaleBaseSize = new

System.Drawing.Size(5, 13);
> this.ClientSize = new

System.Drawing.Size(292, 273);
> this.Controls.Add(this.label1);
> this.FormBorderStyle =

System.Windows.Forms.FormBorderStyle.None;
> this.Name = "Form2";
> this.Text = "Form2";
> this.ResumeLayout(false);
>
> }
> #endregion
> }
>}
>
>
>regards,
>
>Best regards,
>
>Ying-Shen Yu[MSFT]
>Microsoft Support Engineer
>
>This posting is provided "AS IS" with no warranties, and

confers no rights.
>You assume all risk for your use. 2001 Microsoft

Corporation. All rights
>reserved.
>
>.
>


 
Reply With Quote
 
Lionel Johnson
Guest
Posts: n/a
 
      8th Aug 2003
Joe:

Thanks for the reply and all of the additional useful
information - its probably saved me an awful lot of time.
If it is not too much trouble, I'll take up your offer to
peek at the pertinent parts of your code - it will help to
cement some of the ideas.

Thanks very much

Lionel

>-----Original Message-----
>> is it permissible to use
>> control.CreateGraphics()(say on a Panel control) and

then
>> use the returned Graphics object in a separate thread?

>
>Absolutely. You can even call CreateGraphics() from the

secondary
>thread -- the docs point out that CreateGraphics() is one

of only four
>Control methods that can safely be called from other

threads. I'm doing
>this in my RPG development kit (still in its infancy, but

the
>painting-from-a-thread code is well established and

working well).
>
>However, you do need to be careful: the control you're

painting to
>needs to have a valid window handle, from the time you

call
>CreateGraphics() until the time you dispose the Graphics

object.
>Otherwise you'll get sporadic exceptions. From my early

experience, it
>also seems that the control needs to be visible. My code

takes the
>cautious approach, and doesn't start the thread-based

painting until the
>control receives its first Paint event, and stops again

when the form is
>minimized. This may not be necessary, but I don't get

exceptions, so
>I'm happy. I also have code in the Closing event that

waits for any
>in-progress paint events to finish, before allowing the

form to close.
>The devil's in the details, and it took me a while to hit

on the right
>approach, but my current code has been running

successfully for a couple
>of weeks with no exceptions on shutdown. So it can be

done.
>
>If you'd like to see my code, let me know, and I can e-

mail it to you.
>
>.
>

 
Reply With Quote
 
Lionel Johnson
Guest
Posts: n/a
 
      8th Aug 2003

Ying-Shen:

Thanks for the additional information.

I looked further at your suggestion for using a toolwindow
style. Overrode WndProc() and did an immediate return
without calling the base.WndProc() for WM_NCCALCSIZE and
WM_NCPAINT and the border disappeared while the form
stayed out of the Alt-Tab and taskbar list.

Thanks

Lionel

>-----Original Message-----
>Hello Lionel,
> I think it's feasible to do drawing in a serperate UI

worker thread,
>but I suggest you creating the graphics object in new

thread, because the
>handle of the control(say panel) will not be changed

after it created, and
>the CreateGraphics is thread-safe according to the MSDN,

then different
>threads,can draw seperately with their own Graphics

object. However, you
>should be very careful if your worker thread uses any

members of the
>control,such as handling the resize event,closing event

etc.
> On your previous thought, I think it's possible to get

rid of the border
>by modifying the window style to toolwindow style (which

will not show in
>the taskbar or alt-tab list),and then process the

WM_NCCALCSIZE and
>WM_NCPAINT message of the form by yourself. In theory,

there should be an
>style set to make the toolwindow withou a border, but I

haven't found it, I
> will go on researching it and give you a example in the

beginning of next
>week, if I find it.
>
>Thanks!
>
>
>Kind regards,
>
>Ying-Shen Yu [MSFT]
>Microsoft Support Engineer
>
>This posting is provided "AS IS" with no warranties, and

confers no rights.
>You assume all risk for your use. 2001 Microsoft

Corporation. All rights
>reserved.
>
>
>
>
>
>From: "Lionel Johnson" <(E-Mail Removed)>
>Subject: RE: Borderless controls in a separate thread
>Date: Thu, 7 Aug 2003 14:01:11 -0700
>Newsgroups: microsoft.public.dotnet.framework.windowsforms
>
>
>
>Ying-Shen:
>
>Thanks for your prompt reply! Your code does indeed work
>and is very close to what I had. Unfortunately, I had

code
>in the Resize event for the second form that assumed the
>presence of a Graphics object that I create in the Load
>event. It appears that when the form is created without a
>border, a Resize event occurs without the Load event
>occuring and hence the Graphics object is not created in
>time. In the case of a form with a border, the load event
>is executed first and no resize event occurs until after
>that point, by which time my Graphics object is available
>and all is OK.
>
>Now my only problem is that the individual forms (16 of
>them in my application) show up in the Alt-Tab list
>although I can successfully stop them appearing in the
>task bar (setting the ShowInTaskbar property to false).
>
>Please note that the reason that I am doing this is to
>avoid having to marshal the realtime updates in

these "sub-
>forms" through the main UI thread since user interaction
>could (and does) interfere with the realtime drawing.
>
>I had another thought - although high level operations on
>controls must be marshalled through the thread on which
>the controls have been created, is it permissible to use
>control.CreateGraphics()(say on a Panel control) and then
>use the returned Graphics object in a separate thread? Of
>course this would need to be coupled with an override of
>the OnPaint method of the control with no contents
>including no call to the base.OnPaint() to avoid
>unexpected redrawing of the control. This appears to

work,
>but I wonder if it is legal. This approach would achieve
>the same as with the separate forms previously discussed
>without the disadvantage of having them appear in Alt-tab
>list.
>
>Once again, thanks for your help
>
>Lionel
>
>
>>-----Original Message-----
>>hello, Lionel,
>> I wrote a test as you said, and it works properly

>without that
>>exception. Could you give me some more information to

>reproduce the problem?
>> The following is my test code. You may create a

new
>Windows
>>Application and replace the code with the following one.
>>click the New Thread button ,you will see a new form

with
>no border.
>>In addition , It seems that your problem has some

>relation with the data
>>updating mechanism. I hope you can look into it
>>and give me more information about that exception.
>>Thanks,
>>
>>using System;
>>using System.Drawing;
>>using System.Collections;
>>using System.ComponentModel;
>>using System.Windows.Forms;
>>using System.Data;
>>using System.Threading;
>>
>>namespace multithread_UI_update
>>{
>> /// <summary>
>> /// Summary description for Form1.
>> /// </summary>
>> public class Form1 : System.Windows.Forms.Form
>> {
>> private System.Windows.Forms.Button

>button1;
>> private Thread thread = null;
>> private Form2 form = null;
>> /// <summary>
>> /// Required designer variable.
>> /// </summary>
>> private System.ComponentModel.Container

>components = null;
>>
>> public Form1()
>> {
>> //
>> // Required for Windows Form

>Designer support
>> //
>> InitializeComponent();
>>
>> //
>> // TODO: Add any constructor code

>after InitializeComponent call
>> //
>> }
>>
>> /// <summary>
>> /// Clean up any resources being used.
>> /// </summary>
>> protected override void Dispose( bool

>disposing )
>> {
>> if( disposing )
>> {
>> if (components != null)
>> {
>> components.Dispose

>();
>> }
>> }
>> base.Dispose( disposing );
>> }
>>
>> #region Windows Form Designer generated

>code
>> /// <summary>
>> /// Required method for Designer support -

>do not modify
>> /// the contents of this method with the

>code editor.
>> /// </summary>
>> private void InitializeComponent()
>> {
>> this.button1 = new

>System.Windows.Forms.Button();
>> this.SuspendLayout();
>> //
>> // button1
>> //
>> this.button1.Location = new

>System.Drawing.Point(16, 8);
>> this.button1.Name = "button1";
>> this.button1.Size = new

>System.Drawing.Size(144, 32);
>> this.button1.TabIndex = 0;
>> this.button1.Text = "New Thread";
>> this.button1.Click += new

>System.EventHandler(this.button1_Click);
>> //
>> // Form1
>> //
>> this.AutoScaleBaseSize = new

>System.Drawing.Size(5, 13);
>> this.ClientSize = new

>System.Drawing.Size(184, 61);
>> this.Controls.Add(this.button1);
>> this.Name = "Form1";
>> this.Text = "Form1";
>> this.Closed += new

>System.EventHandler(this.Form1_Closed);
>> this.ResumeLayout(false);
>>
>> }
>> #endregion
>>
>> /// <summary>
>> /// The main entry point for the

>application.
>> /// </summary>
>> [STAThread]
>> static void Main()
>> {
>> Application.Run(new Form1());
>> }
>>
>> private void button1_Click(object sender,

>System.EventArgs e)
>> {
>> thread = new Thread(new ThreadStart

>(ThreadProc));
>> thread.Start();
>> }
>>
>> private void ThreadProc()
>> {
>> try
>> {
>> form = new Form2();
>> form.Show();
>> Application.Run(form);
>> }
>> finally
>> {
>> form.Dispose();
>> }
>> }
>>
>> private void Form1_Closed(object sender,

>System.EventArgs e)
>> {
>> if (thread.IsAlive)
>> {
>> form.Close();
>> }
>> }
>> }
>> /// <summary>
>> /// Summary description for Form2.
>> /// </summary>
>> public class Form2 : System.Windows.Forms.Form
>> {
>> private System.Windows.Forms.Label label1;
>> /// <summary>
>> /// Required designer variable.
>> /// </summary>
>> private System.ComponentModel.Container

>components = null;
>>
>> public Form2()
>> {
>> //
>> // Required for Windows Form

>Designer support
>> //
>> InitializeComponent();
>>
>> //
>> // TODO: Add any constructor code

>after InitializeComponent call
>> //
>> }
>>
>> /// <summary>
>> /// Clean up any resources being used.
>> /// </summary>
>> protected override void Dispose( bool

>disposing )
>> {
>> if( disposing )
>> {
>> if(components != null)
>> {
>> components.Dispose

>();
>> }
>> }
>> base.Dispose( disposing );
>> }
>> #region Windows Form Designer generated

>code
>> /// <summary>
>> /// Required method for Designer support -

>do not modify
>> /// the contents of this method with the

>code editor.
>> /// </summary>
>> private void InitializeComponent()
>> {
>> this.label1 = new

>System.Windows.Forms.Label();
>> this.SuspendLayout();
>> //
>> // label1
>> //
>> this.label1.Dock =

>System.Windows.Forms.DockStyle.Fill;
>> this.label1.Location = new

>System.Drawing.Point(0, 0);
>> this.label1.Name = "label1";
>> this.label1.Size = new

>System.Drawing.Size(292, 273);
>> this.label1.TabIndex = 0;
>> this.label1.Text = "label1";
>> //
>> // Form2
>> //
>> this.AutoScaleBaseSize = new

>System.Drawing.Size(5, 13);
>> this.ClientSize = new

>System.Drawing.Size(292, 273);
>> this.Controls.Add(this.label1);
>> this.FormBorderStyle =

>System.Windows.Forms.FormBorderStyle.None;
>> this.Name = "Form2";
>> this.Text = "Form2";
>> this.ResumeLayout(false);
>>
>> }
>> #endregion
>> }
>>}
>>
>>
>>regards,
>>
>>Best regards,
>>
>>Ying-Shen Yu[MSFT]
>>Microsoft Support Engineer
>>
>>This posting is provided "AS IS" with no warranties, and

>confers no rights.
>>You assume all risk for your use. 2001 Microsoft

>Corporation. All rights
>>reserved.
>>
>>.
>>

>
>.
>

 
Reply With Quote
 
Joe White
Guest
Posts: n/a
 
      9th Aug 2003
I'm posting this in the NG in case the code would help anyone else out...

I just posted my multithreaded-painting code on my Web site. Go to
http://www.excastle.com/Tyler/ and scroll to the bottom of the page, and
click "Download Tyler technology preview demo #1".

The parts that will probably interest you most are the SessionWindow and
the SessionViewer. The SessionWindow mostly just passes some
notifications (minimizing, closing) to the SessionViewer. The
SessionViewer (which is a Control parented to the SessionWindow) does
all the work of running a timer, locking critical sections as needed,
etc. It has a property which I somewhat whimsically named "Happy",
which determines whether painting can safely occur at this point in
time: if the viewer is happy, it's safe for threads to paint. Happy
starts off false, gets set to true when the SessionViewer gets its first
paint message, then gets set back to false when the SessionWindow is
minimized or closed. The Happy setter has a lock, which makes sure the
flag won't actually get changed until any paint-operation-in-progress
runs to completion. In turn, this ensures that the form can't be closed
while painting is in progress.

All my rendering is double-buffered. The locking mechanisms reflect
this -- there are more stringent critical-section checks around the
copy-to-screen operation, since that's where the cross-thread
CreateGraphics() actually happens.

If you have any questions about the code (I'm sure I could have
documented some of it better, especially as regards *why* I did things a
certain way), let me know -- either post here, or e-mail me. Hope this
helps!


Lionel Johnson wrote:
> Joe:
>
> Thanks for the reply and all of the additional useful
> information - its probably saved me an awful lot of time.
> If it is not too much trouble, I'll take up your offer to
> peek at the pertinent parts of your code - it will help to
> cement some of the ideas.
>
> Thanks very much
>
> Lionel
>
>
>>-----Original Message-----
>>
>>> is it permissible to use
>>> control.CreateGraphics()(say on a Panel control) and
>>> then use the returned Graphics object in a separate thread?

>>
>> Absolutely. You can even call CreateGraphics() from the
>> secondary thread -- the docs point out that CreateGraphics()
>> is one of only four Control methods that can safely be
>> called from other threads. I'm doing this in my RPG
>> development kit (still in its infancy, but the
>> painting-from-a-thread code is well established and
>> working well).
>>
>> However, you do need to be careful: the control you're
>> painting to needs to have a valid window handle, from
>> the time you call CreateGraphics() until the time you
>> dispose the Graphics object. Otherwise you'll get
>> sporadic exceptions. From my early experience, it
>> also seems that the control needs to be visible. My
>> code takes the cautious approach, and doesn't start the
>> thread-based painting until the control receives its
>> first Paint event, and stops again when the form is
>> minimized. This may not be necessary, but I don't get
>> exceptions, so I'm happy. I also have code in the
>> Closing event that waits for any in-progress paint
>> events to finish, before allowing the form to close.
>>
>> The devil's in the details, and it took me a while to
>> hit on the right approach, but my current code has been
>> running successfully for a couple of weeks with no
>> exceptions on shutdown. So it can be done.
>>
>> If you'd like to see my code, let me know, and I can e-
>> mail it to you.


 
Reply With Quote
 
Lionel Johnson
Guest
Posts: n/a
 
      11th Aug 2003
Joe:
Thanks - much appreciated
Lionel

>-----Original Message-----
>I'm posting this in the NG in case the code would help

anyone else out...
>
>I just posted my multithreaded-painting code on my Web

site. Go to
>http://www.excastle.com/Tyler/ and scroll to the bottom

of the page, and
>click "Download Tyler technology preview demo #1".
>

[snip]
 
Reply With Quote
 
 
 
Reply

Thread Tools
Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Updating Form Controls from Separate Worker Thread - Must all code be in the same source file? snaga.ohme@gmail.com Microsoft Dot NET Framework Forms 3 24th Jun 2007 02:25 AM
Moving a borderless form that contains child controls Flyte Microsoft Dot NET Framework Forms 1 14th Feb 2007 09:07 PM
Hosting user controls on a separate thread. Frosty Madness Microsoft Dot NET Framework 1 23rd Oct 2005 01:02 PM
Keyboard focus issue - form in separate thread/separate appdomain =?Utf-8?B?REpIdWdoZXM=?= Microsoft Dot NET Framework 0 13th Mar 2005 12:07 AM
Controls created on one thread cannot be parented to a control on a different thread gregbacchus@hotmail.com Microsoft Dot NET Framework Forms 8 19th Oct 2004 03:23 PM


Features
 

Advertising
 

Newsgroups
 


All times are GMT +1. The time now is 02:13 PM.