【问题标题】:Cannot access a disposed object "MonthCalendar"无法访问已处置的对象“MonthCalendar”
【发布时间】:2011-05-12 09:31:20
【问题描述】:

我有这段代码来创建并显示一个带有月历控件的表单。

private void showcalendar_Click(object sender, EventArgs e)
{
    ShowCalendar();
}

void ShowCalendar()
{
    DateTime current5 = DateTime.Now.AddDays(-5);

    MonthCalendar cal = new MonthCalendar();
    Panel panel = new Panel();
    Form f = new Form();

    cal.MaxSelectionCount = 1;
    cal.SetDate(current5);
    cal.DateSelected += new DateRangeEventHandler(DateSelected);
    cal.ShowToday = true;
    panel.Width = cal.Width;
    panel.Height = cal.Height;
    panel.BorderStyle = BorderStyle.FixedSingle;
    panel.Controls.Add(cal);
    f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    f.ShowInTaskbar = false;
    f.Size = panel.Size;
    f.Location = MousePosition;
    f.StartPosition = FormStartPosition.Manual;
    f.Controls.Add(panel);
    f.Deactivate += delegate { f.Close(); };
    f.Show();
}

void DateSelected(object sender, DateRangeEventArgs e)
{
    MonthCalendar cal = (MonthCalendar)sender;
    Form f = cal.FindForm();
    f.Close();
}

当我调用 ShowCalendar 月历控件时,我可以在其中选择日期。问题是,当我单击某个区域(描绘当前日期的最低区域)时,出现异常 - “无法访问已处置的对象。对象名称:'MonthCalendar'。”我根本不知道这个异常是如何产生的以及如何摆脱它。也许你有什么想法?

我的应用程序不是多线程的,只是带有调用 ShowCalendar 函数的按钮的简单表单。

【问题讨论】:

  • 堆栈转储说明了什么?
  • 您正在关闭表单,所以它会与表单一起处理!
  • 您是否粘贴了两次堆栈跟踪?没有复制。
  • Visual Studio 2008 完全重现了这一点

标签: c# winforms monthcalendar


【解决方案1】:

一个有趣的问题:我能找到使其工作的唯一方法是将弹出窗体保留为主窗体的属性并使用 Hide() 而不是 Close()。

public partial class Form1 : Form
    {
        Form f = new Form();

        public Form1()
        {
            InitializeComponent();
        }

        private void showcalendar_Click(object sender, EventArgs e)
        {
            ShowCalendar();
        }

        void ShowCalendar()
        {
            DateTime current5 = DateTime.Now.AddDays(-5);

            MonthCalendar cal = new MonthCalendar();
            Panel panel = new Panel();

            cal.MaxSelectionCount = 1;
            cal.SetDate(current5);
            cal.DateSelected += new DateRangeEventHandler(DateSelected);
            cal.ShowToday = true;
            panel.Width = cal.Width;
            panel.Height = cal.Height;
            panel.BorderStyle = BorderStyle.FixedSingle;
            panel.Controls.Add(cal);
            f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
            f.ShowInTaskbar = false;
            f.Size = panel.Size;
            f.Location = MousePosition;
            f.StartPosition = FormStartPosition.Manual;
            f.Controls.Add(panel);
            f.Deactivate += delegate { f.Hide(); };
            f.Show();
        }

        void DateSelected(object sender, DateRangeEventArgs e)
        {
            DateTime selection = e.Start;
            Console.WriteLine("Selected: {0}", selection.ToLongDateString());

            this.Activate(); // Forces popup to de-activate
        }

    }

