与 Windows 窗体网格作斗争

问题描述 投票:0回答:1

DataGridView 没有按照我想要的方式工作。

我需要它来创建两列:

using System.Data;

public partial class Form1 : Form
{
    public Form1() { InitializeComponent(); }

    DataTable todoList = new DataTable();
    bool isEditing = false;

    private void toDoListView_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        /// Creating Colums
        todoList.Columns.Add("Title");
        todoList.Columns.Add("Description");

        ///Pushing out data souce to the data grid
        toDoListView.DataSource = todoList;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        titleOfTaskTextBox.Text = "";
        descriptionTextBox.Text = "";
    }

    private void editButton_Click(object sender, EventArgs e)
    {
        isEditing = true;
        ///Need to fill text fields from data store
        titleOfTaskTextBox.Text = todoList.Rows[toDoListView.CurrentCell.RowIndex].ItemArray[0].ToString();
        descriptionTextBox.Text = todoList.Rows[toDoListView.CurrentCell.RowIndex].ItemArray[1].ToString();
    }

    private void removeButton_Click(object sender, EventArgs e)
    {
        try
        {
            todoList.Rows[toDoListView.CurrentCell.RowIndex].Delete();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex);
        }
    }

    private void saveButton_Click(object sender, EventArgs e)
    {
        if (isEditing)
        {
            todoList.Rows[toDoListView.CurrentCell.RowIndex]["Title"] = titleOfTaskTextBox.Text;
            todoList.Rows[toDoListView.CurrentCell.RowIndex]["Description"] = descriptionTextBox.Text;
        }
        else
        {
            // -> This code is not accepted
            todoList.Rows.Add(titleOfTaskTextBox.Text, descriptionTextBox.Text);
        }
        /// Clear used fields
        titleOfTaskTextBox.Text = "";
        descriptionTextBox.Text = "";
        isEditing = false;
    }
}

它说标记的代码行是错误的。

c# winforms datagridview
1个回答
0
投票

模型 - 视图

在现代编程中,有一种趋势是将数据(=模型)与数据的显示方式(=视图)分开。

这种分离的优点是模型可以在不同的视图中重用。例如,电子表格数据可用于在表格中显示,但也可用于创建图表。

除了可以重用模型之外,它还有一个优点是您可以在没有显示数据的程序的情况下对模型进行单元测试。如果实际数据尚不可用,也可以使用一些模拟数据来测试视图。或者您可能想更改数据的显示方式,而不更改模型。

为了使模型适应视图,需要一个适配器类,通常称为

ViewModel
。这三个类一起缩写为 MVVM。如果您不熟悉 MVVM,请考虑花一个小时左右阅读一些相关背景知识。

回到你的问题

你在形式上做得太多了。如果您必须为表单编写规范,您会说表单显示

Todo list
,它是
ToDo Items
的列表。如果操作员单击标记为
Remove
的按钮,则显示的 ToDoList 中当前选定的项目将被删除。如果操作员单击按钮
Save
,则...等等

您是否注意到在您的规范中您没有使用 DataTable 这个词?当然,当前您的待办事项列表位于数据表中。但是您想重写您的表单并将您的 ToDoList 存储在数据库中吗?或者只是一个简单的文件?或者也许数据是从互联网获取的?

通过将模型与视图分离,您的代码将更容易阅读,更容易进行单元测试,更容易适应新用途或重用。

所以让我们将模型和视图中的代码分开。

您的型号

显然你有

ToDoItems
集合的概念。您称这个集合为
ToDoList
。这个怎么样:

class ToDoItem
{
    public int Id {get; set;}         // not needed right now
    public string Title {get; set;}
    public string Description {get; set;}

    // not sure if you need this, but keep in mind for future uses:
    public DateTime CreationDate {get; set;}
    public DateTime ExpectedFinishedDate {get; set;}
    public DateTime? ActualFinishedDate {get; set;}     // null as long as not finished

    public bool IsFinished => this.ActualFinishedData.HasValue;
}

我已经向 ToDoItem 添加了一个 Id,如果您想标识 ToDoItem,这可能会很方便,例如,如果在将来使用时您想添加必须完成 TodoItem 的人员列表。如果你认为这真的是废话,请忽略它。

还有你的待办事项列表。

class ToDoList : ICollection<ToDoItem>
{
    private ICollection<ToDoItem> ToDoItems {get} new List<ToDoItem>();

    // implementation of the interface is straightforward:
    public int Count => this.ToDoItems.Count;
    public bool IsReadOnly => this.ToDoItems.IsReadOnly;

    public void Add(ToDoItem todoItem)
    {
         this.ToDoItems.Add(todoItem);
    }

    // etc
}

好处是,您已经隐藏了 ToDoList 实际上是内存中的列表。如果在未来的版本中您想将其保存在数据库中,或从互联网上获取数据,您班级的用户将不必注意到这一点,因此不必进行更改。

考虑有一个以

ICollection<ToDoItem>
作为参数的构造函数。您还可以决定创建一个 ToDoFile 类:

class ToDoFile
{
    public FileInfo File {get; set;}
    public ToDoList Read() {...}
    public void Write(IEnumerable<ToDoItem> todoItems) {...}
}

请注意,我可以将

Save(string fileName)
添加到 ToDoList。但是您确定每个 ToDoList 都可以保存为文件吗?难道以后的一些ToDoList会保存在数据库中吗?您想为此限制您的待办事项列表吗?

景色

您没有给出表格的完整规范。在我看来,您想在 DataGridView 中显示您的 ToDoList。您需要按钮来添加/删除 ToDoItems,并且如果操作员单击 DataGridView 中的单元格之一,他可以编辑该单元格的内容。当操作员完成所有更改后,他可以单击

Apply Now
按钮来激活他所做的更改。取消按钮可撤消更改。

这个怎么样:

class FormToDo : Form
{
    // your Form has a ToDoList; initialize this before the Form is shown
    public ToDoList ToDoList {get; set;}

使用 Visual Studio 设计器添加您需要的按钮以及将显示您的 ToDoItems 的 DataGridView。添加 DataGridViewColumns 以显示您想要显示的属性。无需为您不想显示的属性添加列。

在构造函数中定义哪一列将显示哪个属性:

    public FormToDo()
    {
        this.InitializeComponent(); // added by visual studio designer

        // define which column shows which property:
        this.columnTitle.DataPropertyName = nameof(ToDoItem.Title);
        this.columnDescription.DataPropertyName = nameof(ToDoItem.Description);
        this.columnExpectedFinishedDate.DataPropertyName = nameof(ToDoItem.ExpectedFinishedDate);
        ... // etc
    }

当然,您的表单需要一个属性来获取最初必须显示的 ToDoItems:

public IEnumerable<ToDoItem> InitialToDoList => this.ToDoList;

如果将来您的需求发生变化,例如从文件或互联网获取初始 ToDoList,那么这是唯一发生变化的语句。

要显示此 ToDoList,您所要做的就是在显示表单之前将其分配给 DataGrigView.DataSource:

public OnFormShown(object sender, ...)
{
    this.dgvTodoList.DataSource = this.InitialToDoList.ToList();
}

很快,你的表格就显示出来了!然而,访问编辑后的数据并不容易。为此,您需要将对 ToDoItems 的引用放入 BindingList

© www.soinside.com 2019 - 2024. All rights reserved.