在线程中改变控件的visible属性会出现神奇BUG,求此BU*生的原因

时间:2022-04-02 07:35:59
环境:
VS2010, .net 4.0

准备工作:
1,初始化时将控件的visible属性置为false,或者在form的构造函数中将控件的visible属性置为false
2,允许直接在线程中修改控件,在form的构造函数中添加如下代码: Control.CheckForIllegalCrossThreadCalls = false;

现象描述:
在线程中将控件的visible属性置为true,则程序死机.


相关调研:
1,将控件的visible初始化属性改为true,则在线程中可以动态改变控件的visible属性,不会有任何问题.
2,通过button点击,或者用timer触发来改变控件的visible属性也不会有任何问题.

求大神解答:
初始化时将控件的visible属性置为false,为什么就不能再在线程中动态改变visible属性?

以下是代码
form1.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public bool mVal = true;
    //    static Int32 ii = 0;
        public Form1()
        {
            InitializeComponent();
            Control.CheckForIllegalCrossThreadCalls = false;

            Thread tmp = new Thread(setvis);
            tmp.Start();
        }
      
        private void button1_Click(object sender, EventArgs e)
        {
            mVal = true;
          //  label1.Visible = mVal;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            mVal = false;
        //    label1.Visible = mVal;
        }

        private void setvis()
        {

            while (true)
            {
             //   ii++;
            //    label1.Text = ii.ToString();
                Thread.Sleep(100);
                label1.Visible = mVal;

            }

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //label1.Visible = !label1.Visible;
        }
    }
}

form1.designer.cs

namespace WindowsFormsApplication1
{
    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.components = new System.ComponentModel.Container();
            this.label1 = new System.Windows.Forms.Label();
            this.button1 = new System.Windows.Forms.Button();
            this.button2 = new System.Windows.Forms.Button();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(48, 41);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(65, 12);
            this.label1.TabIndex = 0;
            this.label1.Text = "sadasdfsaf";
            this.label1.Visible = false;
            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(191, 66);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(75, 23);
            this.button1.TabIndex = 1;
            this.button1.Text = "button1";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            // 
            // button2
            // 
            this.button2.Location = new System.Drawing.Point(191, 111);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(75, 23);
            this.button2.TabIndex = 2;
            this.button2.Text = "button2";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            // 
            // timer1
            // 
            this.timer1.Enabled = true;
            this.timer1.Interval = 1000;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(284, 262);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.label1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Button button1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.Timer timer1;
    }
}

4 个解决方案

#1


上述代码,将form1.designer.cs中的代码:“this.label1.Visible = false;”删除,则不会有任何问题。

或者将form1.cs中的代码
        private void setvis()
        {

            while (true)
            {
             //   ii++;
            //    label1.Text = ii.ToString();
                Thread.Sleep(100);
                label1.Visible = mVal;

            }

        }
修改为
        private void setvis()
        {

            while (true)
            {
             //   ii++;
            //    label1.Text = ii.ToString();
                label1.Visible = mVal;// mVal初始化为true,先设置为true,再睡眠
                Thread.Sleep(100);

            }

        }
也不会有任何问题,神奇了,求解答。

#2


线程中修改主界面的东西,必须使用委托。


第一种:
 private delegate void CustomAction();
使用:
 this.FindForm().Invoke(new CustomAction(delegate()
 { 
//代码
 }));

第二种:
private delegate void CustomAction();

void Method()
{
  if(!this.InvokeRequired)
{
//代码
}
else
{
 this.BeginInvoke(new CustomAction(Method));
}
}

如果带参数则加上参数即可。

#3


使用委托肯定没问题。
就是不知道为什么控件的visible属性会对初始化值有要求。难道是MS的BUG?

#4


摘录msdn内容:
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。 

你既然在以不安全地方式操作控件,怎么能要求它要“正常”工作呢?

并不仅仅是visible属性,其他很多属性很多方法都可能出现各种各样你想得到想不到的问题

#1


上述代码,将form1.designer.cs中的代码:“this.label1.Visible = false;”删除,则不会有任何问题。

或者将form1.cs中的代码
        private void setvis()
        {

            while (true)
            {
             //   ii++;
            //    label1.Text = ii.ToString();
                Thread.Sleep(100);
                label1.Visible = mVal;

            }

        }
修改为
        private void setvis()
        {

            while (true)
            {
             //   ii++;
            //    label1.Text = ii.ToString();
                label1.Visible = mVal;// mVal初始化为true,先设置为true,再睡眠
                Thread.Sleep(100);

            }

        }
也不会有任何问题,神奇了,求解答。

#2


线程中修改主界面的东西,必须使用委托。


第一种:
 private delegate void CustomAction();
使用:
 this.FindForm().Invoke(new CustomAction(delegate()
 { 
//代码
 }));

第二种:
private delegate void CustomAction();

void Method()
{
  if(!this.InvokeRequired)
{
//代码
}
else
{
 this.BeginInvoke(new CustomAction(Method));
}
}

如果带参数则加上参数即可。

#3


使用委托肯定没问题。
就是不知道为什么控件的visible属性会对初始化值有要求。难道是MS的BUG?

#4


摘录msdn内容:
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。 

你既然在以不安全地方式操作控件,怎么能要求它要“正常”工作呢?

并不仅仅是visible属性,其他很多属性很多方法都可能出现各种各样你想得到想不到的问题