次の方法で共有


DataGridViewの日付カラムにDateTimePickerを使って日付を入力したいのですが

質問

2014年3月24日月曜日 13:33

Windows 8.1 上のMicrosoft Visual C# 2010 で

NpgsqlでPostgreSQL 9.3 へ接続しています。 

データグリッドビューのフィールド「hiduke」の入力にDateTimePickerを使用しようと
以下を参考にしています。

「Windows フォーム DataGridView Cells でコントロールをホストする」
URL http://msdn.microsoft.com/ja-jp/library/7tas5c80(VS.80).aspx

「DataGridView で DateTimePicker を表示できたりできなかったり」
URL http://social.msdn.microsoft.com/Forums/ja-JP/8ae3efbc-d505-4603-a907-33feda7eded3/datagridview-datetimepicker-?forum=csharpexpressja

DataGridViewの「hiduke」セルに初期表示値で当日の日付が表示され
クリック3回でDateTimePickerを表示させることまでは出来たのですが
そのままでは、値が消えてしまうのか(日付は表示されていても)
いざDBへINSERTをしようとしてもhidukeの値が空になっているのかINSERTされません。
また、DateTimePickerで日付を選択するときも、初期表示値のままでは
hidukeの値が空になっているのか、なぜか表示日付以外の日付を選択して
やらないと、INSERTされません。
どうすれば、DateTimePickerの値をDBのhidukeカラムに反映させることが
できるのでしょうか?

下記、コード抜粋です。

// フォームロード時
private void Form1_Load(object sender, EventArgs e)
{
    CalendarColumn col = new CalendarColumn();
    this.dataGridView1.Columns.Insert(1,col);
    this.dataGridView1.Columns[1].HeaderText = "日付";
}
// DGVの規定値
private void dataGridView1_DefaultValuesNeeded
(
object sender, DataGridViewRowEventArgs e
)
{
//セルの既定値を指定する
e.Row.Cells["hiduke"].Value = DateTime.Now.ToShortDateString();
}
// insert
da.InsertCommand = new NpgsqlCommand
(
"insert into t_yakuhin_suitou (hiduke) values (:hiduke)",
                m_conn
            );

da.InsertCommand.Parameters.Add
(
new NpgsqlParameter
(
"hiduke"
, NpgsqlTypes.NpgsqlDbType.Date
, 0
, "col"
, ParameterDirection.Input
, false
, 0
, 0
, DataRowVersion.Current
, DBNull.Value
)
);

よろしくお願いします。

すべての返信 (4)

2014年3月24日月曜日 17:36 ✅回答済み

DataGridView.NotifyCurrentCellDirtyを呼ばないとセルが編集されたことが伝わりません。

Insertされる前にブレークポイントを設定して、実際に値が入っているかどうか確認してみましょう

