|
Prev: !!!....WHAT IS ISLAM
Next: Holiday lights Christmas lights fairy lights, twinkle lights Sale Decoration Installation Los Angeles, CA
From: tshad on 11 Oct 2008 01:23 I have a Windows App that is doing some work and then writing a "Now Processing..." line to the status line of the window as well as the Textbox on the form. But the problem is that the work is in another class from the main class. So it couldn't access the Status Line or textbox. So what we did was set them up as properties: string IStatusDisplay.Status { get { return Status.Text; } set { Status.Text = value; } } string IStatusDisplay.StatusBar { get { return toolStripStatusLabel1.Text; } set { toolStripStatusLabel1.Text = value; this.Refresh(); } } Then in my code I would call it like: Maintenance.CheckAll(this); And the function is: public static void CheckAll(IStatusDisplay display) { ... display.StatusBar = "Now Processing... " + Path.GetFileName(file); display.Status += file + Environment.NewLine; } This works fine until you try to set it up and call it from a thread - for one thing you can't use the this.Refresh() because you get an error: this.Refresh(); Gets me this error: Cross-thread operation not valid: Control 'FieldNameMapSetup' accessed from a thread other than the thread it was created on. Here I am in another function doing a - "display.StatusBar". It is executing the above property until it hits the "this" statement. or if you access a button on the page like: btnExit.Enabled = true; I get the error: Cross-thread operation not valid: Control 'btnExit' accessed from a thread other than the thread it was created on. I am trying to find out how to access the controls on the Form from either the same class or a different class (which was why we were passing "this" to the other class). This all works fine until you put it in a thread. Thanks, Tom
From: JTC^..^ on 11 Oct 2008 02:47 On 11 Oct, 06:23, "tshad" <t...(a)dslextreme.com> wrote: > I have a Windows App that is doing some work and then writing a "Now > Processing..." line to the status line of the window as well as the Textbox > on the form. > > But the problem is that the work is in another class from the main class. > So it couldn't access the Status Line or textbox. > > So what we did was set them up as properties: > > string IStatusDisplay.Status > { > get > { > return Status.Text; > } > set > { > Status.Text = value; > } > } > > string IStatusDisplay.StatusBar > { > get > { > return toolStripStatusLabel1.Text; > } > set > { > toolStripStatusLabel1.Text = value; > this.Refresh(); > } > } > > Then in my code I would call it like: > > Maintenance.CheckAll(this); > > And the function is: > > public static void CheckAll(IStatusDisplay display) > { > ... > display.StatusBar = "Now Processing... " + > Path.GetFileName(file); > > display.Status += file + Environment.NewLine; > > } > > This works fine until you try to set it up and call it from a thread - for > one thing you can't use the this.Refresh() because you get an error: > > this.Refresh(); > > Gets me this error: > > Cross-thread operation not valid: Control 'FieldNameMapSetup' > accessed from a thread other than the thread it was created on. > > Here I am in another function doing a - "display.StatusBar". It is > executing the above property until it hits the "this" statement. > > or if you access a button on the page like: > > btnExit.Enabled = true; > > I get the error: > Cross-thread operation not valid: Control 'btnExit' accessed > from a thread other than the thread it was created on. > > I am trying to find out how to access the controls on the Form from either > the same class or a different class (which was why we were passing "this" to > the other class). > > This all works fine until you put it in a thread. > > Thanks, > > Tom As a solution to getting the status from one class to another use events. The event notifies the form (or any other classes using the class) of the status change. Call a method to trigger the event.. e.g. OnStatusChanged() Your class would contain an event something like this.... public event EventHandler StatusChanged; private void OnStatusChanged() { EventHandler handler = StatusChanged; if(handler != null) { EventArgs args = new EventArgs(); handler(this, args); } } Your form code will look something like this. Where you instantiate the class subscribe to the event. ..... MyClass class = new MyClass(); class.StatusChanged += new StatusChangedEventHandler(class_StatusChangedEventHandler); .... } private void class_StatusChangedEventHandler(object sender, EventArgs e) { this.lblStatus.Text = ((MyClass)sender).Status; }
From: Peter Duniho on 11 Oct 2008 03:41 On Fri, 10 Oct 2008 22:23:06 -0700, tshad <tfs(a)dslextreme.com> wrote: > [...] > I get the error: > Cross-thread operation not valid: Control 'btnExit' accessed > from a thread other than the thread it was created on. > > I am trying to find out how to access the controls on the Form from > either > the same class or a different class (which was why we were passing > "this" to > the other class). > > This all works fine until you put it in a thread. That's because you need to use Control.Invoke() or Control.BeginInvoke() to execute the code that actually has to touch the control itself. The other reply doesn't really address this. That reply is not a lot different from your own attempt to solve the issue with a property, in that it's just an alternate route to the same code, ultimately with the same problem: it will try to interact with the control from a thread other than the one that owns that control. Without a concise-but-complete code sample to work with, it's difficult to propose a precise change for your particular scenario. But, as an example, you might modify your IStatusDisplay.Status property so that it looks like this: string IStatusDisplay.Status { get { return (string)Status.Invoke((MethodInvoker)delegate { return Status.Text; }); } set { Status.Invoke((MethodInvoker)delegate { Status.Text = value }); } } That will ensure that the code inside the getter and setter that actually interacts with the control instance is always executed on the same thread that created your Status control instance. You can apply a similar approach everywhere that you need to access Control instances from a worker thread. By the way, I note that your code actually uses the getter for the Status property. Again, without a concise-but-complete code sample it's difficult to make precise statements, but on the face of it this looks like something that might be a performance issue. In particular, because of the need to use Control.Invoke(), you tie the execution paths of your two threads together, preventing them from working independently. You might find that it makes more sense for the code that's actually changing the status to maintain its own cache of the current status string, and then just set that on the Status control. At a minimum, that would get rid of one synchronizing round-trip, and you could even use Control.BeginInvoke() in the setter, avoiding synchronization issues there too. Pete
From: tshad on 11 Oct 2008 23:51 "Peter Duniho" <NpOeStPeAdM(a)nnowslpianmk.com> wrote in message news:op.uiunnlsk8jd0ej(a)petes-computer.local... > On Fri, 10 Oct 2008 22:23:06 -0700, tshad <tfs(a)dslextreme.com> wrote: > >> [...] >> I get the error: >> Cross-thread operation not valid: Control 'btnExit' accessed >> from a thread other than the thread it was created on. >> >> I am trying to find out how to access the controls on the Form from >> either >> the same class or a different class (which was why we were passing >> "this" to >> the other class). >> >> This all works fine until you put it in a thread. > > That's because you need to use Control.Invoke() or Control.BeginInvoke() > to execute the code that actually has to touch the control itself. > > The other reply doesn't really address this. That reply is not a lot > different from your own attempt to solve the issue with a property, in > that it's just an alternate route to the same code, ultimately with the > same problem: it will try to interact with the control from a thread other > than the one that owns that control. > > Without a concise-but-complete code sample to work with, it's difficult to > propose a precise change for your particular scenario. But, as an > example, you might modify your IStatusDisplay.Status property so that it > looks like this: [snip] Ok, here is a stripped down sample that consists of 3 buttons, a textbox, statusbar and toolStripStatusLabel. I have 2 files (form1 and Maintenance). In form1 I am calling a function in Maintenance that just writes to the textbox and the toolStripStatusLabel. The first button calls the function without a thread and the second with a thread. I also have a this.Refresh() in the code to display on the page correctly when not running in the thread, but in the thread it gives me the error: Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on. If I comment it out, and run the threading, I get the following when the program tries to write to the textbox (Status) from the Maintenance function: display.Status += file + Environment.NewLine; Calls this to set the value set { Status.Text = value; } And get the error: Cross-thread operation not valid: Control 'Status' accessed from a thread other than the thread it was created on. but I don't get the error when setting the toolStripStatusLabel from the Maintenance function: display.StatusBar = "Now Processing... " + file; Why is that? They are both using the "this" that was passed. Just trying to understand the different ways to do this so I can choose the right way in other situations as well. The 3 files in my project are: form1.cs **************************************************** using System; using System.Threading; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; namespace ControlAccessAcrossClasses { public partial class Form1 : Form, IStatusDisplay { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { toolStripStatusLabel1.Text = "This is a test"; } #region IStatusDisplay Members string IStatusDisplay.Status { get { return Status.Text; } set { Status.Text = value; } } string IStatusDisplay.StatusBar { get { return toolStripStatusLabel1.Text; } set { toolStripStatusLabel1.Text = value; //this.Refresh(); } } #endregion private void WithoutThreadButton_Click(object sender, EventArgs e) { Maintenance.CheckAll(this); } private void WithThreadButton_Click(object sender, EventArgs e) { Thread oThread = new Thread(new ThreadStart(CallWithThread)); oThread.Start(); } private void CallWithThread() { Maintenance.CheckAll(this); } private void Exit_Click(object sender, EventArgs e) { Application.Exit(); } } } *************************************************************** maintenance.cs ************************************************************** using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ControlAccessAcrossClasses { class Maintenance { public static void CheckAll(IStatusDisplay display) { String file = "12345.txt"; display.StatusBar = "Now Processing... " + file; display.Status += file + Environment.NewLine; } } public interface IStatusDisplay { string Status { get; set; } string StatusBar { get; set; } } } ************************************************************** And the form1.designer.cs ************************************************************* namespace ControlAccessAcrossClasses { partial class Form1 { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (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.Status = new System.Windows.Forms.TextBox(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); this.WithThreadButton = new System.Windows.Forms.Button(); this.WithoutThreadButton = new System.Windows.Forms.Button(); this.Exit = new System.Windows.Forms.Button(); this.statusStrip1.SuspendLayout(); this.SuspendLayout(); // // Status // this.Status.Location = new System.Drawing.Point(30, 12); this.Status.Multiline = true; this.Status.Name = "Status"; this.Status.Size = new System.Drawing.Size(364, 286); this.Status.TabIndex = 0; // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripStatusLabel1}); this.statusStrip1.Location = new System.Drawing.Point(0, 347); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(495, 22); this.statusStrip1.TabIndex = 1; this.statusStrip1.Text = "statusStrip1"; // // toolStripStatusLabel1 // this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; this.toolStripStatusLabel1.Size = new System.Drawing.Size(0, 17); // // WithThreadButton // this.WithThreadButton.Location = new System.Drawing.Point(187, 316); this.WithThreadButton.Name = "WithThreadButton"; this.WithThreadButton.Size = new System.Drawing.Size(90, 23); this.WithThreadButton.TabIndex = 2; this.WithThreadButton.Text = "With Thread"; this.WithThreadButton.UseVisualStyleBackColor = true; this.WithThreadButton.Click += new System.EventHandler(this.WithThreadButton_Click); // // WithoutThreadButton // this.WithoutThreadButton.Location = new System.Drawing.Point(30, 316); this.WithoutThreadButton.Name = "WithoutThreadButton"; this.WithoutThreadButton.Size = new System.Drawing.Size(114, 23); this.WithoutThreadButton.TabIndex = 3; this.WithoutThreadButton.Text = "Without Thread"; this.WithoutThreadButton.UseVisualStyleBackColor = true; this.WithoutThreadButton.Click += new System.EventHandler(this.WithoutThreadButton_Click); // // Exit // this.Exit.Location = new System.Drawing.Point(318, 316); this.Exit.Name = "Exit"; this.Exit.Size = new System.Drawing.Size(75, 23); this.Exit.TabIndex = 4; this.Exit.Text = "Exit"; this.Exit.UseVisualStyleBackColor = true; this.Exit.Click += new System.EventHandler(this.Exit_Click); // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(495, 369); this.Controls.Add(this.Exit); this.Controls.Add(this.WithoutThreadButton); this.Controls.Add(this.WithThreadButton); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.Status); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); } #endregion private System.Windows.Forms.TextBox Status; private System.Windows.Forms.StatusStrip statusStrip1; private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel2; private System.Windows.Forms.Button WithThreadButton; private System.Windows.Forms.Button WithoutThreadButton; private System.Windows.Forms.Button Exit; } } ************************************************************** Not sure why there are 2 "partial class form1" classes. Thanks, Tom
From: Peter Duniho on 12 Oct 2008 00:17
On Sat, 11 Oct 2008 20:51:06 -0700, tshad <tfs(a)dslextreme.com> wrote: > [...] > Cross-thread operation not valid: Control 'Status' accessed from a > thread other than the thread it was created on. > > but I don't get the error when setting the toolStripStatusLabel from the > Maintenance function: > > display.StatusBar = "Now Processing... " + file; > > Why is that? They are both using the "this" that was passed. The cross-thread exception doesn't have anything to do with what objects or methods are used per se. The same method accessing the same object would not throw the exception if executed on one thread (the thread that owns the object), and yet would throw the exception if executed on a different thread. > Just trying to understand the different ways to do this so I can choose > the > right way in other situations as well. I'm sorry. Other than the above that I commented on, I didn't see anything appreciably different in your most recent post as compared to the previous one, except for the code. I did make an attempt to answer the question; did you consider that answer at all? If you did and it wasn't applicable, you should explain why. If you didn't, then you should. Pete |