[Winform] DataGridView 總結(FAQ)


Q1.  如何使單元格不可編輯?

A:設置 ReadOnly 屬性,可以設置的對象包括 DataGridViewRow(行)、DataGridViewColumn(列)、DataGridViewCell(單元格)以及自身 DataGridView 對象均可設置 ReadOnly 屬性來限制單元格的編輯狀態。

擴展:需要注意的是,當 DataGridView 通過 DataSource 綁定數據自動生成行列時,如果直接在 Form 的構造函數初始化界面 InitializeComponent 后直接設置 ReadOnly 屬性,會造成一些意想不到的效果……

  1 public MainForm()
  2 {
  3     InitializeComponent()
  4 
  5     Application.DoEvents()
  6     dataGridView.DataSource = Person.GetPersons()
  7     dataGridView[0, 0].ReadOnly = true
  8     dataGridView.Rows[2].ReadOnly = true
  9     dataGridView.Columns[1].ReadOnly = true
 10 } 

此時對 DataGridViewCell、DataGridViewRow的ReadOnly 設置無效,而對 DataGridViewColumn 的 ReadOnly 設置有效。

另外,ReadOnly 屬性只是限制用戶在界面上對單元格內容編輯的限制,並不影響在編程代碼中對該單元格的編輯以及刪除行列操作。

當然,你也可以在 CellBeginEdit 事件中對單元格進行判斷,然后直接結束編輯即可,如下:

  1 dataGridView.CellBeginEdit += (sender, e) =>
  2 {
  3     if (e.RowIndex == 0 && e.ColumnIndex == 0)
  4     {
  5         e.Cancel = true
  6         // 或者
  7         // dataGridView.EndEdit();
  8     }
  9 }

Q2.  如何禁用單元格(Disable)?

A:DataGridView 不支持設置單元格的不可用狀態,所以采用折中辦法,使該“禁用”單元格的選中狀態和其背景顏色相同,給人暗示性的外觀提示該單元格“禁用”。

有一種便捷的方式,就是將該“禁用”單元格的選中顏色設置成非選中顏色,即如果未選中前是白底黑字,則將該“禁用”單元格的選中狀態也改成白底黑字即可,對單元格、行和列均適用,舉例如下:

  1 dataGridView[2, 2].Style.SelectionBackColor = Color.White
  2 dataGridView[2, 2].Style.SelectionForeColor = Color.Black
  3 
  4 dataGridView.Rows[1].DefaultCellStyle.SelectionBackColor = Color.White
  5 dataGridView.Rows[1].DefaultCellStyle.SelectionForeColor = Color.Black
  6 
  7 dataGridView.Columns[0].DefaultCellStyle.SelectionBackColor = Color.White
  8 dataGridView.Columns[0].DefaultCellStyle.SelectionForeColor = Color.Black

需要注意的是,同 Q1 中一樣,在 InitializeComponent 方法后面直接操作,其中對單元格的設置無效,對行、列的設置有效!!

但是這種方法對文本內容的單元有效,對於 DataGridViewButtonColumn、DataGridViewCheckBoxColumn、DataGridViewComboBoxColumn 等其他特殊列就沒效果了,畢竟對於特殊列,禁用單元格就是要禁用其中的特殊控件,這時候就需要重寫其中的單元格模版了,以 DataGridViewButtonColumn 為例,代碼如下:

public class DataGridViewButtonColumnExt : DataGridViewButtonColum
{
    public DataGridViewButtonColumnExt()
    {
        this.CellTemplate = new DataGridViewButtonCellExt()
    }
}

public class DataGridViewButtonCellExt : DataGridViewButtonCell
{
    private bool _Enabled;// 設置該單元格是否可用

    /// <summary>
    /// 單元格是否可用
    /// </summary>
    public bool Enabled
    {
        get
        {
            return _Enabled
        }
        set
        {
            _Enabled = value
        }
    }

    public override object Clone()
    {
        DataGridViewButtonCellExt cell =(DataGridViewButtonCellExt)base.Clone()
        cell.Enabled = this.Enabled

        return cell
    }

    public DataGridViewButtonCellExt()
    {
        this._Enabled = true
    }

    protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,
        int rowIndex, DataGridViewElementStates elementState, object value,
        object formattedValue, string errorText, DataGridViewCellStyle cellStyle,
        DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
    {
        if (!this._Enabled)
        {
            // 繪制背景
            if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)
            {
                SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)
                graphics.FillRectangle(cellBackground, cellBounds)
                cellBackground.Dispose()
            }