using System;
using System.Data;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            DataGridView dgv;
            dgv = new DataGridView();
            dgv.Dock = DockStyle.Fill;
            dgv.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom;
            dgv.Height = this.ClientSize.Height;
            dgv.Width = this.ClientSize.Width / 2;
            dgv.Columns.Add(new CalendarColumn() { DataPropertyName = "Column1" });
            //dgv.Columns.Add(new DataGridViewTextBoxColumn());
            this.Controls.Add(dgv);

            DataTable table;
            table = new DataTable();
            table.Columns.Add(new DataColumn("Column1", typeof(DateTime)));// { DefaultValue = new DateTime(2000, 1, 1) }); //AllowDBNullでは無いならDefaultValueを入れておく
            table.Rows.Add(DateTime.Now);
            table.Rows.Add(DateTime.Now.AddDays(1));

            dgv.DataSource = table;
        }
    }

    public class CalendarColumn : DataGridViewColumn
    {
        public CalendarColumn()
            : base(new CalendarCell())
        {
        }

        public override DataGridViewCell CellTemplate
        {
            get { return base.CellTemplate; }
            set
            {
                if (value != null && !value.GetType().IsAssignableFrom(typeof(CalendarCell)))
                {
                    throw new InvalidCastException("Must be a CalendarCell");
                }
                base.CellTemplate = value;
            }
        }
    }

    public class CalendarCell : DataGridViewTextBoxCell
    {
        public CalendarCell()
            : base()
        {
            this.Style.Format = "d";
        }

        public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
            CalendarEditingControl ctl = DataGridView.EditingControl as CalendarEditingControl;
            if (this.Value == null || this.Value == DBNull.Value)
            {
                DateTime dt = DateTime.Now.Date;
                ctl.Value = dt;
            }
            else
            {
                ctl.Value = (DateTime)this.Value;
            }
        }

        public override Type EditType
        {
            get { return typeof(CalendarEditingControl); }
        }

        public override Type ValueType
        {
            get { return typeof(DateTime); }
        }

        public override object DefaultNewRowValue
        {
            get { return null; } //未編集の新規行に余計な初期値が出ないようにする
        }
    }

    class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;

        public CalendarEditingControl()
        {
            this.Format = DateTimePickerFormat.Short;
        }
        public object EditingControlFormattedValue
        {
            get
            {
                return this.Value.ToShortDateString();
            }
            set
            {
                String newValue = value as String;
                if (newValue != null)
                {
                    this.Value = DateTime.Parse(newValue);
                }
            }
        }

        public object GetEditingControlFormattedValue(
            DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        public void ApplyCellStyleToEditingControl(
            DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
            this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
            this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
        }

        public int EditingControlRowIndex
        {
            get { return rowIndex; }
            set { rowIndex = value; }
        }

        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            switch (key & Keys.KeyCode)
            {
            case Keys.Left:
            case Keys.Up:
            case Keys.Down:
            case Keys.Right:
            case Keys.Home:
            case Keys.End:
            case Keys.PageDown:
            case Keys.PageUp:
                return true;
            default:
                return false;
            }
        }

        public void PrepareEditingControlForEdit(bool selectAll)
        {
            //編集開始状態になった時点で編集されたことにしてしまう
            EditingControlValueChanged = true;
        }

        public bool RepositionEditingControlOnValueChange
        {
            get { return false; }
        }

        public DataGridView EditingControlDataGridView
        {
            get { return dataGridView; }
            set { dataGridView = value; }
        }

        public bool EditingControlValueChanged
        {
            get { return valueChanged; }
            set
            {
                valueChanged = value;
                if (value && this.EditingControlDataGridView != null)
                {
                    this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
                }
            }
        }

        public Cursor EditingPanelCursor
        {
            get { return base.Cursor; }
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            EditingControlValueChanged = true;
            base.OnValueChanged(eventargs);
        }
    }
}

DataGridView.DataSourceにたぶんDataTableが入ってるのだろうとみなしてみる

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


2014年3月25日火曜日 3:00 ✅回答済み

//セルの既定値を指定する
e.Row.Cells["hiduke"].Value = DateTime.Now.ToShortDateString();

と書かれていますが、既定値を設定するのであればデータソース側で行うのが筋です。その結果をDataGridViewにも表示するようにします。そうすれば、DataGridViewで編集されたかどうかを意識する必要はありません。
データベースを利用されていますので、おそらくデータソースはDataTableになっていると思いますので、そちらに既定値を設定されると良いと思います。

#上記のようにすれば、gekkaさんのコードでPrepareEditingControlForEditは空で良く、DataGridView.NotifyCurrentCellDirtyはOnValueChangedに書くという一般的な形になると思います。ちなみに、gekkaさんも既定値はDataTableで設定されていますね。コードを斜め読みしているだけなのでおかしなことを言っていたらごめんなさい。

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2014年3月25日火曜日 8:17

DataGridViewColumn.DataPropertyName プロパティが設定されてないようですが、それは問題ないのですか?

DataGridViewColumn.DataPropertyName プロパティ
http://msdn.microsoft.com/ja-jp/library/system.windows.forms.datagridviewcolumn.datapropertyname(v=vs.100).aspx

データソースプロパティの名前またはデータベースの列の名前を設定するのが普通で、設定値が "hiduke" でいいのかどうは質問者さんの実装によるので分かりませんが、形としては以下のようにはずです。

private void Form1_Load(object sender, EventArgs e)
{
    CalendarColumn col = new CalendarColumn();

    // ↓ これが必要では?
    col.DataPropertyName = "hiduke";

    // ・・・略・・・

}

今回の問題とは関係ないかもしれませんが、CalendarColumn に以下のページのコードをそのままコピペして使っている場合、InitializeEditingControl メソッドで this.Value が DateTime にキャストできずエラーになるケース(null でなく DBNull.Value が入っている)があり、それに対処するためコードを修正しなければならないかもしれませんので注意してください。

方法 : Windows フォーム DataGridView Cells でコントロールをホストする
http://msdn.microsoft.com/ja-jp/library/7tas5c80(v=vs.100).aspx


2014年3月31日月曜日 1:26

nc8g さん、こんにちは。
フォーラム オペレーターの星 睦美です。

今回は私のほうでgekka さんとtrapemiya さんの返信に[回答としてマーク] させていただきました。
SurferOnWww さんからもコメントをいただいていますので、もし引き続き質問したい内容がありましたら
新しい質問として投稿いただければと思います。

それでは今後ともMSDN フォーラムをお役立てください。

フォーラム オペレーター 星 睦美 - MSDN Community Support