如何在一个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
的开销。那我们实际上是在adjusted
和offset
的数据框中添加列而不是处理向量。这足以使它几乎与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#的主要内容,如果未能解决你的问题,请参考以下文章
c# datagridview 中添加comboBox的问题