none
DataGridにDataTimePickerカラムを追加した場合、TABキーの入力欄に止まらない RRS feed

  • 質問

  • お世話になっております。

    早速ですが、DataGridにDataTimePickerによる入力を実現するために、CustomDataTimePickerと、それを適用したDataGridTimePickerColumn(DataGridColumnStyleをカスタマイズ)を作成しました。

    (ソースを本文最後に示す)

    上記のCustomDataTimePickerとDataGridTimePickerColumnを利用してみたら、DataGridでカレンダーを利用した日付入力が実現できました。

    動作:DataGridTimePickerColumnを適用した行にフォーカスを移動すると、カレンダー入力が可能になり、Alt+↓により、カレンダーが表示される(Alt+↑によりカレンダーが隠れる)。

     

    問題点:Tabキーを利用してDataGridTimePickerColumnを適用した行にフォーカスを移動すると自動的につぎのカラムへ滑り込んでしまし、フォーカスがとまらない(→、←キーにより移動すればただしく動作する)。

     

    いろいろ調べてみましたが、力が足りなくて、解決できませんでした。

    原因と対策がご存知の方ぜひご教授しください。

    ■ソース1:CustomDataTimePicker

    ----------------

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using TAS.Widgets;

    namespace TAS.Widgets {
     public class DateTimePicker : System.Windows.Forms.DateTimePicker {

      int sign = 0;
      private System.Windows.Forms.TextBox txtDateTime;
      private System.ComponentModel.IContainer components;
      private bool SetDate;
      private const int BTNWIDTH = 16;


      //
      //テキストボックスにフォーカスを当たる
      //
      public void TextGetFocus() {
       txtDateTime.Focus();
       sign  = 1;
      }


      public enum dtpCustomExtensions {
       dtpLong = 0,
       dtpShort = 1,
       dtpTime = 2,
       dtpCustom = 28
      }

      public TextBox textBox {
       get {
        return txtDateTime;
       }
      }

      private bool bDroppedDown;
      private int ButtonWidth = BTNWIDTH;
      private bool mvarShowButtons = true;
      private dtpCustomExtensions mvarFormatEx;
      private int CheckWidth = 0;


      #region Constructor and destructor

      public DateTimePicker()
      {
       // This call is required by the Windows.Forms Form Designer.
       InitializeComponent();

       // TODO: Add any initialization after the InitForm call
       //Initialise bas.Format to Custom, we only need Custom Format
       base.Format = System.Windows.Forms.DateTimePickerFormat.Custom;
       DateTimePicker_Resize(this, null);

      }

     

      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
       if( disposing )
       {
        if( components != null )
         components.Dispose();
       }
       base.Dispose( disposing );
      }

      #endregion Constructor and destructor

      #region Component Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
       this.components = new System.ComponentModel.Container();
       this.txtDateTime = new System.Windows.Forms.TextBox();
       this.SuspendLayout();
       //
       // txtDateTime
       //
       this.txtDateTime.Location = new System.Drawing.Point(20, 49);
       this.txtDateTime.MaxLength = 50;
       this.txtDateTime.Name = "txtDateTime";
       this.txtDateTime.TabIndex = 0;
       this.txtDateTime.Text = "";
       //★★ ★★
       this.txtDateTime.Enter += new System.EventHandler(this.txtDateTime_Enter);
       this.txtDateTime.Validating +=new CancelEventHandler(txtDateTime_Validating);


       //
       // DateTimePicker
       //
       this.Controls.AddRange(new System.Windows.Forms.Control[] {this.txtDateTime});
       this.Value = new System.DateTime(1753, 1, 1, 15, 8, 40, 119);
       this.DropDown += new System.EventHandler(this.DateTimePicker_DropDown);
       this.Resize += new System.EventHandler(this.DateTimePicker_Resize);

     

       //★★ ★★
       this.Enter += new System.EventHandler(this.DateTimePicker_Enter);
       this.CloseUp += new System.EventHandler(this.DateTimePicker_CloseUp);
       this.ResumeLayout(false);
     

      }
      #endregion

      #region overriden and additional properties

      //OverRide Formst and hide it by setting Browsable false, make it read only
      //so it can't be written to, it will always be Custom anyway
      [Browsable(false)]
      public new System.Windows.Forms.DateTimePickerFormat Format
      {
       get
       {
        return base.Format;
       }
    //   set
    //   {
    //    base.Format = value;
    //   }
      }

      //FormatEx, extends the formatting options by allowing additional selections
      //Replaces Format
      [Browsable(true), Category("Appearance"), Description("Format Extensions replaces Format gets sets display Formats")]
      public dtpCustomExtensions FormatEx
      {
       get
       {
        return mvarFormatEx;
       }
       set
       {
        mvarFormatEx = value;
    //    InitialiseCustomMessage();
       }
      }

      //New Property, allows hiding of DropDown Button and Updown Button
      [Browsable(true), Category("Appearance"), Description("Hides DropDown and Spin Buttons, Allows keyed entry only.")]
      public bool ShowButtons
      {
       get
       {
        return mvarShowButtons;
       }
       set
       {
        //Do not allow Set Show Buttons when ReadOnly is true
        //all Buttons and Chexkbox are hidden when Control is Read Only
        if (!this.ReadOnly)
        {
         mvarShowButtons = value;
         if (mvarShowButtons)
         {
          ButtonWidth = BTNWIDTH;
         }
         else
         {
          ButtonWidth = 0;
         }
         DateTimePicker_Resize(this, null);
        }
       }
      }

      //Overrides base.ShowCheckBox
      [Browsable(true), Category("Appearance"), Description("Hides DropDown and Spin Buttons, Allows keyed entry only.")]
      public new bool ShowCheckBox
      {
       get
       {
        return base.ShowCheckBox;
       }
       set
       {
        //Do not allow set ShowCheckBox when ReadOnly is True
        //all Buttons and Chexkbox are hidden when Control is Read Only
        if (!this.ReadOnly)
        {
         base.ShowCheckBox = value;
         if (base.ShowCheckBox)
         {
          CheckWidth = BTNWIDTH;
         }
         else
         {
          CheckWidth = 0;
         }
         DateTimePicker_Resize(this,null);
        }
       }
      }
      
      //overrie Text, we want to set Get Textbox Text
      [Browsable(true), Category("Behavior"), Description("Date and Time displayed")]
      public new string Text
      {
       get
       {
        return txtDateTime.Text;
       }
       set
       {
        txtDateTime.Text = value;
        //Don't bother Formatting the Textbox if it's value is NullString
        //It will cause problems if you do
        if (value != "")
        {
         FormatTextBox();
        }
       }
      }

      //Override bas.ShowUpDown
      [Browsable(true), Category("Appearance"), Description("Uses Updown control to select dates instead of Dropdown control")]
      public new bool ShowUpDown
      {
       get
       {
        return base.ShowUpDown;
       }
       set
       {
        //Do not allow set ShowUpDown when ReadOnly is True
        //all Buttons and Checkbox are hidden when Control is Read Only
        if (!this.ReadOnly)
        {
         base.ShowUpDown = value;
         txtDateTime.Text = "";
        }
       }
      }

      //Override Textbox back Colour so we can add it to the Appearance List
      //and use it to set the BG colour
      [Browsable(true), Category("Appearance"), Description("The Backround Colour user to display Text and Graphics in this Control")]
      public new System.Drawing.Color BackColor
      {
       get
       {
        return base.BackColor;
       }
       set
       {
        base.BackColor = value;
       }
      }

      //New Property Read Only makes it possible to set Textbox to read only
      [Browsable(true), Category("Behavior"), Description("Used to set whether the control can be edited")]
      public bool ReadOnly
      {
       get
       {
        return txtDateTime.ReadOnly;
       }
       set
       {
        //If ReadOnly is true make sure ShowCheckBox, ShowUpDown and ShowButtons
        //are false.
        //all Buttons and Checkbox are hidden when Control is Read Only
        //Be aware of the order these properties are set
        if (value)
        {
         this.ShowCheckBox = false;
         this.ShowUpDown = false;
         this.ShowButtons = false;
         txtDateTime.ReadOnly = value;
        }
        else
        {
         txtDateTime.ReadOnly = value;
         this.ShowButtons = true;
        }
       }
      }


      #endregion

      #region DateTimePicker events

      private void DateTimePicker_Resize(object sender, System.EventArgs e)
      {
       this.txtDateTime.Location = new System.Drawing.Point(-2 + CheckWidth, -2);
       this.txtDateTime.Size = new System.Drawing.Size(this.Width - ButtonWidth - CheckWidth, this.Height);
      }


      
      //★★★ ★★★
      private void txtDateTime_Enter(Object sender, System.EventArgs e  )
      {
       //this.txtDateTime.AppendText("");
       //this.txtDateTime.SelectNextControl(txtDateTime,false,true,true,false);


    //MessageBox.Show("txtDateTime_Enter");
    //   if (txtDateTime.Text.Length > 0 )
    //   {
    //    txtDateTime.SelectionStart = 0;
    ////    txtDateTime.SelectionLength = txtDateTime.Text.Length;
    //   }
    //
    //   SetDate = true;
       this.Value = DateTime.Now;
    //   SetDate = false;
    //
    //   //★★★ ★★★
    //   //SendKeys.Send("{F2}");

      }
    //
    //  private void txtDateTime_KeyDown(object sender, KeyEventArgs e) {
    //  
    //  }


      //
      //
      //
      private void DateTimePicker_Enter(Object sender  ,System.EventArgs  e  )
      {
       
    //   txtDateTime.TabStop = true;
    //   txtDateTime.TabIndex = 1;

       if (txtDateTime.Text.Length > 0 )
       {
        txtDateTime.SelectionStart = 0;
        //txtDateTime.SelectionLength = txtDateTime.Text.Length;
       }

       e = null;
       
      }


      private void DateTimePicker_DropDown(Object sender  , System.EventArgs  e )
      {
       bDroppedDown = true;
    //-------------------------
       this.Focus();
      }

      private void DateTimePicker_CloseUp(object sender, System.EventArgs e)
      {
       if (bDroppedDown || this.ShowUpDown )
       {
        if (! SetDate )
        {
         txtDateTime.Text = this.Value.ToString();
         FormatTextBox();
         bDroppedDown = false;
         txtDateTime.Focus();
        }
       }
      }
     
      protected override void OnValueChanged(System.EventArgs eventargs  )
      {
       
       
       if (bDroppedDown || this.ShowUpDown )
       {
        if (! SetDate )
        {
         txtDateTime.Text = this.Value.ToString();
         FormatTextBox();
        }
       }
      }
      

      //Dispplay dates Times etc, based on Format selected
      private void FormatTextBox()
      {

       switch (mvarFormatEx)
       {
        case dtpCustomExtensions.dtpCustom:
         this.Value = DateTime.Parse(txtDateTime.Text);
         txtDateTime.Text = this.Value.ToString(this.CustomFormat, Application.CurrentCulture);
         break;
        case dtpCustomExtensions.dtpShort:
         try {
          this.Value = DateTime.Parse(txtDateTime.Text);
          txtDateTime.Text = this.Value.ToShortDateString();

         } catch (Exception) {
          //this.Value = new DateTime();
          //txtDateTime.Text = "";
         }
         
         break;
        default:
         break;
       }
      }

      #endregion

     

    //****************************
      protected override bool ProcessKeyMessage(ref Message m) {
       // Keep all the keys for the DateTimePicker.
       return ProcessKeyEventArgs(ref m);
       //return true;
      }


      protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {

       const int WM_KEYDOWN = 0x100;
       const int WM_SYSTEMDOWN = 0x104;
        const int WM_SYSKEYDOWN = 0x104;
        const int VK_DOWN = 0x28;


       if (!isBadCell) {
     
          if (msg.Msg == WM_KEYDOWN || msg.Msg == WM_SYSTEMDOWN) {

         if (keyData == (Keys.Alt | Keys.Down)) {

          SendMessage(this.Handle, WM_SYSKEYDOWN, VK_DOWN, 0);
          
         }
        }

     

        return base.ProcessCmdKey(ref msg, keyData);

     

       } else {

        return false;

       }

      }
      

      [System.Runtime.InteropServices.DllImport("User32.dll",EntryPoint="SendMessageA")]
      public static extern int SendMessage(IntPtr hWnd, int nMsg, int wParam, int lParam);
      private bool isBadCell = false;


      private void txtDateTime_Validating(object sender, CancelEventArgs e) {
       //e.Cancel =true;

      }
     }
    }
    -----------------------------

     

    ■ソース2:DataGridTimePickerColumn(テスト中なので、コメントアウトされた行がちょっと多いです)

    ----------------------

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


    namespace TAS
    {

     public class DataGridTimePickerColumn : DataGridColumnStyle {

      private int noEditrow;
      
      //  private CustomDateTimePicker customDateTimePicker1 =
      //   new CustomDateTimePicker();
      private TAS.Widgets.DateTimePicker customDateTimePicker1 =
       new TAS.Widgets.DateTimePicker();

       
      public TAS.Widgets.DateTimePicker CustomDateTimePicker {
       get{
        return customDateTimePicker1;
        //return new TAS.Widgets.DateTimePicker();
       }

      }
         

       // The isEditing field tracks whether or not the user is
       // editing data with the hosted control.
       private bool isEditing;

       public DataGridTimePickerColumn()
        : base() {
        customDateTimePicker1.Visible = false;
        customDateTimePicker1.FormatEx =
         TAS.Widgets.DateTimePicker.dtpCustomExtensions.dtpShort;
    //    customDateTimePicker1.Validating += new System.ComponentModel.CancelEventHandler(customDateTimePicker1_Validating);

       }

      public DataGridTimePickerColumn(int x)
       : base() {
       customDateTimePicker1.Visible = false;
       customDateTimePicker1.FormatEx =
        TAS.Widgets.DateTimePicker.dtpCustomExtensions.dtpShort;
       
       // 入力不可の行番を設定
       this.noEditrow = x;
      }

      public void setNoEditRow(int x) {

       this.noEditrow = x;

      }

     

       protected override void Abort(int rowNum) {

        //isEditing = false;
        isEditing = true;
    //    customDateTimePicker1.ValueChanged -=new EventHandler(TimePickerValueChanged);
    //    customDateTimePicker1.TextChanged -= new EventHandler(customDateTimePicker1_TextChanged);
        customDateTimePicker1.textBox.TextChanged -= new EventHandler(textBox_TextChanged);
        
        Invalidate();
       }

       protected override bool Commit(CurrencyManager dataSource, int rowNum) {

        customDateTimePicker1.Bounds = Rectangle.Empty;
    //    customDateTimePicker1.ValueChanged -=new EventHandler(TimePickerValueChanged);
    //    customDateTimePicker1.TextChanged -= new EventHandler(customDateTimePicker1_TextChanged);
        customDateTimePicker1.textBox.TextChanged -= new EventHandler(textBox_TextChanged);

        if (!isEditing)
         return true;

        isEditing = false;

        try {

         //DateTime value = customDateTimePicker1.Value;
         String value = customDateTimePicker1.Text;
         SetColumnValueAtRow(dataSource, rowNum, value);

         if (!check(value)) {
          throw new Exception();
         }
         //SetColumnValueAtRow(dataSource, rowNum, value);

        }
        catch (Exception) {
         Abort(rowNum);
    //customDateTimePicker1.textBox.Focus();     
         return false;
        }

        Invalidate();
        return true;

       }


      private bool check (String value) {
    //   try{
    //    DateTime newTime = DateTime.Parse(value);
    //
    //   } catch(Exception) {
    //    return false;
    //   }

       if (value.Equals("2006/02/31"))
       {
        return false;
       }
       return true;
      }


      protected override void Edit(
       CurrencyManager source,
       int rowNum,
       Rectangle bounds,
       bool readOnly,
       string displayText,
       bool cellIsVisible) {
         
       if (rowNum == this.noEditrow) {
        // 入力不可の場合、リードオンリに設定
        readOnly = true;

       } else {
        //    DateTime value = (DateTime)
        //     GetColumnValueAtRow(source, rowNum);
        String value = GetColumnValueAtRow(source, rowNum).ToString();

        if (cellIsVisible) {
         customDateTimePicker1.Bounds = new Rectangle
          (bounds.X + 2, bounds.Y + 2,
          bounds.Width - 4, bounds.Height - 4);

         //customDateTimePicker1.Value = value;
         customDateTimePicker1.Text = value;
         customDateTimePicker1.Visible = true;
    //     customDateTimePicker1.ValueChanged += new EventHandler(TimePickerValueChanged);
    //     customDateTimePicker1.TextChanged += new EventHandler(customDateTimePicker1_TextChanged);
         customDateTimePicker1.textBox.TextChanged += new EventHandler(textBox_TextChanged);
         
         //customDateTimePicker1.textBox.Validating += new System.ComponentModel.CancelEventHandler(textBox_Validating);
        
        }
        else {
         //customDateTimePicker1.Value = value;
         customDateTimePicker1.Text = value;
         customDateTimePicker1.Visible = false;
        }

        if (customDateTimePicker1.Visible)
         DataGridTableStyle.DataGrid.Invalidate(bounds);

        //customDateTimePicker1.Focus();
        customDateTimePicker1.TextGetFocus();
        
       }
      }


       protected override Size GetPreferredSize(
        Graphics g,
        object value) {
        //return new Size(100, customDateTimePicker1.PreferredHeight + 4);
        return new Size(100, customDateTimePicker1.Height + 4);
       }

       protected override int GetMinimumHeight() {
        //return customDateTimePicker1.PreferredHeight + 4;
        return customDateTimePicker1.Height + 4;
       }

       protected override int GetPreferredHeight(Graphics g,
        object value) {
        //return customDateTimePicker1.PreferredHeight + 4;
        return customDateTimePicker1.Height + 4;
       }


       // 必須
       protected override void Paint(Graphics g,
        Rectangle bounds,
        CurrencyManager source,
        int rowNum) {

        Paint(g, bounds, source, rowNum, false);

       }
     
     

       // 必須
       protected override void Paint(
        Graphics g,
        Rectangle bounds,
        CurrencyManager source,
        int rowNum,
        bool alignToRight) {
        Paint(
         g, bounds,
         source,
         rowNum,
         Brushes.Red,
         Brushes.Blue,
         alignToRight);

       }
     
       //必須
       protected override void Paint(
        Graphics g,
        Rectangle bounds,
        CurrencyManager source,
        int rowNum,
        Brush backBrush,
        Brush foreBrush,
        bool alignToRight) {

        //backBrush = new SolidBrush(CommonValue.ControlBackColorDisable);
        //base.Paint(g, bounds, source, rowNum, backBrush, foreBrush, alignToRight);
        //指定行の背景色を変更
        if (rowNum == this.noEditrow || rowNum == 1)
        {
         backBrush = new SolidBrush(Color.Red);
        }

        //    DateTime date = (DateTime)
        //     GetColumnValueAtRow(source, rowNum);
        String date = GetColumnValueAtRow(source, rowNum).ToString();    
        Rectangle rect = bounds;
        g.FillRectangle(backBrush, rect);
        rect.Offset(0, 2);
        rect.Height -= 2;
        g.DrawString(
         //date.ToString("d"),
         date,
         this.DataGridTableStyle.DataGrid.Font,
         foreBrush, rect);

       }

      
       //dataTimePickerをセルに表示
       protected override void SetDataGridInColumn(DataGrid value) {
        base.SetDataGridInColumn(value);
        if (customDateTimePicker1.Parent != null) {
         customDateTimePicker1.Parent.Controls.Remove
          (customDateTimePicker1);
        }
        if (value != null) {
         value.Controls.Add(customDateTimePicker1);
        }
       }

    //   private void TimePickerValueChanged(object sender, EventArgs e) {
    //
    //    // Remove the handler to prevent it from being called twice in a row.
    //    customDateTimePicker1.ValueChanged -= new EventHandler(TimePickerValueChanged);
    //    this.isEditing = true;
    //    base.ColumnStartedEditing(customDateTimePicker1);
    //
    //   }

       private void customDateTimePicker1_TextChanged(object sender, EventArgs e) {
        customDateTimePicker1.TextChanged -= new EventHandler(customDateTimePicker1_TextChanged);    
        this.isEditing = true;
        base.ColumnStartedEditing(customDateTimePicker1);
       }

    //   private void customDateTimePicker1_Validating(object sender, System.ComponentModel.CancelEventArgs e) {
    //    MessageBox.Show(customDateTimePicker1.Text);
      //   }

      private void textBox_TextChanged(object sender, EventArgs e) {
       customDateTimePicker1.TextChanged -= new EventHandler(customDateTimePicker1_TextChanged);    
       customDateTimePicker1.textBox.TextChanged -= new EventHandler(textBox_TextChanged);
       this.isEditing = true;
       base.ColumnStartedEditing(customDateTimePicker1);
      }

      private void textBox_Validating(object sender, System.ComponentModel.CancelEventArgs e) {
    //   TextBox textbox = (TextBox)sender;
    //   if (textbox.Text.Equals("2005/05/05")) {
    //    e.Cancel = true;
    //   } else {
    //    e.Cancel = false;
    //   }
    //   e.Cancel = true;
      }
     }
    }


    ---------------------------------

     

     

     

     

    2006年6月29日 0:45

