一个十分诡异的NullReferenceException异常!

看到标题大家可能会认为这个文章一定没有太大的新意,无非是忘记初始化一类的问题,但是建议大家慢慢看下去,我担保这个问题决非这么无聊,希望给大家增加一些乐趣啊!
大家可以这样重现这个问题:
1,在窗体上放置一个TabControl控件有2个页(tabPage1,tabPage2),tabPage1的为当前页。
2,在tabPage1上放置一个 DataGridView控件dgv1,tabPage2上放置一个DataGridView控件dvg2并且两个   DataGridView的 AutoSizeColumnsMode 属性为Fill,设置好两个DataGridView的数据源后(这里大家可以自己设置一些数据源,保证有一列的数据就可以,设置数组等也可以)。
3,订阅窗体的Load事件,事件处理程序如下:
        private void form1_Load(object sender, EventArgs e)
        {
            
            dgv1.Columns[0].HeaderText = "名称";
            dgv1.Columns[0].Width = 120;

            dgv2.Columns[0].HeaderText = "名称";
            dgv2.Columns[0].Width = 120;//运行到这句的时候就会发生NullReferenceException异常;
        }

按如上步骤,应该可以重现这个问题了,问题十分诡异,设置HeaderText时候没有抛出NullReferenceException异常,这说明dgv2.Columns[0]不是null,经过监视窗口观察,他的确不是null。
那到底什么是null呢?大家可能认为我又会像过去的一些文章中用IL说明问题吧!
这回不用IL来说明,我们看看类库中的源代码:
DataGridViewColumn类的Width属性是这样定义的:
        public int Width
        {
            get
            {
                return this.Thickness;
            }
            set
            {
                this.Thickness = value;
            }
        }
实际上也没有做什么,只是设置了基类DataGridViewBand的Thickness属性,Thickness属性定义如下:
        internal int Thickness
        {
            get
            {
                if (this.bandIsRow && this.bandIndex > -1)
                {
                    int height, minimumHeight;
                    GetHeightInfo(this.bandIndex, out height, out minimumHeight);
                    return height;
                }
                return this.thickness;
            }
            set
            {
                int minimumThickness = this.MinimumThickness;
                if (value < minimumThickness)
                {
                    value = minimumThickness;
                }
                if (value > maxBandThickness)
                {
                    if (this.bandIsRow)
                    {
                        throw new ArgumentOutOfRangeException("Height", SR.GetString(SR.InvalidHighBoundArgumentEx, "Height", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture)));
                    }
                    else
                    {
                        throw new ArgumentOutOfRangeException("Width", SR.GetString(SR.InvalidHighBoundArgumentEx, "Width", (value).ToString(CultureInfo.CurrentCulture), (maxBandThickness).ToString(CultureInfo.CurrentCulture)));
                    }
                }
                bool setThickness = true;
                if (this.bandIsRow)
                {
                    if (this.DataGridView != null && this.DataGridView.AutoSizeRowsMode != DataGridViewAutoSizeRowsMode.None)
                    {
                        this.cachedThickness = value;
                        setThickness = false;
                    }
                }
                else
                {
                    DataGridViewColumn dataGridViewColumn = (DataGridViewColumn) this;
                    DataGridViewAutoSizeColumnMode inheritedAutoSizeMode = dataGridViewColumn.InheritedAutoSizeMode;
                    if (inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.Fill &&
                        inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.None &&
                        inheritedAutoSizeMode != DataGridViewAutoSizeColumnMode.NotSet)
                    {
                        this.cachedThickness = value;
                        setThickness = false;
                    }
                    else if (inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && this.DataGridView != null)
                    {
                        if (dataGridViewColumn.Visible)
                        {
                            IntPtr handle = this.DataGridView.Handle;
                            this.DataGridView.AdjustFillingColumn(dataGridViewColumn, value);
                            setThickness = false;
                        }
                    }
                }
 
                if (setThickness && this.thickness != value)
                {
                    if (this.DataGridView != null)
                    {
                        this.DataGridView.OnBandThicknessChanging();
                    }
                    this.ThicknessInternal = value;
                }
            }
        }

代码太长了啊,我把和这个问题相关的代码帖出来,把AutoSizeColumnsMode 属性为Fill,会执行到这里:
                    else if (inheritedAutoSizeMode == DataGridViewAutoSizeColumnMode.Fill && this.DataGridView != null)
                    {
                        if (dataGridViewColumn.Visible)
                        {
                            IntPtr handle = this.DataGridView.Handle;  //我认为异常是这行代码抛出的,这由于你的dgv2所在的TabPage并没有激活所以这时Handle为null,所以抛出了 NullReferenceException异常
                            this.DataGridView.AdjustFillingColumn(dataGridViewColumn, value);
                            setThickness = false;
                        }
                    }
这个异常是内部抛出的!
如下方法可以解决这个问题:
1,不要把AutoSizeColumnsMode 属性为Fill,然后再自己设置列宽。
2,可以把dgv2所在的TabPage设置为激活状态:
        private void form1_Load(object sender, EventArgs e)
        {
            this.tabControl1.SelectedIndex = 1;
            dgv1.Columns[0].HeaderText = "名称";
            dgv1.Columns[0].Width = 120;

            dgv2.Columns[0].HeaderText = "名称";
            dgv2.Columns[0].Width = 120;
        }
3,在其他位置修改列宽,比如dgv2的DataBindingComplete事件中。

posted on 2010-02-03 12:33  周雪峰  阅读(2198)  评论(7编辑  收藏  举报