A stopwatch program is also one of my heart, because always want to write such a thing, but always to the GUI side to think, so it is more daunting, may be in school when learning the Sequela of MFC, but when I want to use win form today (say or first write win form) write such a thing when , incredibly easy.
So, do not do not scary, fear is you do not do, because you do not do, you will never know you can do it. It turns out that most of the things you hesitate to do can actually be done.
Although the simple function of a stopwatch is achieved successfully, it starts the timing and stops. But it raises a question about win form and the C # thread.
Next one, let's talk about the stopwatch class implementation
namespaceutils{ Public classTime {Private int_minute;Private int_second;Private BOOL_flag;//thread ID PrivateThread _timingthread =NULL; PublicTime () { This. _minute = 0; This. _second = 0; This. _flag =true; }// <summary> // Start timing // </summary> Public voidStart () {if(_timingthread = =NULL) {_timingthread =NewThread (NewThreadStart (Addsecond)); _timingthread.start (); } }// <summary> /// thread execution method // </summary> Private voidAddsecond () { while(_flag) {thread.sleep (1000);if( This. _second = = 59) { This. _minute++; This. _second = 0; }Else{ This. _second++; } } }// <summary> /// formatted display of timing results // </summary> // <returns></returns> Public stringFormattimeresult () {stringminute =string. Empty;stringSecond =string. Empty;if( This. _minute <) {minute ="0"+ This. _minute. ToString (); }Else{minute = This. _minute. ToString (); }if( This. _second <) {second ="0"+ This. _second. ToString (); }Else{second = This. _second. ToString (); }returnMinute +":"+ second; }// <summary> // Stop // </summary> Public voidStop () { This. _flag =false; }// <summary> /// return to 0 operation // </summary> Public voidZero () { This. _minute = 0; This. _second = 0; } }}
The implementation of the stopwatch is still relatively simple, feel this write, but also convenient to do the extension later.
Let's talk about win form
This is how the form is, a label, a two button
At first, I wrote a code like this
Public Partial classForm1:form {PrivateTime MTime =NULL;PrivateThread Mdisplaythread =NULL; PublicForm1 () {InitializeComponent (); MTime =NewTime ();//Instantiation Stopwatch class}Private voidButton_start_click (Objectsender, EventArgs e) {Mtime.start (); Mdisplaythread =NewThread (NewThreadStart (Displaycurrenttime)); Mdisplaythread.start (); Button_start. Enabled =false; } Public voidDisplaycurrenttime () { while(true) {thread.sleep (1000); Label_time.text = Mtime.formattimeresult ();//real-time update of label tagsConsole.WriteLine (' {0} ', Mtime.formattimeresult ()); } }Private voidButton_stop_click (Objectsender, EventArgs e) {mtime.stop (); Button_start. Enabled =true; }}
There is nothing wrong with this writing, when you click the "Start Timer" button and create a thread that is used to update the display timings on the label every second.
However, then reported a such error:cross-thread operation not Valid:control ' label_time ' accessed from a thread and other than the thread it was created on.
On the web, this error appears to be common, and the reason for this error on MSDN is that this error occurs when you try to update a win form from a separate thread .
Check, that is, the control properties on the win form want to be modified, can only be called in the creation control of the thread, cannot be outside the thread is called. and the above
Label_time.text = Mtime.formattimeresult ();
This code is exactly what happens in the newly created thread, so it will be an error.
The solution is to use Delegate (delegate) plus control. Invoke to federate the implementation. Here's a look at the implementation section
Public Partial classForm1:form {PrivateTime MTime =NULL;PrivateThread Mdisplaythread =NULL; Public Delegate voidUpdatelabel ();//Declare a delegate PublicUpdatelabel Updatelabel;//define a delegate PublicForm1 () {InitializeComponent (); MTime =NewTime (); Updatelabel =NewUpdatelabel (UpdateTime);//instantiation of a delegate object}Private voidButton_start_click (Objectsender, EventArgs e) {Mtime.start (); Mdisplaythread =NewThread (NewThreadStart (Displaytimefunc)); Mdisplaythread.start (); Button_start. Enabled =false; }// <summary> /// thread execution method // </summary> Public voidDisplaytimefunc () { while(true) {thread.sleep (1000); This. Invoke ( This. Updatelabel); } }// <summary> /// Refresh the label individually // </summary> Public voidUpdateTime () {label_time.text = Mtime.formattimeresult (); }Private voidButton_stop_click (Objectsender, EventArgs e) {mtime.stop (); Button_start. Enabled =true; } }
In this code, the Mdisplaythread thread executes the Displaytimefunc method, and the Displaytimefunc method is actually updating the label, The difference is that the Control.Invoke method is used, which is not to say that changes to the properties of the control are performed on the line thread of the control being created? Now it looks as if it's still the same. That's because we don't know what Control.Invoke is. the explanation on MSDN is that the specified delegate executes on the thread that owns the underlying window handle for this control. OK, I see, This.updatelabel. This delegate is finally executed in a window-created thread.
Think back, in fact, the idea is relatively simple, is to change the control properties of the operation in a method, and then write a delegate, and then write a thread, the execution method of the threads call this delegate OK.
But this is not all over, there is a small problem, that is, when I want to close the form after the time, I found that the error has started:
Invoke or BeginInvoke cannot is called on a control until the window handle have been created.
The study found that the cause of this problem is that we "did not wipe pp after the toilet," the above code does not have an action to mdisplaythread this thread has done a termination action.
So we also need to add the following actions
Private void Form1_formclosing (object sender, FormClosingEventArgs e) { mdisplaythread.abort (); }
This is complete and terminates the thread before closing the Form1 form.
When I was doing this little thing, I got to know some of the delegate and Control.Invoke and threading knowledge points. I will find a time to take a good look at this part, and strive to summarize what points out.