【讨论】:

    【解决方案2】:

    解决方法:在关闭托管此 MonthsCalendar 的表单之前,从其父级中删除 MonthCalendar。所以改变是添加行cal.Parent.Controls.Remove(cal)。 DateSelected 方法变为:

    void DateSelected(object sender, DateRangeEventArgs e)
    {
        MonthCalendar cal = (MonthCalendar)sender;
        Form f = cal.FindForm();
        cal.Parent.Controls.Remove(cal);
        f.Close();
    }
    

    【讨论】:

      【解决方案3】:

      我有一个快速解决方案,只需 3 个步骤。

      修复和增强:

      • 不同版本windows下固定矩形动态大小。
      • 验证主体形式是否位于最顶层。
      • 从内存中卸载日历表单没有错误。
      • MonthCalendarMaskedTextBox 控件之间的良好行为

      步骤:

      1) 创建一个新的 windows 窗体应用程序,查看 form1 中的代码并将所有文本替换为:

      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.Globalization;
      using System.Threading;
      
      namespace WindowsFormsApplication1
      {
          public partial class Form1 : Form
          {
              private System.Windows.Forms.CheckBox chkShowWeeksNumbers;
              private System.Windows.Forms.CheckBox chkThisFormTopMost;
              private System.Windows.Forms.MaskedTextBox maskedInputBox;
              private System.Windows.Forms.Button btnShowFloatingCalendar;
      
              public Form1()
              {
                  this.chkShowWeeksNumbers = new System.Windows.Forms.CheckBox();
                  this.chkThisFormTopMost = new System.Windows.Forms.CheckBox();
                  this.maskedInputBox = new System.Windows.Forms.MaskedTextBox();
                  this.btnShowFloatingCalendar = new System.Windows.Forms.Button();
                  this.SuspendLayout();
                  // 
                  // chkShowNumbersOfWeeks
                  // 
                  this.chkShowWeeksNumbers.AutoSize = true;
                  this.chkShowWeeksNumbers.Location = new System.Drawing.Point(18, 116);
                  this.chkShowWeeksNumbers.Name = "chkShowWeeksNumbers";
                  this.chkShowWeeksNumbers.Size = new System.Drawing.Size(137, 17);
                  this.chkShowWeeksNumbers.TabIndex = 1;
                  this.chkShowWeeksNumbers.Text = "Show number of weeks";
                  this.chkShowWeeksNumbers.UseVisualStyleBackColor = true;
                  // 
                  // chkThisFormTopMost
                  // 
                  this.chkThisFormTopMost.AutoSize = true;
                  this.chkThisFormTopMost.Location = new System.Drawing.Point(18, 139);
                  this.chkThisFormTopMost.Name = "chkThisFormTopMost";
                  this.chkThisFormTopMost.Size = new System.Drawing.Size(124, 17);
                  this.chkThisFormTopMost.TabIndex = 2;
                  this.chkThisFormTopMost.Text = "This form TopMost";
                  this.chkThisFormTopMost.UseVisualStyleBackColor = true;
                  this.chkThisFormTopMost.CheckedChanged += new EventHandler(chkThisFormTopMost_CheckedChanged);
                  // 
                  // maskedInputBox
                  // 
                  this.maskedInputBox.Location = new System.Drawing.Point(18, 53);
                  this.maskedInputBox.Mask = "00/00/0000 00:00";
                  this.maskedInputBox.Name = "maskedInputBox";
                  this.maskedInputBox.Size = new System.Drawing.Size(115, 20);
                  this.maskedInputBox.TabIndex = 3;
                  this.maskedInputBox.ValidatingType = typeof(System.DateTime);
                  // 
                  // btnShowFloatingCalendar
                  // 
                  this.btnShowFloatingCalendar.Location = new System.Drawing.Point(139, 49);
                  this.btnShowFloatingCalendar.Name = "btnShowFloatingCalendar";
                  this.btnShowFloatingCalendar.Size = new System.Drawing.Size(65, 27);
                  this.btnShowFloatingCalendar.TabIndex = 4;
                  this.btnShowFloatingCalendar.Text = "Calendar";
                  this.btnShowFloatingCalendar.UseVisualStyleBackColor = true;
                  this.btnShowFloatingCalendar.Click += new EventHandler(btnShowFloatingCalendar_Click);
      
                  // 
                  // Form1
                  // 
                  this.Controls.Add(this.btnShowFloatingCalendar);
                  this.Controls.Add(this.maskedInputBox);
                  this.Controls.Add(this.chkThisFormTopMost);
                  this.Controls.Add(this.chkShowWeeksNumbers);
      
                  InitializeComponent();
              }
      
              private void Form1_Load(object sender, EventArgs e)
              {
                  //DateTime format using in United States
                  //More info: http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo%28v=vs.71%29.aspx
                  //           http://msdn.microsoft.com/en-us/library/5hh873ya.aspx
      
                  Thread.CurrentThread.CurrentCulture = new CultureInfo(0x0409);
      
                  CultureInfo cultureInfoUSA = new CultureInfo(0x0409, false);
                  this.maskedInputBox.Culture = cultureInfoUSA;
              }
      
              //Constructor
              clsMonthCalendarBehavior userCalendar = new clsMonthCalendarBehavior();
      
              private void btnShowFloatingCalendar_Click(object sender, EventArgs e)
              {           
                  userCalendar.ShowCalendar(this.maskedInputBox,
                                            this.chkShowWeeksNumbers.Checked,
                                            this.chkThisFormTopMost.Checked);
              }
      
              private void chkThisFormTopMost_CheckedChanged(object sender, EventArgs e)
              {
                  this.TopMost = this.chkThisFormTopMost.Checked;
              }
          }
      }
      

      2) 将新的类项添加到项目中并命名为clsMonthCalendarBehavior.cs,稍后将所有文本替换为:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Windows.Forms;
      using System.Drawing;
      
      namespace WindowsFormsApplication1
      {
          class clsMonthCalendarBehavior
          {
              private bool manualDateTimeIsDone
              {
                  get
                  {
                      return (SetDateTimeManual(this._dateTimeInput.Text));
                  }
              }
      
              private static DateTime dateTimeManual;
      
              //Determine if the user inserting a correctly date and time
              internal static bool SetDateTimeManual(string inputReference)
              {
                  DateTime newDateTime = new DateTime(2000, 1, 1, 0, 0, 0);
      
                  bool isDateTime = DateTime.TryParse(inputReference, out newDateTime);
      
                  if (isDateTime)
                      dateTimeManual = newDateTime;
      
                  return (isDateTime ? true : false);
              }
      
              private MaskedTextBox _dateTimeInput;
      
              internal void ShowCalendar(MaskedTextBox dateTimeInput,
                                         bool showNumbersOfWeeks,
                                         bool principalFormIsTopMost)
              {
                  MonthCalendar monthCalendarCustomized = new MonthCalendar();
                  Panel popupPanel = new Panel();
                  Form floatingForm = new Form();
      
                  this._dateTimeInput = dateTimeInput;
      
                  //OPTIONAL: Show week numbers
                  monthCalendarCustomized.ShowWeekNumbers = showNumbersOfWeeks;
                  monthCalendarCustomized.MaxSelectionCount = 1;
      
                  if (manualDateTimeIsDone)
                      monthCalendarCustomized.SetDate(dateTimeManual); //User, date and time selected
                  else
                      monthCalendarCustomized.SetDate(DateTime.Now); //System, actual date and time
      
                  monthCalendarCustomized.DateSelected += new DateRangeEventHandler(DateSelected);
                  monthCalendarCustomized.KeyDown +=new KeyEventHandler(KeyDown);
      
                  monthCalendarCustomized.ShowToday = true;
      
                  //IDEA: bolded dates about references, etc.
                  monthCalendarCustomized.BoldedDates = new DateTime[]
                  {
                      DateTime.Today.AddDays(1),
                      DateTime.Today.AddDays(2),
                      DateTime.Today.AddDays(7),
                      DateTime.Today.AddDays(31),
                      DateTime.Today.AddDays(10)
                  };
      
                  popupPanel.BorderStyle = BorderStyle.FixedSingle;
      
                  popupPanel.Controls.Add(monthCalendarCustomized);
      
                  floatingForm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
                  floatingForm.ShowInTaskbar = false;
      
                  floatingForm.Location = Control.MousePosition;
                  floatingForm.StartPosition = FormStartPosition.Manual;
      
                  floatingForm.Controls.Add(popupPanel);
                  floatingForm.Deactivate += delegate { floatingForm.Close(); };
      
                  //NOTE: if principal from is topmost, cannot show in front "floatingForm" with calendar 
                  //      this option  fix the situation.
                  floatingForm.TopMost = principalFormIsTopMost;
      
                  //NOTE: set initial size of controls.
                  floatingForm.Size = popupPanel.Size = new Size(20, 20);
      
                  floatingForm.Show();
      
                  popupPanel.Size = floatingForm.Size = monthCalendarCustomized.Size;
      
                  popupPanel.Width = popupPanel.Width + 2;
                  popupPanel.Height = popupPanel.Height + 2;
      
                  floatingForm.Width = floatingForm.Width + 3;
                  floatingForm.Height = floatingForm.Height + 3;
              }
      
              void DateSelected(object sender, DateRangeEventArgs e)
              {
                  //Set data selected with culture info mask
                  this._dateTimeInput.Text = SetTimeValue(e.Start).ToString("MM/dd/yyyy HH:mm");
      
                  CloseFloatingForm(sender);
              }
      
              private static void CloseFloatingForm(object sender)
              {
                  MonthCalendar monthCalendarCustomized = (MonthCalendar)sender;
                  Form floatingForm = monthCalendarCustomized.FindForm();
      
                  monthCalendarCustomized.Parent.Controls.Remove(monthCalendarCustomized);
      
                  floatingForm.Close();
              }
      
              private DateTime SetTimeValue(DateTime selectedDateTime)
              {
                  //Recovery time of after selection, because when user select a new date
                  //Month Calendar reset the time
                  if (manualDateTimeIsDone)
                  {
                      TimeSpan addTimeValue = new TimeSpan(dateTimeManual.Hour,
                                                           dateTimeManual.Minute,
                                                           dateTimeManual.Second);
      
                      selectedDateTime = selectedDateTime.Add(addTimeValue);
                  }
      
                  return (selectedDateTime);
              }
      
              private void KeyDown(object sender, KeyEventArgs e)
              {
                  if (e.KeyCode == Keys.Escape)
                      CloseFloatingForm(sender);                
              }
          }
      }
      

      3) 运行和测试。

      【讨论】:

        【解决方案4】:

        我认为问题在于您关闭了包含月历的表单,这使得您的控件被释放。

        【讨论】:

        • 这并不能解释为什么它只在点击“今天的日期”部分而不是任何其他日期时发生。
        【解决方案5】:

        有趣。我可以在这里很好地重现这个(在 VS 2008 中运行,目标 3.5)。代码中有相当多的噪音,以下导致相同的行为,即

        • 通过点击日期/数字来选择任何日期
        • 选择底部的“今天”区域会生成ObjectDisposedException

        减少/最少的完整测试:

        using System;
        using System.Windows.Forms;
        
        namespace Test
        {
            public class Form1 : Form
            {
                public Form1()
                {
                    var dateSelectionButton = new Button();
                    SuspendLayout();
                    dateSelectionButton.Text = "Pick Date";
                    dateSelectionButton.Click += (SelectDateClick);
                    Controls.Add(dateSelectionButton);
                    ResumeLayout();
                }
        
                private void SelectDateClick(object sender, EventArgs e)
                {
                    MonthCalendar cal = new MonthCalendar();
                    Form f = new Form();
                    cal.DateSelected += DateSelected;
                    f.Controls.Add(cal);
                    f.Show();
                }
        
                void DateSelected(object sender, DateRangeEventArgs e)
                {
                    MonthCalendar cal = (MonthCalendar)sender;
                    Form f = cal.FindForm();
                    f.Close();
                }
            }
        }
        

        【讨论】:

        • 这是一个较短的复制品,但它如何增加价值?
        • 实际上我只是不明白为什么代码不能以这种方式工作/为什么需要解决方法。如果没有人能最终回答这个问题,我会说这可能是一个可能的错误,它再次需要一个最小的配方来重现它。在不知道为什么有必要的情况下解决这个问题也是值得怀疑的,imo。
        猜你喜欢
        • 2011-10-23
        • 2011-03-25
        • 1970-01-01
        • 1970-01-01
        • 2023-03-23
        • 1970-01-01
        相关资源
        最近更新 更多