如何在一个DataGridView中的一列添加DateTimePicker控件 C#

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在一个DataGridView中的一列添加DateTimePicker控件 C#相关的知识,希望对你有一定的参考价值。

如何在一个DataGridView中的一列上添加DateTimePicker控件, 更多勋章这个要自定义了,可能用到如下的几个类: 从DataGridViewColumn继承一个新的列类型 从DataGridViewCell继承一个新的单元类型. 从DateTimePicker, IDataGridViewEditingControl继承新的DateTimePicker控件,

参考技术A DataGridView 控件提供了多种列类型,使得用户可以通过多种方式输入和编辑值。但是,如果这些列类型无法满足数据输入要求,您也可以使用承载所选控件的单元格创建自己的列类型。要做到这一点,必须定义派生自 DataGridViewColumn 和 DataGridViewCell 的类。您还必须定义派生自 Control 并实现 IDataGridViewEditingControl 接口的类。
下面的代码示例演示如何创建日历列。此列的单元格在普通的文本框单元格中显示日期,但当用户编辑单元格时,就会出现 DateTimePicker 控件。为了避免必须再次实现文本框显示功能,CalendarCell 类从 DataGridViewTextBoxCell 类派生,而不是直接从 DataGridViewCell 类继承。

注意

当从 DataGridViewCell 或 DataGridViewColumn 派生并向派生类添加新属性时,请确保重写 Clone 方法以便在克隆操作期间复制新属性。还应调用基类的 Clone 方法,以便将基类的属性复制到新的单元格或列中。

using System;
using System.Windows.Forms;

public class CalendarColumn : DataGridViewColumn

public CalendarColumn() : base(new CalendarCell())



public override DataGridViewCell CellTemplate

get

return base.CellTemplate;

set

// Ensure that the cell used for the template is a CalendarCell.
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()

// Use the short date format.
this.Style.Format = "d";


public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)

// Set the value of the editing control to the current cell value.
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);
CalendarEditingControl ctl =
DataGridView.EditingControl as CalendarEditingControl;
ctl.Value = (DateTime)this.Value;


public override Type EditType

get

// Return the type of the editing contol that CalendarCell uses.
return typeof(CalendarEditingControl);



public override Type ValueType

get

// Return the type of the value that CalendarCell contains.
return typeof(DateTime);



public override object DefaultNewRowValue

get

// Use the current date and time as the default value.
return DateTime.Now;




class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl

DataGridView dataGridView;
private bool valueChanged = false;
int rowIndex;

public CalendarEditingControl()

this.Format = DateTimePickerFormat.Short;


// Implements the IDataGridViewEditingControl.EditingControlFormattedValue
// property.
public object EditingControlFormattedValue

get

return this.Value.ToShortDateString();

set

String newValue = value as String;
if (newValue != null)

this.Value = DateTime.Parse(newValue);




// Implements the
// IDataGridViewEditingControl.GetEditingControlFormattedValue method.
public object GetEditingControlFormattedValue(
DataGridViewDataErrorContexts context)

return EditingControlFormattedValue;


// Implements the
// IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)

this.Font = dataGridViewCellStyle.Font;
this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;


// Implements the IDataGridViewEditingControl.EditingControlRowIndex
// property.
public int EditingControlRowIndex

get

return rowIndex;

set

rowIndex = value;



// Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
// method.
public bool EditingControlWantsInputKey(
Keys key, bool dataGridViewWantsInputKey)

// Let the DateTimePicker handle the keys listed.
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;



// Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
// method.
public void PrepareEditingControlForEdit(bool selectAll)

// No preparation needs to be done.


// Implements the IDataGridViewEditingControl
// .RepositionEditingControlOnValueChange property.
public bool RepositionEditingControlOnValueChange

get

return false;



// Implements the IDataGridViewEditingControl
// .EditingControlDataGridView property.
public DataGridView EditingControlDataGridView

get

return dataGridView;

set

dataGridView = value;



// Implements the IDataGridViewEditingControl
// .EditingControlValueChanged property.
public bool EditingControlValueChanged

get

return valueChanged;

set

valueChanged = value;



// Implements the IDataGridViewEditingControl
// .EditingPanelCursor property.
public Cursor EditingPanelCursor

get

return base.Cursor;