すべての返信

  • まず、VisualStudioは2003ですか、2005ですか?

    このページは参考にしていますでしょうか?
    http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/cpref/html/frlrfsystemwindowsformsdatagridcolumnstyleclasstopic.asp
    このページのサンプルでもTABはおかしいのでしょうか?
    おかしくなければここからやりたい事を追加していったらどうでしょうか?

    2006年7月1日 18:08
  • えムナウ さん

    ご返信どうもありがとうございました。

    作成したプログラムはご提示のサンプルを元の作成しました。

    もとのサンプルではTABキーが正常に動きます。

    ただし、元のサンプルにはAlt+↓(↑)によるカレンダーの表示、クローズがないため、この部分を自分で追加しました。その結果、TABキーの動きがおかしくなってしまったようです。

    追加した部分はDateTimePickerクラスの以下の部分です。

    多分この部分のソースが問題を起こしたと思いますが、いろいろ試しても

    解決できなかったので、たいへん焦ってます。

    もしいい解決方法があれば、ぜひご教授お願いします。

    ■DateTimePickerクラスの自分追加部分

    ---------------

      protected override bool ProcessKeyMessage(ref Message m) {
       // Keep all the keys for the DateTimePicker.
       return ProcessKeyEventArgs(ref m); 
       
      }


      protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {

       const int WM_KEYDOWN = 0x100;
       const int WM_SYSTEMDOWN = 0x104; 
       const int WM_SYSKEYDOWN = 0x104; 
       const int VK_DOWN = 0x28; 
     
      if (msg.Msg == WM_KEYDOWN || msg.Msg == WM_SYSTEMDOWN) {


             if (keyData == (Keys.Alt | Keys.Down)) {

              SendMessage(this.Handle, WM_SYSKEYDOWN, VK_DOWN, 0);
          
            }
        

            return base.ProcessCmdKey(ref msg, keyData); 
        }

      }
      

      [System.Runtime.InteropServices.DllImport("User32.dll",EntryPoint="SendMessageA")]
      public static extern int SendMessage(IntPtr hWnd, int nMsg, int wParam, int lParam);

    -----------------------------------------------------

     

     

    2006年7月2日 4:23
  • 開発バージョンを書き忘れました。

    ちなみに、.NET STUDIO2003を使っています。

     

    2006年7月2日 6:49
  • ぱっと見ですが。
    protected override bool ProcessCmdKey(ref Message msg, Keys keyData) が、
    1)全てのケースでbase.ProcessCmdKey(ref msg, keyData)を呼んでいるか?
    2)SendMessage(this.Handle, WM_SYSKEYDOWN, VK_DOWN, 0); で「文字がコントロールによって処理された」ことにはならないか?

    この辺をチェックしてください。

    2006年7月2日 18:40