            // 繪制邊框
            if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
            {
                PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
            }

            Rectangle buttonArea = cellBound
            Rectangle buttonAdjustment = this.BorderWidths(advancedBorderStyle)
            buttonArea.X += buttonAdjustment.X
            buttonArea.Y += buttonAdjustment.Y
            buttonArea.Height -= buttonAdjustment.Height
            buttonArea.Width -= buttonAdjustment.Width
            
            // 繪制按鈕控件
            ButtonRenderer.DrawButton(graphics, buttonArea, PushButtonState.Disabled)
            // 繪制文本內容
            if (this.FormattedValue is String)
            {
                TextRenderer.DrawText(graphics, (string)this.FormattedValue,
                    this.DataGridView.Font, buttonArea, SystemColors.GrayText)
            }
        }
        else
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue,
                errorText, cellStyle, advancedBorderStyle, paintParts)
        }
    }
}

View Code

下面是 CheckBox 列的重寫例子,因為復選框有兩種禁用效果,一種選中時禁用,一種是未選中時禁用,所以加了一個 IsChecked 屬性:

public class DataGridViewCheckBoxColumnExt : DataGridViewCheckBoxColum
{
	public DataGridViewCheckBoxColumnExt()
	{
		this.CellTemplate = new DataGridViewCheckBoxCellExt()
	}
}

public class DataGridViewCheckBoxCellExt : DataGridViewCheckBoxCell
{
	private bool _Enable
	private bool _IsChecked
	
	/// <summary>
	/// 是否選中
	/// </summary>
	public bool IsChecked
	{
		get
		{
			return _IsChecked
		}
		set
		{
			_IsChecked = value
		}
	}

	/// <summary>
	/// 是否可用
	/// </summary>
	public bool Enable
	{
		get
		{
			return _Enable
		}
		set
		{
			_Enable = value
		}
	}

	public DataGridViewCheckBoxCellExt()
	{
		_Enable = true
		_IsChecked = false
	}

	public override object Clone()
	{
		DataGridViewCheckBoxCellExt dgvcce = (DataGridViewCheckBoxCellExt)base.Clone()
		dgvcce.Enable = this.Enable
		dgvcce.IsChecked = this.IsChecked

		return dgvcce
	}

	protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,
		int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue,
		string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle,
		DataGridViewPaintParts paintParts)
	{
		if (!_Enable)
		{
			// 繪制背景
			if ((paintParts & DataGridViewPaintParts.Background) == DataGridViewPaintParts.Background)
			{
				SolidBrush cellBackground = new SolidBrush(cellStyle.BackColor)
				graphics.FillRectangle(cellBackground, cellBounds)
				cellBackground.Dispose()
			}

			// 繪制邊框
			if ((paintParts & DataGridViewPaintParts.Border) == DataGridViewPaintParts.Border)
			{
				PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
			}

			Point pos = cellBounds.Locatio
			// 調整位置居中
			pos.X = pos.X + cellBounds.Width / 2 - 7
			pos.Y = pos.Y + cellBounds.Height / 2 - 7
			// 繪制按鈕控件
			CheckBoxRenderer.DrawCheckBox(graphics, pos,
				IsChecked ? CheckBoxState.CheckedDisabled : CheckBoxState.UncheckedDisabled)
		}
		else
		{
			base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value,
				formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
		}
	}
}
View Code

Q3.  如何在單元格內同時顯示圖片和文字?

A:參考網上查到的資料,將文本列(DataGridViewTextBoxColum)中的文本向右偏移,然后在前面置入圖片;

public class TextAndImageColumn : DataGridViewTextBoxColum
{
    private Image m_ImageValue
    private Size m_ImageSize

    public TextAndImageColumn()
    {
        this.CellTemplate = new TextAndImageCell()
    }

    public override object Clone()
    {
        TextAndImageColumn c = base.Clone() as TextAndImageColum
        c.m_ImageValue = this.m_ImageValue
        c.m_ImageSize = this.m_ImageSize

        return c
    }

