C# - 在创建新控件的线程仍在运行时关闭表单

时间:2016-09-16 14:41:45

标签: c# multithreading winforms

我试图在辅助线程上添加自定义控件,但是当我在线程仍在运行时关闭窗口时,我得到了以下异常:

  

在窗口之前无法在控件上调用Invoke或BeginInvoke   句柄已经创建。

我不知道获取此异常的原因是由于错误的线程还是因为我在线程仍在运行时关闭窗口。

这是我得到例外的代码:

panelWall.Invoke(new Action(() =>
            {
                postControl = new FBPostUserControl(m_LoggedInUser.Name, m_LoggedInUser.ImageNormal, post.CreatedTime);
                postControl.PostBody = post.Message;
                postControl.Image = postImage;
                postControl.Dock = DockStyle.Top;
                postControl.BringToFront();
            }));

这是我的自定义控件的代码:

public partial class FBPostUserControl : UserControl
{
    private readonly string m_UserName = string.Empty;
    private readonly Image m_UserProfileImage = null;
    private readonly DateTime? m_DatePosted = null;
    private Image m_Image = null;
    private string m_PostBody = string.Empty;

    public string UserName
    {
        get { return m_UserName; }
    }

    public DateTime? DatePosted
    {
        get { return m_DatePosted; }
    }

    public Image Image
    {
        get { return m_Image; }
        set
        {
            if (value == null)
            {
                pictureBoxImage.Visible = false;
            }
            else
            {
                pictureBoxImage.Visible = true;
                pictureBoxImage.Image = value;
                updateImageSize();
            }
        }
    }

    private void updateImageSize()
    {
        if (pictureBoxImage.Image != null)
        {
            double ratio = pictureBoxImage.Image.Width / pictureBoxImage.Image.Height;
            pictureBoxImage.Height = (int)(pictureBoxImage.Width / ratio);
            pictureBoxImage.SizeMode = PictureBoxSizeMode.Zoom;
        }
    }

    public string PostBody
    {
        get { return m_PostBody; }
        set
        {
            if (string.IsNullOrWhiteSpace(value) == false)
            {
                labelPostBody.Visible = true;
                labelPostBody.Text = value;
            }
            else
            {
                labelPostBody.Visible = false;
            }
        }
    }

    public Image UserProfileImage
    {
        get { return m_UserProfileImage; }
    }

    public FBPostUserControl(string i_Name, Image i_ProfileImage, DateTime? i_PostDate)
    {
        InitializeComponent();
        m_UserName = i_Name;
        m_UserProfileImage = i_ProfileImage;
        m_DatePosted = i_PostDate;

        refreshHeader();
    }

    private void refreshHeader()
    {
        pictureBoxUserImage.Image = m_UserProfileImage;
        labelName.Text = m_UserName;

        if (labelDate != null)
        {
            labelDate.Text = m_DatePosted.ToString();
        }
        else
        {
            labelDate.Visible = false;
        }
    }
}

1 个答案:

答案 0 :(得分:1)

首先,我没有看到您在新线程中启动操作,因为Invoke方法只是将操作发布到UI线程中的调度程序队列。

因此,您的代码中没有真正的多线程,但在执行操作时,用户有机会在调度程序队列中发布Windows消息以关闭表单,并且可以在下次调用之前处理它。因此,为了避免例外,请在下次调用之前检查表单是否已关闭。

顺便说一句,我认为启动一个新线程只是为了更新图形元素没有真正的优势,因为最后,它们必须在UI线程中更新,而你只是花时间和资源在往返。

如果你有一个很长的图形操作并且你的目标是.NET Framework 4.5或更高版本,那么标准的方法是等待长图形操作的异步方法,并在内部等待任务间隔的Task.Yield()给出用户有机会取消,关闭窗口等。

基本上,Task.Yield()将方法延续发布到UI调度程序,当它返回时,您可以检查表单并在表单关闭时取消长操作:

    async Task LongJustGraphicsOperation()
    {
        while (true)
        {
            //do some work and give a pause
            await Task.Yield();
            if (formIsClosed) break;
        }
    }

Task.Yield()是旧VB doevents的任务版本。

请注意。检查winform是否已关闭Detect when a form has been closed

有点棘手