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

尝试使 Silverlight DataGrid 类似于 Excel

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.29/5 (7投票s)

2010 年 1 月 5 日

CPOL

6分钟阅读

viewsIcon

76146

downloadIcon

3669

业务用户非常喜欢 Excel。使用类似的导航和功能有助于他们接受您的应用程序。

引言

对于许多简单的编辑操作,只提供一个网格并允许用户就地编辑是一个不错的解决方案。习惯了 Excel(事实上,他们要求类似的功能)的用户,如果您的应用程序的行为与 Excel 有些相似,他们将能够立即了解您的应用程序是如何工作的。

背景

就本文而言,我将“类似 Excel 的行为”定义为网格单元格之间简单、易于发现的导航,而不是公式、添加图表的能力等。我们只是希望用户能够简单地执行 CRUD 操作,而无需阅读说明文档或联系任何人。

Silverlight DataGrid 的默认导航如下:双击单元格或在单元格中按 F2 会进入编辑模式——用户可能永远不会发现 F2 的作用,并且会因为输入不起作用而感到恼火。

默认 Excel 导航如下:双击或在单元格中键入内容会进入编辑模式。

换句话说,Excel 用户期望在单元格中键入内容,并且能够接收文本,除非他们在键盘上洒了东西/弄坏了键盘,否则他们不会去拿鼠标。

作为额外的奖励,也是我们在与用户辩论时的最后一种备用方案,我们还将提供合理的复制粘贴功能,以便用户能够粘贴任意数量的行,这些行包含所有列(我们将忽略只读列的粘贴到网格中)或仅包含可写列。这样,用户就可以像在 Excel 中一样轻松地将数据从我们的网格中导出,并且可以轻松地将数据导入,而无需经过任何他们不习惯的向导或额外的步骤。

如果所有这些都失败了,用户可以从我们的网格中复制数据,在他们喜欢的编辑器中随意编辑,然后从那里复制并粘贴回 Silverlight DataGrid

复制和粘贴

让我们先处理好备用方案:复制和粘贴。Excel 会理解以换行符分隔的行和以制表符分隔的列的复制粘贴数据。这似乎并不难实现,但我们在 DataGrid 中首先需要处理两个问题

  • DataGrid 没有网格数据和绑定数据,只有绑定数据.
  • 因此,要求获取第 1 行、第 3 列的内容有些困难,除非您碰巧知道您已将属性 X 绑定到网格中的第 3 列。我们在这里寻找的是一个更通用的解决方案,该解决方案可以与大多数网格一起使用,而无需了解底层的绑定细节。

    幸运的是,假设单元格中有一个 TextBlock 并且您已经拥有该行绑定项的引用,就可以通过以下方式实现

    (column.GetCellContent(item) as TextBlock).Text

    要获取绑定对象的引用,您可以使用 dataGrid.SelectedItems

    但是,根据您的显示数据的复杂程度,您的网格中可能有一个更复杂的结构。以下应该可以处理大多数情况,并且足够易于理解,如果您在网格中有非常奇怪的东西,可以对其进行修改

    private static TextBlock GetCellTextBlock(object item, DataGridColumn column)
    {
        var cellData = (column.GetCellContent(item) as TextBlock);
        if (cellData == null)
        {
            //for our custom columns
            var gridData = (column.GetCellContent(item) as Panel);
            if (gridData != null)
            {
                cellData = (gridData.Children.Where(x => x.GetType() == 
                             typeof(TextBlock)).FirstOrDefault() as TextBlock);
            }
        }
        return cellData;
    }
  • 我们必须处理的第二个问题是,DataGrid 不会渲染内容,直到它实际上可见
  • 从性能的角度来看,这是有道理的,因为它不会渲染您甚至看不到的网格部分。但这确实会给您带来一些困难,如果您想按显示顺序访问网格中的数据。一种解决方法是使用反射。另一种解决方法是手动滚动网格可见,以便在复制数据时!其中一个比另一个简单得多,并且在最终网格滚动到数据末尾之前,用户几乎看不到。如果这困扰您,您随时可以滚动回起始选择,但目前我将就这样保留。但是,将当前单元格滚动到视图中的命令非常简单

    dataGrid.ScrollIntoView(item, column);

一旦我们解决了这两个问题,处理复制和粘贴就相当简单了,可以使用制表符和换行符分隔符连接或拆分数据。有关更多详细信息,请参阅附件源代码。

如果您查看源代码,您会注意到,在复制数据时,会添加标题(这样用户就知道他们拥有哪些列,当有很多列时很重要),并且用户可以粘贴任意数量的数据,即使存在只读列。这里有一个视频,可以在不下载源代码的情况下观看此功能:http://www.blip.tv/file/3055352

那么,如何让编辑行为像 Excel 一样工作呢?

简短、大致有效的解决方案

private void dataGrid_CurrentCellChanged(object sender, EventArgs e)
{
   dataGrid.BeginEdit();
}

使用此方法,一旦单元格获得焦点,它就会进入编辑模式。这意味着用户可以直接在单元格中键入,并且上下箭头键也有效,但左右箭头键会遍历单元格,直到到达末尾,然后进入下一个单元格。因此,如果用户对此感到满意,或者习惯于使用 Tab 键而不是箭头键,那么您就完成了。无论如何,如果您期望用户进行大量内联编辑,并且不担心意外更改数据,那么此解决方案比默认方案要好。

复杂、多行、真正有效的解决方案

如果我们捕获网格上的 KeyUp 事件,并且能够检测到我们刚刚进入编辑模式,那么我们可以手动将文本框的文本设置为按下的第一个键,并将光标移到该文本的末尾。很简单,对吧?

事实并非如此。据我所知,没有办法知道网格是否处于编辑模式,所以我使用对象本身的 Tag 属性来存储它,并且仅在按下某个键、该键不是导航键且列不是只读的情况下执行此操作

//don't start editing for nav keys so a left arrow, etc. doesn't put text in a box
if (dataGrid.Tag == null && !IsNavigationKey(e) && !dataGrid.CurrentColumn.IsReadOnly)
{
    bool isShifty = ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift);
    string letter = ((char)e.PlatformKeyCode).ToString();
    letter = (isShifty ? letter.ToUpper() : letter.ToLower());
    dataGrid.Tag = letter;

    //beginedit will fire the focus event
    //if we try to access the textbox here its text will not be set
    dataGrid.BeginEdit();
}

好的,这有点取巧,但现在我们可以设置文本了吗?不行,在 KeyDownKeyUp 事件中,文本框尚未初始化,因此我们必须捕获 GotFocus 事件并使用相同的 Tag 属性知道要放入其中什么。

private void dataGrid_GotFocus(object sender, RoutedEventArgs e)
{
    if (dataGrid.Tag != null)
    {
        var box = CopyPasteSupport.GetCellItem<TextBox>(
                       dataGrid.SelectedItem, dataGrid.CurrentColumn);
        if (box != null)
        {
            box.Text = dataGrid.Tag.ToString();
            box.SelectionStart = 1; //move editing cursor to end of text
        }
    }
}

然后在 CellEditEnded 事件上重置我们的取巧状态机制。

void dataGrid_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e)
{
    dataGrid.Tag = null;
}

现在用户可以开始在单元格中键入,而不会在进入编辑模式时丢失第一个按键。用户很可能永远不会知道您做了这些,只是照常使用,而无需提问或致电。

要启用所有这些功能,您只需要引用 ExcelBehavior 项目,并在您的网格 Load 事件中添加这一行

ExcelBehavior.EnableForGrid(dataGrid);

历史

  • 2010 年 1 月 9 日 - 初始版本。
© . All rights reserved.