需要一个非常定制的大型Winforms网格

我即将开发一个Windows PC应用程序(它可以是WinForms或WPF),我主要担心的是我必须解决的UI问题。

基本上,我需要有一个大约50×50的网格,我需要从用户那里获得输入。 这是2500场。 实际上大多数将留空,大约10%将由用户填写。 每个字段可以是空白的,也可以是1到4的数字。我想要简单的输入 – 也许是一个下拉框(因为用键盘键入所有2500个字段没有意义,我希望用户填写使用鼠标的值)。

我在考虑下拉框或者甚至是标签,当你点击它们时会改变价值,但问题是(从我做过的测试中)添加2500种任何类型的控件会使界面非常慢。 我尝试在winForms应用程序中使用tablelayoutpanel,使用suspend / resumeupdate函数,还有doublebuffering,这有点帮助,但它仍然非常慢。 我不愿意去DataGridView路由,因为我需要非常自定义标头,我需要UI来自动更新一些百分比,因为用户更改了字段中的值。 但如果这是我唯一的选择,我不会反对。

我听说WPF可能会更好,因为你可以有很多控件,而且每个控件都没有自己的窗口处理,而且还有虚拟化(不确定实现有多难)。

我愿意接受建议。 我知道有人会建议打破网格,我最终可能会这样做。 无论哪种方式,我想知道在Windows应用程序中使用许多控件的大型网格的最有效方法,就像我要在不破坏网格的情况下开发它一样。

我正在使用VS 2013,在C#,.NET 4.0中开发。

谢谢!

正如@Kerry的答案所certificate的那样,winforms对几乎所有内容的回答是“你不能在winforms中做到这一点,因此你需要创建一个更适合winforms限制的更差的替代UI设计。” – 这不是我对任何体面的UI框架的期望。

这是我在10分钟内在 WPF中用大约20行C#代码50行XAML实现的

在此处输入图像描述

  • 与此WPF UI交互时的响应时间是我的计算机上的立即 (I5 CPU和常规video卡)。 即使没有虚拟化 (因为我使用的是不虚拟化的UniformGrid ),这比你希望在winforms中实现的任何东西都要好。
  • 我根据您的要求推出了一个编号为1-4的ComboBox
  • 完全可定制 (无需借助任何“所有者抽奖”黑客)。 我甚至添加了这些行和列号,当然这些都是可滚动区域的一部分。
  • Touch-Ready – 这种大滚动的UI真的更适合触摸设备。 winforms范例甚至没有考虑到的东西。 否则,您还可以使用箭头键在网格中实现类似Excel的键盘导航,以创建更好的非触摸用户体验。
  • 只需很少的努力,您还可以固定行和列标题,同时保持与整个网格的滚动偏移的一致性。
  • 这实际上是一个ListBox ,这意味着它具有SelectedItem的概念,并且它默认也展示了类似ListBox的视觉样式(您可以在所选项目上看到浅蓝色背景和轮廓)。
  • 通过创建具有Items集合的正确ViewModel然后使用ItemsControls让WPF完成创建UI的工作,逻辑 UI 分离 。 在这个例子中,没有一行C#代码可以操纵任何UI元素。 这一切都是通过漂亮的DataBinding完成的。

完整来源:

                    1 2 3 4                                     

代码背后:

 using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; namespace WpfApplication3 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new ViewModel(); } } } 

视图模型:

 public class ViewModel { public List RowMarkers { get; set; } public List ColumnMarkers { get; set; } public ObservableCollection Items { get; set; } public ViewModel() { RowMarkers = Enumerable.Range(1, 50).Select(x => x.ToString()).ToList(); ColumnMarkers = new[] { " " }.Concat(Enumerable.Range(1, 50).Select(x => x.ToString())).ToList(); var list = new List(); for (int i = 0; i < 50; i++) { for (int j = 0; j < 50; j++) { list.Add(new Item()); } } Items = new ObservableCollection(list); } } 

数据项:

 public class Item { public int? Value { get; set; } } 
  • 您可能希望向Item类添加RowColumn属性,以便您可以跟踪实际包含值的行/列。 然后您可以像这样使用LINQ:

     var values = Items.Where(x => Value != null); 

    并获取Item列表并获取每个Itemitem.RowItem.Column

  • 忘记winforms。 这完全没用。 – 此时,winforms已经完全过时了。 无论您使用winforms实现什么,您都可以在WPF中实现相同的function,代码量为10%,并且结果可能更好。 不建议将winforms用于任何新项目,仅用于维护旧版应用程序。 这是一项古老的技术,不适合满足当今用户界面的需求。 这就是微软创建WPF来取代它的原因。

  • WPF Rocks 。 只需将我的代码复制并粘贴到File -> New Project -> WPF Application然后自己查看结果。

  • 如果您需要进一步的帮助,请告诉我。

重要说明: WPF默认控件模板在Windows 8中的重量比Windows 7版本轻得多(遵循Windows 8的理念,即删除沉重的Aero内容,几乎所有透明胶片都具有较小的UI占用空间)。

这意味着在Windows 7上测试我的代码可能无法在性能方面产生预期的结果。 如果发生这种情况,请不要担心。 它是可以修复的。 必须引入少量额外的XAML(一些StyleControlTemplate )以“更快”的方式替换Windows 7默认值。