    public Image Image
    {
        get
        {
            return this.m_ImageValue
        }
        set
        {
            if (this.Image != value)
            {
                this.m_ImageValue = value
                this.m_ImageSize = value.Size
                if (this.InheritedStyle != null)
                {
                    Padding inheritedPadding = this.InheritedStyle.Padding
                    this.DefaultCellStyle.Padding = new Padding(m_ImageSize.Width,
                        inheritedPadding.Top, inheritedPadding.Right,
                        inheritedPadding.Bottom)
                }
            }
        }
    }

    public Size ImageSize
    {
        get
        {
            return m_ImageSize
        }
        set
        {
            m_ImageSize = value
        }
    }

    private TextAndImageCell TextAndImageCellTemplate
    {
        get
        {
            return this.CellTemplate as TextAndImageCell
        }
    }
}

public class TextAndImageCell : DataGridViewTextBoxCell
{
    private Image m_ImageValue
    private Size m_ImageSize

    public override object Clone()
    {
        TextAndImageCell c = base.Clone() as TextAndImageCell
        c.m_ImageValue = this.m_ImageValue
        c.m_ImageSize = this.m_ImageSize

        return c
    }

    public Image Image
    {
        get
        {
            if (this.OwningColumn == null || this.OwningTextAndImageColumn == null)
            {
                return m_ImageValue
            }
            else if (this.m_ImageValue != null)
            {
                return this.m_ImageValue
            }
            else
            {
                return this.OwningTextAndImageColumn.Image
            }
        }
        set
        {
            if (this.m_ImageValue != value)
            {
                this.m_ImageValue = value
                this.m_ImageSize = value.Size
                Padding inheritedPadding = this.InheritedStyle.Padding
                this.Style.Padding = new Padding(m_ImageSize.Width,
                    inheritedPadding.Top, inheritedPadding.Right,
                    inheritedPadding.Bottom)
            }
        }
    }

    protected override void Paint(Graphics graphics, Rectangle clipBounds,
        Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState,
        object value, object formattedValue, string errorText,
        DataGridViewCellStyle cellStyle,
        DataGridViewAdvancedBorderStyle advancedBorderStyle,
        DataGridViewPaintParts paintParts)
    {
        // Paint the base content
        base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState,
          value, formattedValue, errorText, cellStyle,
          advancedBorderStyle, paintParts)

        if (this.Image != null)
        {
            // Draw the image clipped to the cell.
            System.Drawing.Drawing2D.GraphicsContainer container = graphics.BeginContainer()
            graphics.SetClip(cellBounds)
            graphics.DrawImageUnscaled(this.Image, cellBounds.Location)
            graphics.EndContainer(container)
        }
    }

    private TextAndImageColumn OwningTextAndImageColum
    {
        get
        {
            return this.OwningColumn as TextAndImageColum
        }
    }
}
View Code

還有一種實現方式是在 CellPainting 事件中對指定單元格進行圖文繪制,參考百度文庫 

Q4.  如何讓 DataGridViewComboBox 可編輯?

A:見 DataGridView 中 DataGridViewComboBox 的可編輯

還有一種根據 參考資料1 提供的方法,更簡捷,就是在 CellValidating 事件中將新編輯的內容添加到 Items 集合中,在 EditingControlShowing 事件中將下拉框類型 DropDownStyle 設置成 ComboBoxStyle.DropDown,使用戶可以進入編輯狀態,代碼如下:

  1 private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
  2 {
  3     try
  4     {
  5         if (dgv4.CurrentCellAddress.X == 4)
  6         {
  7             ComboBox cb = e.Control as ComboBox;
  8             if (cb != null)
  9             {
 10                 cb.DropDownStyle = ComboBoxStyle.DropDown;
 11             }
 12         }
 13     }
 14     catch (Exception ex)
 15     {
 16         MessageBox.Show(ex.Message);
 17     }
 18 }
 19 
 20 private void dgv4_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
 21 {
 22     try
 23     {
 24         if (e.ColumnIndex == 4)
 25         {
 26             DataGridViewComboBoxColumn dgvcbc = (DataGridViewComboBoxColumn)dgv4.Columns[4];
 27             if (!dgvcbc.Items.Contains(e.FormattedValue))
 28             {
 29                 dgvcbc.Items.Add(e.FormattedValue);
 30             }
 31         }
 32     }
 33     catch (Exception ex)
 34     {
 35         MessageBox.Show(ex.Message);
 36     }
 37 }

Q5.  如何多列排序?