protected override void OnValueChanged(EventArgs eventargs)

// Notify the DataGridView that the contents of the cell
// have changed.
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnValueChanged(eventargs);



public class Form1 : Form

private DataGridView dataGridView1 = new DataGridView();

[STAThreadAttribute()]
public static void Main()

Application.Run(new Form1());


public Form1()

this.dataGridView1.Dock = DockStyle.Fill;
this.Controls.Add(this.dataGridView1);
this.Load += new EventHandler(Form1_Load);
this.Text = "DataGridView calendar column demo";


private void Form1_Load(object sender, EventArgs e)

CalendarColumn col = new CalendarColumn();
this.dataGridView1.Columns.Add(col);
this.dataGridView1.RowCount = 5;
foreach (DataGridViewRow row in this.dataGridView1.Rows)

row.Cells[0].Value = DateTime.Now;


参考技术B DateTimePicker貌似没办法调整高度的,所以也就不能覆盖整个单元格

如何在R中的一列中添加具有不同值的新行

基本上我有一个所有名称的矢量names,以及一个带有BIN(0/1)字段和NAME字段的数据帧df。对于BIN==0的每一行,我想创建一个重复的行,但是用1代替,并将其添加到df的底部,并使用不同的名称。这是我必须选择一个新名称,给定当前名称:

sample(names[names!=name], 1)

但我不知道如何对此进行矢量化,并使用来自BIN的相同数据将其添加到df

编辑:示例数据:

df = data.frame(BIN=c(1,0,1), NAME=c("alice","bob","cate"))
names = c("alice","bob","cate","dan")

我接近这样的事情:

rbind(df, df %>% filter(BIN == 0) %>%
    mutate(NAME = sample(names[names!=NAME],1)))

但是我得到一个错误:在binattr(e1,e2)中:长度(e1)不是长度的倍数(e2)。

答案

这是一个简单的方法。我认为这很简单,如果您有疑问,请告诉我:

rename = subset(df, BIN == 0)
rename$NEW_NAME = sample(names, size = nrow(rename), replace = TRUE)
while(any(rename$NAME == rename$NEW_NAME)) {
  matches = rename$NAME == rename$NEW_NAME
  rename$NEW_NAME[matches] = sample(names, size = sum(matches), replace = TRUE)
}
rename$BIN = 1
rename$NAME = rename$NEW_NAME
rename$NEW_NAME = NULL

result = rbind(df, rename)
result
#    BIN  NAME
# 1    1 alice
# 2    0   bob
# 3    1  cate
# 21   1 alice

这是另一种方法,不太清晰但效率更高。这是“正确”的方式,但它需要更多的思考和解释。

df$NAME = factor(df$NAME, levels = names)
rename = subset(df, BIN == 0)
n = length(names)
# we will increment each level number with a random integer from
# 1 to n - 1 (with a mod to make it cyclical)
offset = sample(1:(n - 1), size = nrow(rename), replace = TRUE)
adjusted = (as.integer(rename$NAME) + offset) %% n
# reconcile 1-indexed factor levels with 0-indexed mod operator
adjusted[adjusted == 0] = n
rename$NAME = names[adjusted]
rename$BIN = 1
result = rbind(df, rename)

(或者,为dplyr改写)

df = mutate(df, NAME = factor(NAME, levels = names))
n = length(names)
df %>% filter(BIN == 0) %>%
  mutate(
    offset = sample(1:(n - 1), size = n(), replace = TRUE),
    adjusted = (as.integer(NAME) + offset) %% n,
    adjusted = if_else(adjusted == 0, n, adjusted),
    NAME = names[adjusted],
    BIN = 1
  ) %>%
  select(-offset, -adjusted) %>%
  rbind(df, .)

由于您的问题是矢量化部分,我建议在具有多个BIN 0行的示例案例上测试答案,我使用了这个:

df = data.frame(BIN=c(1,0,1,0,0,0,0,0,0), NAME=rep(c("alice","bob","cate"), 3))

而且,因为我很好奇,这里有10个行的基准,有26个名字。结果首先,代码如下:

# Unit: milliseconds
#             expr        min         lq      mean     median         uq        max neval
#       while_loop  34.070438  34.327020  37.53357  35.548047  39.922918  46.206454    10
#        increment   1.397617   1.458592   1.88796   1.526512   2.123894   3.196104    10
#  increment_dplyr  24.002169  24.681960  25.50568  25.374429  25.750548  28.054954    10
#         map_char 346.531498 347.732905 361.82468 359.736403 374.648635 383.575265    10

“聪明”的方式是迄今为止最快的方式。我的猜测是dplyr减速是因为我们不能直接替换adjusted的相关位,而是必须添加if_else的开销。那我们实际上是在adjustedoffset的数据框中添加列而不是处理向量。这足以使它几乎与while循环方法一样慢,这仍然比map_chr快10倍,nn = 10000 df = data.frame( BIN = sample(0:1, size = nn, replace = TRUE, prob = c(0.7, 0.3)), NAME = factor(sample(letters, size = nn, replace = TRUE), levels = letters) ) get.new.name <- function(c){ return(sample(names[names!=c],1)) } microbenchmark::microbenchmark( while_loop = { rename = subset(df, BIN == 0) rename$NEW_NAME = sample(names, size = nrow(rename), replace = TRUE) while (any(rename$NAME == rename$NEW_NAME)) { matches = rename$NAME == rename$NEW_NAME rename$NEW_NAME[matches] = sample(names, size = sum(matches), replace = TRUE) } rename$BIN = 1 rename$NAME = rename$NEW_NAME rename$NEW_NAME = NULL result = rbind(df, rename) }, increment = { rename = subset(df, BIN == 0) n = length(names) # we will increment each level number with a random integer from # 1 to n - 1 (with a mod to make it cyclical) offset = sample(1:(n - 1), size = nrow(rename), replace = TRUE) adjusted = (as.integer(rename$NAME) + offset) %% n # reconcile 1-indexed factor levels with 0-indexed mod operator adjusted[adjusted == 0] = n rename$NAME = names[adjusted] rename$BIN = 1 }, increment_dplyr = { n = length(names) df %>% filter(BIN == 0) %>% mutate( offset = sample(1:(n - 1), size = n(), replace = TRUE), adjusted = (as.integer(NAME) + offset) %% n, adjusted = if_else(adjusted == 0, n, adjusted), NAME = names[adjusted], BIN = 1 ) %>% select(-offset,-adjusted) }, map_char = { new.df <- df %>% filter(BIN == 0) %>% mutate(NAME = map_chr(NAME, get.new.name)) %>% mutate(BIN = 1) }, times = 10 ) 必须一次排成一排。

library(tidyverse)

df <- data.frame(BIN=c(1,0,1), NAME=c("alice","bob","cate"), stringsAsFactors = FALSE)
names <- c("alice","bob","cate","dan")

df %>% 
  mutate(NAME_new = ifelse(BIN == 0, sample(names, n(), replace = TRUE), NA)) %>% 
  gather(name_type, NAME, NAME:NAME_new, na.rm = TRUE) %>% 
  mutate(BIN = ifelse(name_type == "NAME_new", 1, BIN)) %>% 
  select(-name_type)
另一答案

有点奇怪,但我认为这应该是你想要的:

  BIN  NAME
1   1 alice
2   0   bob
3   1  cate
4   1 alice

输出:

rowwise()
另一答案

好吧,我不打算回答我自己的问题,但我确实找到了一个更简单的解决方案。我认为它比使用library(tidyverse) get.new.name <- function(c){ return(sample(names[names!=c],1)) } new.df <- rbind(df, df %>% filter(BIN == 0) %>% mutate(NAME = map_chr(NAME, get.new.name)) %>% mutate(BIN = 1) 更好,但我不知道它是否必然是最有效的方式。

map_char

map最终变得非常重要,而不仅仅是qazxswpoi,因为后者将返回一个奇怪的列表列表。

以上是关于如何在一个DataGridView中的一列添加DateTimePicker控件 C#的主要内容,如果未能解决你的问题,请参考以下文章

datagridview 怎么改变第一列的颜色

C#如何获取datagridview最后一行第一列数据的值

c# datagridview 中添加comboBox的问题

如何设置datagridview列为combobox

C# winfrom datagridview 如何为一列添加向下滚动条 请看清楚再回答!

如何以编程方式定义 datagridview 中的列?