65.9K
CodeProject 正在变化。 阅读更多。
Home

基本文件 IO 和 DataGridView

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.20/5 (6投票s)

2006年7月12日

CPOL

4分钟阅读

viewsIcon

62944

downloadIcon

808

使用 .NET StreamReader 和 DataGridView 创建一个通用的行读取应用程序。

Sample Image - LineCounter.png

引言

如果你曾想知道自己写代码的速度有多快,你可能数过代码行数。随着程序越来越复杂,并且工作在团队中共享,很难知道有多少行代码是“属于”你的。

代码行数可能不是衡量性能的最佳方法(我能想象一个教室里满是猴子不停地按回车键……)。但有时,能够说“看看我们做了什么!”会让人感到满足。

本文探讨了基本的文件 IO、如何手动将结果存储在 DataGridView 中,以及基本的事件处理。

连接 GUI

GUI 只有几个组件。一个 TextBox 提供了一个地方,我们可以在其中输入我们感兴趣的要计数的文件扩展名。一个 DataGridView 提供了一个显示结果的地方。一个 CheckBox 为我们提供了一些选项,而一个 StatusStrip 帮助我们向用户总结结果。

使用 Anchor 属性来调整每个 Control 如何“依附”在 Form 的边界上。这使我们能够调整窗口大小,以便在文件名较长或结果数量较多时获得更好的视图。锚定是环境中内置的便捷布局工具之一。

Sample image

现在,我们将连接 DataGridView 控件中的列。窗体编辑器使这变得轻而易举。突出显示 DataGridView 并选择 Columns 属性。

Sample image

现在我们有了一个可以用来生成列的对话框。单击“添加”按钮会打开一个附加对话框,可用于创建列。选择一个名称和标题文本,以及 DataGridViewTextBoxColumn 的列类型。其他有趣的列属性包括 FillWeight,它会影响列在布局期间获得的优先级。

Sample image

为 GUI 添加功能的下一步是添加事件处理程序。例如,当我们单击“开始”按钮时,我们希望应用程序查找符合我们搜索条件的文件、打开它们并计数行数。

我们可以使用窗体编辑器轻松添加此处理程序。只需双击“开始”按钮,即可为我们添加事件处理程序,如下所示:

private void goButton_Click(object sender, EventArgs e)
{
  // TODO: Count files here...
}

此外,该函数已在 *designer.cs* 文件中与事件委托注册。这会告诉运行时,当 goButton.Click 事件触发时,我们希望运行 goButton_Click 方法。

this.goButton.Click += new System.EventHandler(this.goButton_Click);

因为我们的程序可能会找到我们想要忽略的文件,所以我们将 DataGridView 连接起来,以便仅收集所选文件的信息。然后,我们可以选择我们想要计入最终行数的结果。

使用窗体编辑器,我们将 MultiSelect 属性设置为 True,将 SelectionMode 属性设置为 FullRowSelect。剩下要做的就是添加一个事件处理程序来响应选择的变化。在窗体编辑器中找到 SelectionChanged 属性,然后双击以生成以下方法:

private void dgvResult_SelectionChanged(object sender, EventArgs e)
{
  // TODO: Count the selection here.
}

该方法已在 *designer.cs* 文件中连接如下:

this.dgvResult.SelectionChanged += 
         new System.EventHandler(this.dgvResult_SelectionChanged);

通过调整窗体编辑器中的属性可以实现许多更改,但让我们继续进行项目的主体部分,即编写我们自己的代码。

使用 StreamReader 计算文件中的行数

在 C# 中从系统读取文件非常容易。涉及的关键对象是 FileStreamReader。静态方法 File.OpenText 会打开由本地或完全限定的路径名指定的文件,并生成一个 StreamReader 对象来处理文件内容。StreamReader 内置了 ReadLine 方法;非常适合我们的任务。

private int CountLinesInFile(string filename)
{
    StreamReader sr = File.OpenText(filename);
    int count = 0;
    while (sr.ReadLine() != null)
      count++;
    sr.Close();
    return count;
}

在目录中查找文件

我们的下一个任务是确定应该打开哪些文件。我们将利用 System.Collections.Generic.List<> 类来包含我们的文件名。下面的代码片段演示了我们如何收集所有与请求的扩展名匹配的文件名:

private void CountFiles()
{
    // Repository for discovered filenames 
    List<string> filenames = new List<string>();

    // Running linecount total
    int total = 0;

    // The Split method is a quick and dirty way to parse a string.
    string[] extensions = txtExts.Text.Split(" ".ToCharArray());

    foreach (string ext in extensions)
    {
      // The third parameter of this overload allows us to 
      // automatically inspect subdirectories. Default is 
      // SearchOption.TopDirectoryOnly, and the argument 
      // could have been omitted, but is included here for
      // information's sake.
      filenames.AddRange(
        chkSubDir.Checked ?
        Directory.GetFiles(
            Directory.GetCurrentDirectory(),
            "*." + ext,
            SearchOption.AllDirectories) :
        Directory.GetFiles(
            Directory.GetCurrentDirectory(),
            "*." + ext,
            SearchOption.TopDirectoryOnly));
    }

    // ...

我们的下一个任务是打开每个文件、计数行数,并将结果添加到 DataGridView。我们将使用 DataGridView.Rows.Add(object[] params) 方法来添加每一行。使用此方法,每个 object 都应按顺序对应于 DataGridView 的列。

    // ...

    // Wipe out any previous results
    dgvResult.Rows.Clear();

    // No that we have all the filenames, 
    // open the files to count their lines.
    foreach (string filename in filenames)
    {
      int count = CountLinesInFile(filename);
      total += count;

      // This line adds a new row to the DataGridView.
      // We choose the string.Substring method to display
      // only the relative path of the file
      dgvResult.Rows.Add(
        filename.Substring(Directory.GetCurrentDirectory().Length+1),
        count.ToString());
    }

    // Add the total to the bottom
    dgvResult.Rows.Add(
       new object[] { "Total:", total.ToString() });
}

goButton_Clicked 事件处理程序调用此方法,我们就拥有了一个行计数器!

将 MultiSelect 与 DataGridView 结合使用

为了总结,我们将添加功能,以便在需要时可以忽略某些结果。下面的代码片段计算所选行中的结果,并将结果写入下方的 StatusStrip

private void CountSelection()
{
    int selected = 0;
    int total = 0;
    foreach (DataGridViewRow row in dgvResult.Rows)
    {
        try
        {
            // Ignore rows that are either not selected 
            // or are the total row. Because some "filler"
            // rows exist, use the catch block to effectively
            // ignore the invalid data in those rows.
            if (row.Selected &&
                row.Cells[0].Value.ToString() != "Total:")
            {
              selected++;
              total += int.Parse(row.Cells[1].Value.ToString());
            }
        }
        catch { }
    }

    // Update the status label with the results
    lblFileCt.Text = selected.ToString() + " files selected";
    lblLineCt.Text = total == 0 ? "No lines" : total.ToString() + " lines";
}

结论

本文仅触及了文件 IO 和 DataGridView 等数据控件的表面。希望您觉得本文有用。本文已在作者的网站上更新和维护:http://www.notesoncode.com/articles/2007/06/11/BasicFileIOAndTheDataGridView.aspx。欢迎您提供反馈。感谢您的阅读。

© . All rights reserved.