A:分綁定數據和非綁定數據兩種情況處理。

1、綁定數據

如果綁定的數據源(如 DataView)支持多列排序,則 DataGridView 在綁定數據后會保留數據源排序后的結果,但是只有第一個進行排序的列會在 DataGridView 的顯示排序標記。而且,DataGridView 的 SortedColumn 屬性也只返回第一個排序列。

如果數據源實現了 IBindingListView 接口,並提供了對 Sort 屬性的支持,那么該數據源就可以支持多列排序。為了彌補上面提到的只標記了第一排序列的缺陷,可以手動對進行排序的列設置 SortGlyphDirection 屬性來標記。

  1 BindingSource bindingSource = new BindingSource();
  2 dgv4.AutoGenerateColumns = false;
  3 dgv4.DataSource = bindingSource;
  4 
  5 DataTable dt = new DataTable();
  6 dt.Columns.Add("C1", typeof(int));
  7 dt.Columns.Add("C2", typeof(string));
  8 dt.Columns.Add("C3", typeof(string));
  9 
 10 dt.Rows.Add(1, "1", "Test1");
 11 dt.Rows.Add(2, "2", "Test2");
 12 dt.Rows.Add(2, "2", "Test1");
 13 dt.Rows.Add(3, "3", "Test3");
 14 dt.Rows.Add(4, "4", "Test4");
 15 dt.Rows.Add(4, "4", "Test3");
 16 
 17 DataView view = dt.DefaultView;
 18 view.Sort = "C2 ASC,C3 DESC";
 19 bindingSource.DataSource = view;
 20 
 21 DataGridViewTextBoxColumn col0 = new DataGridViewTextBoxColumn();
 22 col0.DataPropertyName = "C1";
 23 dgv4.Columns.Add(col0);
 24 col0.SortMode = DataGridViewColumnSortMode.Programmatic;
 25 col0.HeaderCell.SortGlyphDirection = SortOrder.None;
 26 
 27 DataGridViewTextBoxColumn col1 = new DataGridViewTextBoxColumn();
 28 col1.DataPropertyName = "C2";
 29 dgv4.Columns.Add(col1);
 30 col1.SortMode = DataGridViewColumnSortMode.Programmatic;
 31 col1.HeaderCell.SortGlyphDirection = SortOrder.Ascending;
 32 
 33 DataGridViewTextBoxColumn col2 = new DataGridViewTextBoxColumn();
 34 col2.DataPropertyName = "C3";
 35 dgv4.Columns.Add(col2);
 36 col2.SortMode = DataGridViewColumnSortMode.Programmatic;
 37 col2.HeaderCell.SortGlyphDirection = SortOrder.Descending;

需要注意的是,對 SortGlyphDirection 屬性的設置要在 DataGridView 綁定 DataSource 后面操作,否則會不生效。image

上面代碼來自資料參考2的,可以簡化成:

DataTable dt = new DataTable();
dt.Columns.Add("C1", typeof(int));
dt.Columns.Add("C2", typeof(string));
dt.Columns.Add("C3", typeof(string));

dt.Rows.Add(1, "1", "Test1");
dt.Rows.Add(2, "2", "Test2");
dt.Rows.Add(2, "2", "Test1");
dt.Rows.Add(3, "3", "Test3");
dt.Rows.Add(4, "4", "Test4");
dt.Rows.Add(4, "4", "Test3");

DataView view = dt.DefaultView;
view.Sort = "C2 ASC,C3 DESC";

dgv4.DataSource = dt;
dgv4.Columns[1].SortMode = DataGridViewColumnSortMode.Programmatic;
dgv4.Columns[1].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
dgv4.Columns[2].SortMode = DataGridViewColumnSortMode.Programmatic;
dgv4.Columns[2].HeaderCell.SortGlyphDirection = SortOrder.Descending;
View Code

2、非綁定數據

為了提供對多個列排序的支持,可以通過處理 SortCompare 事件,或者調用重載的 Sort ( IComparer ) 方法以更靈活的方式進行排序。

2.1、SortCompare 事件

