none
DataGridView 左上隅クリックで、すべて選択を禁止する方法 RRS feed

  • 質問

  • いつも大変お世話になっています。

    環境:Win7 Visual Studio2013

    DataGridView 左上隅(TopLeftHeaderCell)をクリックすると、DataGridView全体が選択状態になります。

    このセルがクリックされたかどうかは、

            private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
            {
                if ((e.ColumnIndex == -1)  && (e.RowIndex == -1))
                {
                    MessageBox.Show("TopLeftHeaderCellがクリックされました。");
                }
            }
    

    上記で捉えられるのですが、すでに全選択状態になっています。

    このセルがクリックされた時、

    全選択を回避し、クリックされたことのみを知る方法はありますでしょうか?

    なお具体的に行いたい内容は、このセルがクリックされたタイミングで、

    列ヘッダークリックによる行のソートを、元々の並び順に戻すことです。

    元々の並び順をデータとして持っている列を作成して、その列でソートさせる方法を取るべきでしょうか?

    以上、ご教授の程、よろしくお願いいたします。


    kizakura_ui

    2016年4月4日 8:26

回答

  • こんにちは。

    CellMouseDownをオーバーライドして別イベントを発生させてはどうでしょうか。

    public partial class Form1 : Form
    {
    	public Form1()
    	{
    		InitializeComponent();
    		dataGridView1.HogeEvent += DataGridView1_HogeEvent;
    	}
    
    	private void DataGridView1_HogeEvent(object sender, EventArgs e)
    	{
    		MessageBox.Show("TopLeftHeaderCellがクリックされました。");
    	}
    }
    
    public class DataGridViewEx : DataGridView
    {
    	public event EventHandler HogeEvent;
    
    	protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
    	{
    		if(e.RowIndex == -1 && e.ColumnIndex == -1)
    		{
    			HogeEvent?.Invoke(this, EventArgs.Empty);
    			return;
    		}
    		base.OnCellMouseDown(e);
    	}
    }
    

    • 回答としてマーク kizakura_ui 2016年4月5日 1:21
    2016年4月4日 8:43
    モデレータ
  • 全選択させないのはソースコード見るとMultiSelectプロパティをfalseにしてやればできそう。
    #選択解除されてしまうので使いにくいですが

    列ヘッダークリックによる行のソートを、元々の並び順に戻すことです。

    元々の並び順をデータとして持っている列を作成して、その列でソートさせる方法を取るべきでしょうか?

    並び順を戻すのは、DataSourceへのデータの与え方に依ります。
    DataSourceを設定するやり方をしているなら、DataSourceから作成される並び替え処理を行っているクラスに対して並び替えを解除を行うと元に戻ります。
    直接DataGridView.Rowsに値を入れている場合は、隠し列をつくって順番を入れておくか、行のTagに順番を入れておくという方法もあります。

    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
    	public partial class Form1 : Form
    	{
    		public Form1()
    		{
    			InitializeComponent();
    
    			DataGridView dgv = new DataGridView();
    			dgv.MouseDown += dgv_MouseDown;
    			dgv.AutoGenerateColumns = true;
    			dgv.Dock = DockStyle.Fill;
    			this.Controls.Add(dgv);
    
    			var rnd = new Random();
    			int testType = rnd.Next() % 2;
    
    			DataTable dt = new DataTable();
    			dt.Columns.Add("C0", typeof(string));
    			dt.Columns.Add("C1", typeof(int));
    			for (int i = 0; i < 10; i++)
    			{
    				var row = dt.NewRow();
    				row[0] = "ABC" + i.ToString();
    				row[1] = rnd.Next();
    				dt.Rows.Add(row);
    			}
    			DataSet ds = new DataSet();
    			ds.Tables.Add(dt);
    
    			switch (rnd.Next() % 5)
    			{
    			case 0:
    				dgv.DataSource = dt;
    				this.Text = "DataTable";
    				break;
    			case 1:
    				dgv.DataSource = new DataView(dt);
    				this.Text = "DataView";
    				break;
    			case 2:
    				dgv.DataSource = ds;
    				dgv.DataMember = dt.TableName;
    				this.Text = "DataSet";
    				break;
    			case 3:
    				dgv.DataSource = new BindingSource(dt, "");
    				this.Text = "BindingSource";
    				this.Text = "BindingSource";
    				break;
    			default:
    				dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "C0" });
    				dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "C1", ValueType = typeof(int) });
    				for (int i = 0; i < 10; i++)
    				{
    					int rowIndex = dgv.Rows.Add("ABC" + i.ToString(), rnd.Next());
    					dgv.Rows[rowIndex].Tag = i;//行のタグに元の並び順を入れておく
    				}
    				this.Text = "Direct";
    				break;
    			}
    		}
    
    		private bool isMultiSelect;
    
    		private void dgv_MouseDown(object sender, MouseEventArgs e)
    		{
    			var dgv = (DataGridView)sender;
    
    			var info = dgv.HitTest(e.X, e.Y);
    			if (info.ColumnIndex == -1 && info.RowIndex == -1)
    			{
    				isMultiSelect = dgv.MultiSelect;
    				dgv.MultiSelect = false;
    				dgv.MouseUp += dgv_MouseUp;
    			}
    		}
    
    		private void dgv_MouseUp(object sender, MouseEventArgs e)
    		{
    			var dgv = (DataGridView)sender;
    			dgv.MouseUp -= dgv_MouseUp;
    			dgv.MultiSelect = isMultiSelect;
    
    			OnTopLeftClick(dgv);
    		}
    
    		private void OnTopLeftClick(DataGridView dgv)
    		{
    			if (dgv.SortedColumn != null)
    			{
    
    				IBindingList ibindingList = null;
    				if (dgv.DataSource != null)
    				{
    					var cm = dgv.BindingContext[dgv.DataSource, dgv.DataMember] as CurrencyManager;
    					if (cm != null)
    					{
    						ibindingList = cm.List as IBindingList;
    					}
    					if (ibindingList != null)
    					{
    						ibindingList.RemoveSort();
    					}
    				}
    				else// (dgv.DataSource == null)
    				{
    					//DataSourceにソースを設定せずにDataGridView.Rows.Addで行を追加している場合
    					dgv.Sort(new DataGridViewRowTagSort());
    				}
    
    				//選択解除
    				//dgv.ClearSelection();
    			}
    		}
    
    		class DataGridViewRowTagSort : System.Collections.IComparer
    		{
    			public int Compare(object x, object y)
    			{
    				DataGridViewRow rX = (DataGridViewRow)x;
    				DataGridViewRow rY = (DataGridViewRow)y;
    
    				return ((int)rX.Tag).CompareTo((int)rY.Tag);
    			}
    		}
    	}
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2016年4月4日 11:33
    • 回答としてマーク kizakura_ui 2016年4月5日 2:01
    2016年4月4日 11:20
  • 透明なPanelで覆って操作できなくしてしまう方法があります。以下の例はDataGridView全体を透明なPanelで覆い、DataGridView自体を操作できなくしています。

    Is it possible to disable the left most row selector of DataGridView? i.e. make it unclickable? 
     https://social.msdn.microsoft.com/Forums/windows/en-US/ee0bf1c3-15a9-4f5d-92a3-eacfe2d5c118/is-it-possible-to-disable-the-left-most-row-selector-of-datagridview-ie-make-it-unclickable?forum=winforms

    上記のページを参考にして、DataGridViewの左上のセルのみを透明なPanelで覆って操作できないコードを書いてみました。
    この透明なPanelのClickイベントで、この透明なPanalがクリックされたことはわかりますが、元の並び順に戻すのはケースバイケースだと思います。とりあえず以下の例では最初の列でソートすることによって元の並び順に戻していますが、場合によっては隠し列に並び順を持たせても良いと思います。

    public partial class MainForm : Form { DataTable dataTable; public MainForm() { InitializeComponent(); dataTable = new DataTable(); dataTable.Columns.Add(new DataColumn("Column1")); dataTable.Columns.Add(new DataColumn("Column2")); DataRow newRow = dataTable.NewRow(); newRow[0] = 1; newRow[1] = "z"; dataTable.Rows.Add(newRow); newRow = dataTable.NewRow(); newRow[0] = 2; newRow[1] = "a"; dataTable.Rows.Add(newRow); dataGridView1.DataSource = dataTable; //透明なPanelでDataGridViewの左上のセルを覆う。 var mypanel = new AlphaPanel(); mypanel.Location = dataGridView1.Location; mypanel.Size = dataGridView1.TopLeftHeaderCell.Size; this.Controls.Add(mypanel); mypanel.BringToFront(); mypanel.Click += Mypanel_Click; } private void Mypanel_Click(object sender, EventArgs e) { DataGridViewColumn col = dataGridView1.SortedColumn; //どの列でもソートされていなければ抜ける。 if(col == null) return;

    //最初の列でソートし直す dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Ascending); col = dataGridView1.SortedColumn; col.SortMode = DataGridViewColumnSortMode.NotSortable; //一度グリフを消す。 col.SortMode = DataGridViewColumnSortMode.Automatic; } class AlphaPanel : Panel { const int WS_EX_TRANSPARENT = 0x00000020; protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= WS_EX_TRANSPARENT; return cp; } } protected override void OnPaintBackground(PaintEventArgs e) { //do not paint the background } protected override void OnPaint(PaintEventArgs e) { SolidBrush brush = new SolidBrush(Color.Transparent); e.Graphics.FillRectangle(brush, new Rectangle(0, 0, this.Width, this.Height)); } } }


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/




    2016年4月5日 1:12
    モデレータ

すべての返信

  • こんにちは。

    CellMouseDownをオーバーライドして別イベントを発生させてはどうでしょうか。

    public partial class Form1 : Form
    {
    	public Form1()
    	{
    		InitializeComponent();
    		dataGridView1.HogeEvent += DataGridView1_HogeEvent;
    	}
    
    	private void DataGridView1_HogeEvent(object sender, EventArgs e)
    	{
    		MessageBox.Show("TopLeftHeaderCellがクリックされました。");
    	}
    }
    
    public class DataGridViewEx : DataGridView
    {
    	public event EventHandler HogeEvent;
    
    	protected override void OnCellMouseDown(DataGridViewCellMouseEventArgs e)
    	{
    		if(e.RowIndex == -1 && e.ColumnIndex == -1)
    		{
    			HogeEvent?.Invoke(this, EventArgs.Empty);
    			return;
    		}
    		base.OnCellMouseDown(e);
    	}
    }
    

    • 回答としてマーク kizakura_ui 2016年4月5日 1:21
    2016年4月4日 8:43
    モデレータ
  • 全選択させないのはソースコード見るとMultiSelectプロパティをfalseにしてやればできそう。
    #選択解除されてしまうので使いにくいですが

    列ヘッダークリックによる行のソートを、元々の並び順に戻すことです。

    元々の並び順をデータとして持っている列を作成して、その列でソートさせる方法を取るべきでしょうか?

    並び順を戻すのは、DataSourceへのデータの与え方に依ります。
    DataSourceを設定するやり方をしているなら、DataSourceから作成される並び替え処理を行っているクラスに対して並び替えを解除を行うと元に戻ります。
    直接DataGridView.Rowsに値を入れている場合は、隠し列をつくって順番を入れておくか、行のTagに順番を入れておくという方法もあります。

    using System;
    using System.ComponentModel;
    using System.Data;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
    	public partial class Form1 : Form
    	{
    		public Form1()
    		{
    			InitializeComponent();
    
    			DataGridView dgv = new DataGridView();
    			dgv.MouseDown += dgv_MouseDown;
    			dgv.AutoGenerateColumns = true;
    			dgv.Dock = DockStyle.Fill;
    			this.Controls.Add(dgv);
    
    			var rnd = new Random();
    			int testType = rnd.Next() % 2;
    
    			DataTable dt = new DataTable();
    			dt.Columns.Add("C0", typeof(string));
    			dt.Columns.Add("C1", typeof(int));
    			for (int i = 0; i < 10; i++)
    			{
    				var row = dt.NewRow();
    				row[0] = "ABC" + i.ToString();
    				row[1] = rnd.Next();
    				dt.Rows.Add(row);
    			}
    			DataSet ds = new DataSet();
    			ds.Tables.Add(dt);
    
    			switch (rnd.Next() % 5)
    			{
    			case 0:
    				dgv.DataSource = dt;
    				this.Text = "DataTable";
    				break;
    			case 1:
    				dgv.DataSource = new DataView(dt);
    				this.Text = "DataView";
    				break;
    			case 2:
    				dgv.DataSource = ds;
    				dgv.DataMember = dt.TableName;
    				this.Text = "DataSet";
    				break;
    			case 3:
    				dgv.DataSource = new BindingSource(dt, "");
    				this.Text = "BindingSource";
    				this.Text = "BindingSource";
    				break;
    			default:
    				dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "C0" });
    				dgv.Columns.Add(new DataGridViewTextBoxColumn() { HeaderText = "C1", ValueType = typeof(int) });
    				for (int i = 0; i < 10; i++)
    				{
    					int rowIndex = dgv.Rows.Add("ABC" + i.ToString(), rnd.Next());
    					dgv.Rows[rowIndex].Tag = i;//行のタグに元の並び順を入れておく
    				}
    				this.Text = "Direct";
    				break;
    			}
    		}
    
    		private bool isMultiSelect;
    
    		private void dgv_MouseDown(object sender, MouseEventArgs e)
    		{
    			var dgv = (DataGridView)sender;
    
    			var info = dgv.HitTest(e.X, e.Y);
    			if (info.ColumnIndex == -1 && info.RowIndex == -1)
    			{
    				isMultiSelect = dgv.MultiSelect;
    				dgv.MultiSelect = false;
    				dgv.MouseUp += dgv_MouseUp;
    			}
    		}
    
    		private void dgv_MouseUp(object sender, MouseEventArgs e)
    		{
    			var dgv = (DataGridView)sender;
    			dgv.MouseUp -= dgv_MouseUp;
    			dgv.MultiSelect = isMultiSelect;
    
    			OnTopLeftClick(dgv);
    		}
    
    		private void OnTopLeftClick(DataGridView dgv)
    		{
    			if (dgv.SortedColumn != null)
    			{
    
    				IBindingList ibindingList = null;
    				if (dgv.DataSource != null)
    				{
    					var cm = dgv.BindingContext[dgv.DataSource, dgv.DataMember] as CurrencyManager;
    					if (cm != null)
    					{
    						ibindingList = cm.List as IBindingList;
    					}
    					if (ibindingList != null)
    					{
    						ibindingList.RemoveSort();
    					}
    				}
    				else// (dgv.DataSource == null)
    				{
    					//DataSourceにソースを設定せずにDataGridView.Rows.Addで行を追加している場合
    					dgv.Sort(new DataGridViewRowTagSort());
    				}
    
    				//選択解除
    				//dgv.ClearSelection();
    			}
    		}
    
    		class DataGridViewRowTagSort : System.Collections.IComparer
    		{
    			public int Compare(object x, object y)
    			{
    				DataGridViewRow rX = (DataGridViewRow)x;
    				DataGridViewRow rY = (DataGridViewRow)y;
    
    				return ((int)rX.Tag).CompareTo((int)rY.Tag);
    			}
    		}
    	}
    }


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2016年4月4日 11:33
    • 回答としてマーク kizakura_ui 2016年4月5日 2:01
    2016年4月4日 11:20
  • 透明なPanelで覆って操作できなくしてしまう方法があります。以下の例はDataGridView全体を透明なPanelで覆い、DataGridView自体を操作できなくしています。

    Is it possible to disable the left most row selector of DataGridView? i.e. make it unclickable? 
     https://social.msdn.microsoft.com/Forums/windows/en-US/ee0bf1c3-15a9-4f5d-92a3-eacfe2d5c118/is-it-possible-to-disable-the-left-most-row-selector-of-datagridview-ie-make-it-unclickable?forum=winforms

    上記のページを参考にして、DataGridViewの左上のセルのみを透明なPanelで覆って操作できないコードを書いてみました。
    この透明なPanelのClickイベントで、この透明なPanalがクリックされたことはわかりますが、元の並び順に戻すのはケースバイケースだと思います。とりあえず以下の例では最初の列でソートすることによって元の並び順に戻していますが、場合によっては隠し列に並び順を持たせても良いと思います。

    public partial class MainForm : Form { DataTable dataTable; public MainForm() { InitializeComponent(); dataTable = new DataTable(); dataTable.Columns.Add(new DataColumn("Column1")); dataTable.Columns.Add(new DataColumn("Column2")); DataRow newRow = dataTable.NewRow(); newRow[0] = 1; newRow[1] = "z"; dataTable.Rows.Add(newRow); newRow = dataTable.NewRow(); newRow[0] = 2; newRow[1] = "a"; dataTable.Rows.Add(newRow); dataGridView1.DataSource = dataTable; //透明なPanelでDataGridViewの左上のセルを覆う。 var mypanel = new AlphaPanel(); mypanel.Location = dataGridView1.Location; mypanel.Size = dataGridView1.TopLeftHeaderCell.Size; this.Controls.Add(mypanel); mypanel.BringToFront(); mypanel.Click += Mypanel_Click; } private void Mypanel_Click(object sender, EventArgs e) { DataGridViewColumn col = dataGridView1.SortedColumn; //どの列でもソートされていなければ抜ける。 if(col == null) return;

    //最初の列でソートし直す dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Ascending); col = dataGridView1.SortedColumn; col.SortMode = DataGridViewColumnSortMode.NotSortable; //一度グリフを消す。 col.SortMode = DataGridViewColumnSortMode.Automatic; } class AlphaPanel : Panel { const int WS_EX_TRANSPARENT = 0x00000020; protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ExStyle |= WS_EX_TRANSPARENT; return cp; } } protected override void OnPaintBackground(PaintEventArgs e) { //do not paint the background } protected override void OnPaint(PaintEventArgs e) { SolidBrush brush = new SolidBrush(Color.Transparent); e.Graphics.FillRectangle(brush, new Rectangle(0, 0, this.Width, this.Height)); } } }


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/




    2016年4月5日 1:12
    モデレータ
  • Tak1wa様

    さっそくご回答くださり、ありがとうございました。

    コーディングをそのまま使わせていただいたところ、希望通りの結果が得られました!

    >   HogeEvent?.Invoke(this, EventArgs.Empty);
    このコードの意味がよくわらないので、これから勉強させていただきます。

    ご指導、ありがとうございました。

    今後ともよろしくお願いいたします。

    追伸:

    ”HogeEvent?” ←末尾の ’?’ は、名称を適当に変更するように、という意味でしょうか?


    kizakura_ui

    2016年4月5日 1:21
  • gekka様

    いつもご指導、ありがとうございます。

    色々なパターンに対応できるようなソースを書いてくださり、大変勉強になりました。

                dataView = new DataView(dataTable);
                dataSource = new BindingSource();
    
                dataSource.DataSource = dataView;
                dataSource.DataMember = "";
                bindingNavigator1.BindingSource = dataSource;
                dataGridView1.DataSource = dataSource;
    

    DataSourceを設定していますので、RemoveSort()で、元の並び順に戻すことができました。

    IBindingListの使い方を勉強して、他の処理でも活用できたら、と思っています。

    質問内容以上のご指導を、懇切丁寧にしてくださりありがとうございました。

    今後ともよろしくお願いいたします。


    kizakura_ui

    2016年4月5日 2:43
  • trapemiya様

    ご指導、ありがとうございます。

    ”透明なPanelで覆う”なんて、想像もつきませんでした。

    というか、圧倒的に知識が欠如してるので、大変勉強になりました。

    そして、希望通りの結果が得られました。

    >場合によっては隠し列に並び順を持たせても良いと思います。

    これは元々保持していましたので、この列のソートにより元に戻すこともできました。

    教えてくださった皆様、それぞれに、すぐ実行できる形でソースを添付してくださったので、

    悩むことなく使わせていただけました。

    ありがとうございました。

    今後ともよろしくお願いいたします。


    kizakura_ui

    2016年4月5日 4:35
  • 追伸:

    ”HogeEvent?” ←末尾の ’?’ は、名称を適当に変更するように、という意味でしょうか?

    NULL条件演算子です。
    https://msdn.microsoft.com/ja-jp/library/dn986595.aspx

    VisualStudio2013だと使用できなかったかもしれません、失礼しました。
    上記URLに記載されていますが、以下と同様です。(イベント名は任意)

    PropertyChanged?.Invoke(e)
    var handler = this.PropertyChanged;
    if (handler != null)
        handler(…)

    2016年4月5日 4:42
    モデレータ
  • Tak1wa様

    知識不足でお恥ずかしいです。

    ご指導、ありがとうございました。


    kizakura_ui

    2016年4月5日 4:59