虽然你已经得到了你的答案而且这是一个老post,但我会用无用的替代方案给你一个解决方案 ,仅用于运动,并向某些人展示它并不像他们想象的那样无用。 当然,在WPF中做一些事情会更快( 如果你知道得很好),并且大多数时候可能更短,但如果你是一个真正的程序员,就没有“无用的框架”这样的东西。

这只是一个原始的例子(无论如何你都不会使用它)所以不要那么挑剔。 它可以使用一些图像用于细胞以获得更好的外观等,但这并不重要。 无论如何, 它是一个完整的控件(不需要设计器代码),因此如果您将代码粘贴到解决方案的某个地方,您就可以将它放在工具箱中的表单上 ,您将立即看到它的外观。 运行它来测试行为。 使用[x,y]对其进行索引,以编程方式获取或设置值。

 public class ValuesPanel : Panel { public int this[int x, int y] { get { return cells[x, y]; } set { cells[x, y] = value; } } private int[,] cells = new int[50, 50]; const int cell_width = 15, cell_height = 15; private int x, y, scroll_x, scroll_y; public ValuesPanel() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.OptimizedDoubleBuffer, true); AutoScrollMinSize = new Size(50 * cell_width, 50 * cell_height); BorderStyle = BorderStyle.Fixed3D; Cursor = Cursors.Hand; } protected override void OnPaint(PaintEventArgs e) { e.Graphics.FillRectangle(Brushes.DeepSkyBlue, x * cell_width - scroll_x, y * cell_height - scroll_y, cell_width, cell_height); for (int j = 0; j < 50; j++) for (int i = 0; i < 50; i++) { int xx = i * cell_width - scroll_x, yy = j * cell_height - scroll_y; e.Graphics.DrawString(cells[i, j].ToString(), Font, new SolidBrush(ForeColor), new PointF(3 + xx, 1 + yy)); e.Graphics.DrawLine(Pens.Gray, xx, yy, xx + cell_width - 2, yy); e.Graphics.DrawLine(Pens.Gray, xx, yy + cell_height - 2, xx, yy); e.Graphics.DrawLine(Pens.White, xx + 1, yy + cell_height - 1, xx + cell_width - 1, yy + cell_height - 1); e.Graphics.DrawLine(Pens.White, xx + cell_width - 1, yy + 1, xx + cell_width - 1, yy + cell_height - 1); } } protected override void OnScroll(ScrollEventArgs se) { Invalidate(); base.OnScroll(se); scroll_x = HorizontalScroll.Value; scroll_y = VerticalScroll.Value; } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); x = Math.Min(49, (e.Location.X + scroll_x) / cell_width); y = Math.Min(49, (e.Location.Y + scroll_y) / cell_height); Invalidate(); } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Left) cells[x, y] = (cells[x, y] % 4) + 1; else if (e.Button == MouseButtons.Right) cells[x, y] = cells[x, y] < 2 ? 4 : cells[x, y] - 1; } } 

我真的很喜欢WPF,但我无法忍受有人称WinForms“没用”。 继续编码! ;)

您需要将这些输入分类为逻辑组。

想象一个表单,类似于Visual Studio中的“属性”窗口。 顶部是一个combobox,列出了所有主要的逻辑组(飞机,汽车,拖拉机,公共汽车等)。 选中后,将显示datagridview,第一列是属性的名称,第二列是保存值。

所有属性都必须具有默认值,并且必须在datagridview中清楚地反映出来。

可以使用表单右下角的“保存”按钮,按下该按钮可保存当前逻辑组值。

datagridview必须是可滚动的。 您只显示15-20项,如属性窗口。

当用户从combobox中选择另一个逻辑组时,将加载该逻辑组属性和值。

这是一种更清晰的方式来获取用户输入并且易于用户理解。

不要向用户提供50×50网格,希望他们知道要填写什么,不要填写什么。 假装你正在为一个孩子设计。 使其简单易懂,易于理解。

也许,我迟到了这个派对,但我在浏览StackOverflow时偶然发现了这个老问题。 我在答案中没有看到WinForms解决方案,我认为我的答案可以帮助人们为这个平台寻找解决方案。

以下是我在一分钟内使用iGrid WinForms网格实现的解决方案:

 private iGDropDownList myCombo = new iGDropDownList(); private void Form1_Load(object sender, EventArgs e) { myCombo.Items.Add(1); myCombo.Items.Add(2); myCombo.Items.Add(3); myCombo.Items.Add(4); iGrid1.DefaultCol.CellStyle.DropDownControl = myCombo; iGrid1.Cols.Count = 50; iGrid1.Rows.Count = 50; } 

和相应的截图:

单元格组合框WinForms网格

如果需要,可以通过将ShowControlsInAllCells属性设置为false来隐藏所有combobox按钮。

使用iGrid这种解决方案的主要好处之一是它即使在便宜的低端平板电脑上也能非常快速地工作。 iGrid的单元格不包含真正的combobox控件,这一切都是在高度优化的绘图代码中绘制的,仅在视口中当前可见的单元格中。 因此,对于iGrid,无论是显示50行还是500,000行都没有区别 – 内容将以相同的速度绘制和滚动。

您可以补充一点,即使在2017年,iGrid仍在开发和支持中!