private void dgv4_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
    try
    {
        e.SortResult = String.Compare(e.CellValue1.ToString(), e.CellValue2.ToString());
        if (e.SortResult == 0 && e.Column.Name != "ID")
        {
            e.SortResult = string.Compare(
                dgv4.Rows[e.RowIndex1].Cells["ID"].Value.ToString(),
                dgv4.Rows[e.RowIndex2].Cells["ID"].Value.ToString());
        }

        e.Handled = true;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

上面的例子表示當你點擊列頭對某一列進行排序時,如果值相同時,則會按照對應 ID 值進行排序;所以如果要支持多列排序,在該事件里處理即可。

2.2、IComparer 接口

樓主擴展了資料里提供的參考例子,改成通用的多列排序接口實現,

  1 public class RowComparer : IComparer
  2 {
  3     private Dictionary<string, int> m_SortList;
  4 
  5     /// <summary>
  6     /// 排序字符串,格式:(ColName1 AES,ColName2 DESC,ColName3 AES,...)
  7     /// </summary>
  8     public string Sort
  9     {
 10         get;
 11         set;
 12     }
 13 
 14     /// <summary>
 15     /// 構造函數,初始化排序條件
 16     /// </summary>
 17     public RowComparer(string sort)
 18     {
 19         Sort = sort;
 20         try
 21         {
 22             string[] tmp = Sort.Split(',');
 23             m_SortList = new Dictionary<string, int>();
 24             for (int i = 0; i < tmp.Length; i++)
 25             {
 26                 string[] tmp2 = tmp[i].Split(new char[] { ' ' },
 27                     StringSplitOptions.RemoveEmptyEntries);
 28                 string colName = tmp2[0].ToLower();
 29                 int sortType = tmp2[1].ToLower().Equals("AES") ? 1 : -1;
 30                 if (m_SortList.ContainsKey(colName))
 31                 {
 32                     m_SortList[colName] = sortType;
 33                 }
 34                 else
 35                 {
 36                     m_SortList.Add(colName, sortType);
 37                 }
 38             }
 39         }
 40         catch (Exception ex)
 41         {
 42             throw new Exception(ex.Message);
 43         }
 44     }
 45 
 46     #region IComparer 成員
 47 
 48     public int Compare(object x, object y)
 49     {
 50         int compareResult = 0;// 比較結果
 51         int sortMode = 0;// 排序方式
 52         try
 53         {
 54             DataGridViewRow dgvr1 = (DataGridViewRow)x;
 55             DataGridViewRow dgvr2 = (DataGridViewRow)y;
 56             foreach (string colName in m_SortList.Keys)
 57             {
 58                 compareResult = string.Compare(dgvr1.Cells[colName].Value.ToString(),
 59                     dgvr2.Cells[colName].Value.ToString());
 60                 sortMode = m_SortList[colName];
 61                 if (compareResult != 0)
 62                 {
 63                     break;
 64                 }
 65             }
 66         }
 67         catch (Exception ex)
 68         {
 69             MessageBox.Show(ex.Message);
 70         }
 71 
 72         return compareResult * sortMode;
 73     }
 74 
 75     #endregion
 76 }
View Code

Sort 屬性采用 DataView 的 Sort 屬性設置,然后在 RowComparer 構造函數對排序字符串進行處理,最后在接口方法 Compare 中依先后順序逐級排序;

Q6.  綁定 List 時,如何使 DataGridView 和 List 數據源同步修改?(3)

A:當 DataGridView 綁定 List 數據源時,對 List 進行操作后並不會實時更新到 DataGridView 上,這時候采用 BindingList 就可以很好的解決問題。BindingList 類可以用作創建雙向數據綁定機制的基類。BindingList 提供 IBindingList 接口的具體泛型實現,這樣就不必實現完整的 IBindingList 接口了。

BindingList 還可以通過擴展的 AddNew 方法支持工廠創建的示例;通過 EndNew CancelNew 方法實現新項的事務性提交和回滾。

Q7.  如何在用戶刪除記錄時顯示確認對話框?

A:用戶選中一行后按 Delete 鍵會觸發 UserDeletingRow 事件(當然,前提是要設置 DataGridView 的 AllowUserToDeleteRows 屬性為 True 才可以),在該事件里提示用戶是否刪除當前選中記錄即可。

private void dgv4_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e)
{
    try
    {
        if (!e.Row.IsNewRow)
        {
            if (MessageBox.Show("確定刪除當前選中數據?", "刪除",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2)
                == System.Windows.Forms.DialogResult.No)
            {
                e.Cancel = true;
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Q8.  如何為單元格內的控件添加事件處理函數?

A:諸如 DataGridViewButtonColumn 列里的 Button,DataGridViewCheckBoxColumn 列里的 CheckBox 等等,要給 Button 或 CheckBox 控件添加事件處理函數,則可以通過實現 DataGridView 的 EditingControlShowing 事件,該事件是在單元格進入編輯模式時觸發,可以處理執行該編輯控件的自定義初始化。它的第二個參數 DataGridViewEditingControlShowingEventArgs 類型,其 Control 屬性就是該單元格內的編輯控件了。拿 DataGridViewComboBoxColumn 列里的 ComboBox 事件舉個例子:

private void dgv4_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
    try
    {
        ComboBox cb = e.Control as ComboBox;
        if (cb != null)
        {
            cb.SelectedIndexChanged -= new EventHandler(cb_SelectedIndexChanged);
            cb.SelectedIndexChanged += cb_SelectedIndexChanged;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

private void cb_SelectedIndexChanged(object sender, EventArgs e)
{
    MessageBox.Show("Selected Index Changed!");
}
View Code

需要注意的是,在 EditingControlShowing 事件里對編輯控件進行事件綁定時,要防止其添加多個相同的事件處理函數,所以在綁定事件前可以先移除相應的事件處理函數。

Q9.  如何讓數據顯示區域(列)填充整個 DataGridView 大小?

A:可以設置 DataGridView 的 AutoSizeColumnsMode 屬性,設置為 DataGridViewAutoSizeColumnsMode.Fill,此時列寬會自動調整到使所有列寬精確填充控件的顯示區域。當然,你可以通過設置每一列的 DataGridViewColumn.FillWeight 屬性來設置每一列的相對寬度。

如果只是想把最后一列填充剩下的空間,而前面那些列都是固定大小的,那可以直接設置最后一列的 DataGridViewColumn.AutoSizeMode 的屬性為 DataGridViewAutoSizeColumnMode.Fill 即可。

Q10.  如何設置 Image 列中當值為 NULL 時不顯示圖像(默認顯示“X”圖像)?

A:設置 Image 列的 dataGridView.DefaultCellStyle.NullValue = null;

Q11.  如何設置單元格內容自動換行?

A:設置自動換行:

datagridview.DefaultCellStyle.WrapMode = DataGridViewTriState.True;

然后設置自動調整高度,設置

datagridview.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders;

或者 datagridview.AutoResizeRows(DataGridViewAutoSizeRowsMode.DisplayedCellsExceptHeaders);

需要注意的是,該設置對中文有效;如果是英文,一個單詞的寬度超過單元格的寬度,則是不會自動處理換行的,但是英文單詞與單詞之間有空格的話,也會處理自動換行。

Q12.  如何禁用列的自動排序?

A:設置 DataGridViewColumn 的 SortMode 屬性,該屬性默認值為 DataGridViewColumnSortMode.Automatic,即自動排序,改成 DataGridViewColumnSortMode.NotSortable 則可避免默認的排序;或者設置成 DataGridViewColumnSortMode.Programmatic,僅允許通過編程的方式對列進行排序。

Q13.  如何拖動調整行順序?

A:設置 DataGridView 的 AllowDrop 屬性為 true,並實現 MouseDown、MouseMove、DragOver、DragDrop 四個事件,參考代碼如下:

  1 private Rectangle _dragBox;
  2 private int _rowIndexFrom;// 鼠標按下位置的行索引
  3 private int _rowIndexTo;// 將要拖動到的行索引位置
  4 private void dgv4_MouseDown(object sender, MouseEventArgs e)
  5 {
  6     try
  7     {
  8         _rowIndexFrom = dgv4.HitTest(e.X, e.Y).RowIndex;
  9         if (_rowIndexFrom != -1)
 10         {
 11             Size size = SystemInformation.DragSize;
 12             _dragBox = new Rectangle(new Point(e.X - (size.Width / 2), e.Y - (size.Height / 2)), size);
 13         }
 14         else
 15         {
 16             _dragBox = Rectangle.Empty;
 17         }
 18     }
 19     catch (Exception ex)
 20     {
 21         MessageBox.Show(ex.Message);
 22     }
 23 }
 24 private void dgv4_MouseMove(object sender, MouseEventArgs e)
 25 {
 26     try
 27     {
 28         if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
 29         {
 30             if (_dragBox != Rectangle.Empty && !_dragBox.Contains(e.X, e.Y))
 31             {
 32                 DragDropEffects dropEffect = dgv4.DoDragDrop(dgv4.Rows[_rowIndexFrom], DragDropEffects.Move);
 33             }
 34         }
 35     }
 36     catch (Exception ex)
 37     {
 38         MessageBox.Show(ex.Message);
 39     }
 40 }
 41 private void dgv4_DragDrop(object sender, DragEventArgs e)
 42 {
 43     try
 44     {
 45         // 鼠標坐標是相對於屏幕的,轉換成工作區 DataGridView 上的坐標
 46         Point clientPoint = dgv4.PointToClient(new Point(e.X, e.Y));
 47         // 獲取鼠標下面的所在行號
 48         _rowIndexTo = dgv4.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
 49         if (e.Effect == DragDropEffects.Move)
 50         {
 51             DataGridViewRow rowToMove = e.Data.GetData(typeof(DataGridViewRow)) as DataGridViewRow;
 52             dgv4.Rows.RemoveAt(_rowIndexFrom);
 53             dgv4.Rows.Insert(_rowIndexTo, rowToMove);
 54         }
 55     }
 56     catch (Exception ex)
 57     {
 58         MessageBox.Show(ex.Message);
 59     }
 60 }
 61 private void dgv4_DragOver(object sender, DragEventArgs e)
 62 {
 63     e.Effect = DragDropEffects.Move;// 設置拖動效果
 64 }
View Code

Q14.  如何顯示主從表?

A:主從表的意思就是通過兩個 DataGridView 控件顯示具有主從關系的兩個數據表,在主表中選擇一行記錄,則從表中也會隨之改變,顯示對應的記錄數據。

1、下面代碼是 DataGridView 直接綁定 DataSet 數據集:

DataTable masterDT = Person.GetPersons2();// 主表,主鍵"ID"
DataTable detailDT = Person.GetPersons3();// 從表,外鍵"ID"

DataSet dataDS = new DataSet();
dataDS.Tables.Add(masterDT);
dataDS.Tables.Add(detailDT);
// 建立主從表關系
dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]);

// 數據綁定
dgv5_1.DataSource = dataDS;
dgv5_1.DataMember = dataDS.Tables[0].TableName;

dgv5_2.DataSource = dataDS;
dgv5_2.DataMember = dataDS.Tables[0].TableName + ".Custom";

dgv5_1.MultiSelect = false;

代碼中主表 masterDT,從表 detailDT,通過 DataSet 的 Relations 建立主從關系,注意其中 Add 方法的第一個參數是關系名稱,在后面的數據綁定中要用到。

負責顯示主表數據的 DataGridView(dgv5_1),負責顯示從表數據的 DataGridView(dgv5_2),其 DataSource 均綁定同一個 DataSet,DataMember 注意區別,主表的 dgv5_1 直接綁定主表名稱即可,從表的 dgv5_2 的 DataMember 屬性則要綁定"主表名稱.關系名稱"。

2、下面代碼是 DataGridView 綁定 BindingSource

DataTable masterDT = Person.GetPersons2();// 主表,主鍵"ID"
DataTable detailDT = Person.GetPersons3();// 從表,外鍵"ID"
DataSet dataDS = new DataSet();
dataDS.Tables.Add(masterDT);
dataDS.Tables.Add(detailDT);
dataDS.Relations.Add("Custom", masterDT.Columns["ID"], detailDT.Columns["ID"]);

BindingSource masterBS = new BindingSource();
masterBS.DataSource = dataDS;
masterBS.DataMember = masterDT.TableName;
dgv5_1.DataSource = masterBS;

BindingSource detailBS = new BindingSource();
detailBS.DataSource = masterBS;
detailBS.DataMember = "Custom";
dgv5_2.DataSource = detailBS;

dgv5_1.MultiSelect = false;

注意如果 DataGridView 通過 BindingSource 綁定數據,並且在 BindingSource 中設置 DataSource 和 DataMember 時,此時的從表對應的 BindingSource 設置的 DataMember 應該直接用關系名稱即可,與上面的"主表名稱.關系名稱"區別開來!

Q15.  如何同時顯示綁定數據和非綁定數據?

A:樓主就以資料1中例子來講解,下面代碼是樓主親敲親測的。該例子是說如何創建一列復選框列,用來負責選擇數據。並且通過字典緩存(_CheckState)以及相應的事件,保證在用戶排序時不會丟失已有的選擇狀態。

private Dictionary<int, bool> _CheckState;// 存儲非綁定列數據
public MainForm()
{
    InitializeComponent();

    _CheckState = new Dictionary<int, bool>();
    dgv4.AutoGenerateColumns = true;
    dgv4.ReadOnly = false;
    dgv4.DataSource = Person.GetPersons2();
    dgv4.VirtualMode = true;
    dgv4.Columns.Insert(0, new DataGridViewCheckBoxColumn());
    dgv4.CellValueChanged += (s, e) =>
    {
        if (e.ColumnIndex == 0 && e.RowIndex != -1)
        {
            int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
            _CheckState[id] = Convert.ToBoolean(dgv4.Rows[e.RowIndex].Cells[0].Value);
        }
    };
    dgv4.CellValueNeeded += (s, e) =>
    {
        if (e.ColumnIndex == 0)
        {
            int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
            if (_CheckState.ContainsKey(id))
            {
                e.Value = _CheckState[id];
            }
            else
            {
                e.Value = false;
            }
        }
    };
    dgv4.CellValuePushed += (s, e) =>
    {
        if (e.ColumnIndex == 0)
        {
            int id = Convert.ToInt32(dgv4.Rows[e.RowIndex].Cells["ID"].Value);
            if (!_CheckState.ContainsKey(id))
            {
                _CheckState.Add(id, Convert.ToBoolean(e.Value));
            }
            else
            {
                _CheckState[id] = Convert.ToBoolean(e.Value);
            }
        }
    };
}

1、復選框值改變時通過 CellValueChanged 事件緩存到 _ChekState 字典中;2、VirtualMode 為 true,並且單元格有值時需要顯示和格式化的處理,是通過 CellValueNeeded 事件;3、VirtualMode 屬性為 true,並且單元格值已更改並需要存儲在基礎數據源中時通過 CellValuePushed 事件處理。

Q16.  列頭右鍵菜單控制列的顯隱[4]

A:需求是在列頭位置處增加右鍵菜單,控制 DataGridView 所有列的顯示和隱藏。

首先在 DataGridView 列的增刪時對菜單選項進行更新,然后在菜單點擊事件中修改列的可見性以及菜單欄該選項的 IsChecked 屬性,最后在右鍵單擊處顯示菜單即可。

  1 // 列頭位置顯示右鍵菜單
  2 dataGridView.ColumnHeaderMouseClick += (sender, e) =>
  3 {
  4     if (e.Button == MouseButtons.Right)
  5     {
  6         contextMenuStrip.Show(MousePosition.X, MousePosition.Y);
  7     }
  8 };
  9 // 增加列時
 10 dataGridView.ColumnAdded += (sender, e) =>
 11 {
 12     ToolStripMenuItem item = new ToolStripMenuItem(e.Column.Name);
 13     contextMenuStrip.Items.Add(item);
 14     item.Checked = e.Column.Visible;
 15 };
 16 // 刪除列時
 17 dataGridView.ColumnRemoved += (sender, e) =>
 18 {
 19     int count = contextMenuStrip.Items.Count;
 20     for (int i = 0; i < count; i++)
 21     {
 22         ToolStripMenuItem tsm = (ToolStripMenuItem)contextMenuStrip.Items[i];
 23         if (tsm.Text.Equals(e.Column.Name))
 24         {
 25             contextMenuStrip.Items.Remove(tsm);
 26             break;
 27         }
 28     }
 29 };
 30 // 菜單欄控制列的顯示和隱藏
 31 contextMenuStrip.ItemClicked += (sender, e) =>
 32 {
 33     string colName = e.ClickedItem.Text;
 34     bool isChecked = ((ToolStripMenuItem)e.ClickedItem).Checked;
 35     dataGridView.Columns[colName].Visible = !isChecked;
 36     ((ToolStripMenuItem)e.ClickedItem).Checked = !isChecked;
 37 };

資料參考

1、http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html (主要參考)

2、http://blogs.msdn.com/b/jfoscoding/archive/2005/11/17/494012.aspx

3、https://msdn.microsoft.com/zh-cn/library/ms132679.aspx#Mtps_DropDownFilterTextBindingList

4、http://www.cnblogs.com/over140/archive/2012/04/16/2451200.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM