我发现这段代码可以将一列向下移动一行:
private void ShiftColumnByOneRow(DataGridView dataGridView, int columnIndex)
{
if (dataGridView.Rows.Count > 1 && columnIndex >= 0 && columnIndex < dataGridView.Columns.Count)
{
// Store the value of the last cell in the column
var lastValue = dataGridView.Rows[dataGridView.Rows.Count - 1].Cells[columnIndex].Value;
// Shift each cell value down by one row
for (int i = dataGridView.Rows.Count - 1; i > 0; i--)
{
dataGridView.Rows[i].Cells[columnIndex].Value = dataGridView.Rows[i - 1].Cells[columnIndex].Value;
}
// Set the first cell value to the last cell value
dataGridView.Rows[0].Cells[columnIndex].Value = lastValue;
}
}
//Call this method when needed:
// Example usage
ShiftColumnByOneRow(yourDataGridView, columnIndex);
如何向上移动?
我已经尝试过:
dataGridView.Rows[i-1].Cells[columnIndex].Value = dataGridView.Rows[i].Cells[columnIndex].Value;
这会将列中的所有行变成空白。
在现代编程中,倾向于将数据(=模型)与数据向操作员显示的方式(=视图)分开。要将模型链接到视图,需要一个适配器类,通常称为 ViewModel。这三个类一起缩写为 MVVM。如果您对此不熟悉,请考虑阅读一些有关 MVVM 的背景信息。
将数据与向操作员显示的方式分开,无需更改模型即可更改视图。例如,您可以在图表中显示数据,但也可以在表格中显示相同的数据。
分离的模型和视图,使您可以更改模型,而无需更改视图。例如,您可以从文件、数据库甚至互联网中获取数据。这些是对模型的更改。如果由于某种原因无法提供相同的接口,适配器类 ViewModel 可以将模型的布局转换为视图。无需更改视图。
将模型与视图分离还使您能够重用模型并在不需要视图的情况下对模型进行单元测试。同样,您可以使用模拟数据测试向操作员显示数据的方式。您不需要包含实际数据的数据库,您的模型可以是要显示的项目列表。
当人们使用 DataGridView 时,他们倾向于直接摆弄列和行。将数据与向操作员表示的方式分开要容易得多。这样,您只需重新排列显示数据的顺序,重新排列向操作员显示的方式。
您没有告诉我们您正在显示什么类型的数据。为了使沟通更容易,我们假设您正在显示一系列产品:
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public decimal Price {get; set;}
public int Stock {get; set;}
}
在某个地方你有一个方法来获取必须显示的所有产品:
IEnumerable<Product> FetchProductsToDisplay() {...}
实施超出了这个问题的范围。
使用 Visual Studio Designer,您已将 DataGridView 添加到表单中,并向 DataGridView 添加了一些列。也许您显示产品的所有属性,但也许您不想显示所有属性,例如,产品的描述可能太长而无法在表中显示。简而言之,不要创建您不想显示的 DataGridViewColumns。如果您的列有时显示,有时隐藏,请使用属性 使用属性
DataGridViewColumn.Visible
。
在表单的构造函数中,您可以使用
DataGridViewColumn.DataPropertyName
定义哪一列显示哪个属性
public MyForm() : Form
{
InitializeComponent();
// define which column shows which Product property
this.ColumnProductId.DataPropertyName = nameof(Product.Id);
this.ColumnProductName.DataPropertyName = nameof(Product.Name);
this.ColumnProductPrice.DataPropertyName = nameof(Product.Price);
... // etc.
}
显示数据的方法取决于您是否只想显示数据,或者您是否希望操作员编辑数据,或者以其他方式操作数据,例如按列排序。
如果仅显示就足够了,您可以使用
List<Product>
。如果需要编辑,最好使用BindingList<Product>
。
BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
现在只需一行即可填充 DataGridView:
this.DisplayedProducts = new BindingList<Product>(this.FetchProductsToDisplay().ToList();
因为您使用了 BindingList,所以操作员可以编辑产品。他可以添加和删除产品。完成编辑后,操作员可以按
Apply Now
按钮,或 Ok
:
void OnButtonApplyNow_Presses(object sender, ...)
{
this.ProcessEditedProducts();
}
void ProcessEditedProducts()
{
ICollection<Product> editedProducts = this.DisplayedProducts();
// find out which Products are Added / Removed / Changed
// and process the changes
this.ProcessProducts(editedProducts);
}
或者,您想要将所有行向上移动,并将第一行放在最后,或者将所有行向下移动,并将最后一行放在前面。
void ShiftDisplayedProductsUp()
{
IEnumerable<Product> displayedProducts = this.DisplayedProducts;
// use LinQ to move the first element to the end
IEnumerable<Product> shiftedProducts = displayedProducts
.Skip(1)
.Concat(displayedProducts.Take(1));
// display the shifted products:
this.DisplayedProducts = new BindingList<Product>(shiftedProducts.ToList());
}
很快,您的移动列表就会显示出来。当然,您也可以使用 BindingList 方法从 BindingList 中删除第一个 Product 并将其添加到末尾。我不确定这是否有效。
为了完整起见,访问当前产品或所选产品可能会很方便:
Product CurrentProduct => (Product)this.dataGridView1.CurrentRow.DataBoundItem;
IEnumerable<Product> SelectedProducts => this.dataGridView1.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
通过将数据与向操作员显示的方式分开,您的代码看起来更加清晰。数据可以重复用于其他目的。它可以在没有 DataGridView 的情况下进行单元测试,并且您可以使用简单的产品模拟序列来测试 DataGridView。
为了实现这一目标,我只需要定义哪一列显示哪个 Product 属性,并且只有一个简单的属性:DisplayedProducts。
通过使用此属性,您可以直接访问操作员所做的更改。您对集合所做的更改会立即显示给操作员。
最后:您是否注意到数据和视图的分离使得大多数程序变得非常简单?通常您只获取数据,操作数据并再次显示它。无需访问列和行。