需要一个非常定制的大型 Winforms 网格 [英] Need a very customized large Winforms grid

查看:19
本文介绍了需要一个非常定制的大型 Winforms 网格的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am about to develop a Windows PC application (it can be WinForms or WPF) and my main concern is a UI problem I will have to address.

基本上,我需要一个大约 50x50 的网格,我需要从用户那里获取输入.那是 2500 个字段.实际上,大多数将留空,大约 10% 将由用户填写.每个字段可以为空或有一个从 1 到 4 的数字.我想要简单的输入 - 也许是一个下拉框(因为在使用键盘时对所有 2500 个字段进行选项卡没有意义,我希望用户填写鼠标的值).

Basically, I need to have a grid of about 50x50 that I need to get input from the user for. That's 2500 fields. Realistically most will be left blank, about 10% will be filled out by a user. Each field can be blank or have a number from 1 to 4. I want easy input - a drop down box perhaps (since it doesn't make sense to tab through all 2500 fields when with the keyboard, I want the user to fill out the values with the mouse).

我在想下拉框或什至标签会在您单击它们时更改值,但问题是(根据我所做的测试)添加 2500 种任何类型的控件都会使界面变得非常缓慢.我尝试在带有挂起/恢复更新功能的 WinForms 应用程序中使用 tablelayoutpanel,还有双缓冲,这有点帮助,但它仍然非常慢.我不愿意走 DataGridView 路线,因为我需要非常自定义的标题,并且我需要 UI 在用户更改字段中的值时自动更新某些百分比.但如果这是我唯一的选择,我不会反对.

I was thinking the drop down boxes or maybe even labels that change value when you click them, but the problem is (from the tests I've done) adding 2500 of ANY type of control will make the interface horribly slow. I tried using a tablelayoutpanel in a WinForms app with the suspend/resumeupdate functions, and also doublebuffering and that helps a bit, but it's still terribly slow. I am reluctant to go the DataGridView route because I need VERY custom headers and I need the UI to auto update some percentages as the user changes values in the fields. But I will not be opposed if that's my only option.

我听说 WPF 可能会更好,因为你可以有很多控件,而且每个控件都没有自己的 Windows 句柄,而且还有虚拟化(不知道实现起来有多难).

I heard WPF may be better since you can have many controls and each one doesn't take its own windows handle, and there's virtualization (not sure how hard that is to implement).

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

I'm open to suggestions. I know someone will suggest to break up the grid, which I may end up doing. Either way, I'd like to know the most efficient method for a large grid with many controls in a Windows app as if I were going to develop this without breaking up the grid.

我使用的是 VS 2013,使用 C#、.NET 4.0 进行开发.

I'm using VS 2013, developing in C#, .NET 4.0.

谢谢!

推荐答案

正如@Kerry's answer 所证明的,winforms 对几乎所有事情的回答都是你不能在 winforms 中这样做,因此你需要创建一个适合 winforms 限制的更糟糕的替代 UI 设计." - 这不是我对任何体面的 UI 框架的期望.

As demonstrated by @Kerry's answer, the winforms answer to almost everything is "you can't do that in winforms, therefore you need to create a much poorer substitute UI design that fits into winforms limitations." - that is not what I would expect from any decent UI framework.

这是我在 10 分钟中使用 20 行 C# 代码50 行 XAMLWPF 中实现的/strong>:

This is what I achieved in WPF in 10 minutes with some 20 lines of C# code and 50 lines of XAML:

  • 在我的机器(I5 CPU 和普通显卡)上与此 WPF UI 交互时的响应时间立即.即使没有虚拟化(因为我使用的是没有虚拟化的UniformGrid),这也比您希望在winforms中实现的任何事情都要好.
  • 我按照您的要求引入了一个带有数字 1-4 的 ComboBox.
  • 完全可定制(无需借助任何所有者绘制"技巧).我什至添加了这些行和列编号,当然它们都是可滚动区域的一部分.
  • Touch-Ready - 这种大滚动 U​​I 确实更适合触摸设备.winforms范式甚至没有考虑到的东西.否则,您还可以使用箭头键在网格中实现类似 Excel 的键盘导航,以创建更好的非触摸用户体验.
  • 只需努力,您还可以固定行和列标题,同时保持它们与整个网格的滚动偏移的一致性.
  • 这实际上是一个ListBox,这意味着它有SelectedItem的概念,并且默认展示了类似ListBox的视觉风格(你可以看到灯光-所选项目的蓝色背景和轮廓).
  • 逻辑与 UI 解耦,方法是创建一个合适的 ViewModel 和一组 Items,然后使用 ItemsControls 让 WPF 完成创建 UI 的工作.此示例中没有一行 C# 代码可以操作任何 UI 元素.这一切都是通过漂亮的 DataBinding.
  • The response time when interacting with this WPF UI is Immediate on my machine (an I5 CPU and a regular video card). Even with no virtualization (since I'm using an UniformGrid which does not virtualize), this is way better than anything you can ever hope to achieve in winforms.
  • I introduced a ComboBox with numbers 1-4 as you requested.
  • Fully customizable (without resorting to any "owner draw" hacks). I even added these Row and Column numbers which of course are all part of the scrollable area.
  • Touch-Ready - this kind of big-scrolling UI is really better suited for a touch device. Something that the winforms paradigm does not even take into account. Otherwise, you could also implement Excel-like keyboard navigation in the grid with the arrow keys to create a better non-touch user experience.
  • With a small effort you could also make the row and column headers fixed while retaining their consistency with the scroll offset of the entire grid.
  • This is actually a ListBox, which means it has the concept of SelectedItem and it also by default exhibits the ListBox-like visual style (you can see the light-blue background and outline on the selected item).
  • The logic is decoupled from the UI by creating a proper ViewModel with a collection of Items and then using ItemsControls to let WPF do it's job of creating the UI. There's not a single line of C# code in this example that manipulates any UI element. It's all done via beautiful DataBinding.

完整来源:

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate x:Key="MarkerTemplate">
            <Border BorderBrush="Gray" BorderThickness="1" Margin="1" Background="Gainsboro">
                <Grid Width="50" Height="30">
                    <TextBlock Text="{Binding}" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                </Grid>
            </Border>
        </DataTemplate>

        <Style TargetType="ListBoxItem">
            <Setter Property="Padding" Value="0"/>
        </Style>
    </Window.Resources>

    <DockPanel>
        <ListBox ItemsSource="{Binding Items}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="Gray" BorderThickness="1">
                    <Grid Width="50" Height="30">
                        <TextBlock Text="{Binding Value}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        <ComboBox x:Name="ComboBox" SelectedItem="{Binding Value}" 
                                  IsDropDownOpen="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                                  Visibility="Collapsed">
                            <sys:Int32>1</sys:Int32>
                            <sys:Int32>2</sys:Int32>
                            <sys:Int32>3</sys:Int32>
                            <sys:Int32>4</sys:Int32>
                        </ComboBox>
                    </Grid>
                    </Border>
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
                            <Setter TargetName="ComboBox" Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
                </DataTemplate>
            </ListBox.ItemTemplate>

            <ListBox.Template>
                <ControlTemplate TargetType="ListBox">
                    <ScrollViewer CanContentScroll="True" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
                        <DockPanel>
                            <ItemsControl DockPanel.Dock="Top" ItemsSource="{Binding ColumnMarkers}"
                                ItemTemplate="{StaticResource MarkerTemplate}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <VirtualizingStackPanel Orientation="Horizontal"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>

                            <ItemsControl DockPanel.Dock="Left" ItemsSource="{Binding RowMarkers}"
                                          ItemTemplate="{StaticResource MarkerTemplate}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <VirtualizingStackPanel Orientation="Vertical"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>

                            <UniformGrid Rows="50" Columns="50" IsItemsHost="True"/>
                        </DockPanel>
                    </ScrollViewer>
                </ControlTemplate>
            </ListBox.Template>
        </ListBox>
    </DockPanel>
</Window>

背后的代码:

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<string> RowMarkers { get; set; }

    public List<string> ColumnMarkers { get; set; }

    public ObservableCollection<Item> 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<Item>();

        for (int i = 0; i < 50; i++)
        {
            for (int j = 0; j < 50; j++)
            {
                list.Add(new Item());
            }
        }

        Items = new ObservableCollection<Item>(list);
    }
}

数据项:

public class Item
{
    public int? Value { get; set; }
}

  • 您可能希望将 RowColumn 属性添加到 Item 类,以便您可以跟踪哪些行/columns 实际上包含值.然后你可以像这样使用 LINQ:

    • You will probably want to add Row and Column properties to the Item class so that you can keep track of what rows/columns actually contain values. Then You could use LINQ like so:

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

      并获取Item的列表,并为每个获取item.RowItem.Column.

      and obtain a list of Items and get item.Row and Item.Column for each of them.

      忘记winform.它完全没用. - 在这一点上,winforms 已经完全过时了.无论您可以使用 winforms 实现什么,您都可以在 WPF 中使用 10% 的代码量实现相同的目标,并且可能会获得更好的结果.winforms 不推荐用于任何新项目,仅用于维护遗留应用程序.这是一项古老的技术,不适合满足当今的 UI 需求.这就是微软创建 WPF 来替换它的原因.

      Forget winforms. It's completely useless. - at this point, winforms is completely obsolete. Whatever you can achieve with winforms, you can achieve the same thing in WPF with 10% the amount of code and probably with much better results. winforms is not recommended for any new projects, only to maintain legacy applications. It is an ancient technology that is not suited to cater for today's UI needs. That's why Microsoft created WPF to replace it.

      WPF 摇滚.只需将我的代码复制并粘贴到 File ->新项目 ->WPF 应用程序 并亲自查看结果.

      WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.

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

      Let me know if you need further help.

      重要说明: WPF 默认控件模板在 Windows 8 中比它们的 Windows 7 对应项轻得多(遵循Windows 8 删除沉重的 Aero 东西和实际操作的哲学所有透明胶片都具有更小的 UI 占用空间).

      Important Note: the WPF default control templates are much lighter weight in Windows 8 than their Windows 7 counterparts (following the Windows 8 philosophy of removing the heavy Aero stuff and practically all transparencies to have a smaller UI footprint).

      这意味着在 Windows 7 上测试我的代码可能不会产生性能方面的预期结果.如果这恰好是情况,别担心.它是可以修复的.少量额外的 XAML必须引入(一些 Styles 和 ControlTemplates)到用更快"的东西替换 Windows 7 默认设置.

      This means that testing my code on Windows 7 might not yield the expected results in terms of performance. If that happens to be the case, Don't worry. It is fixable. A small amount of additional XAML would have to be introduced (some Styles and ControlTemplates) to replace the Windows 7 defaults by something "faster".

      这篇关于需要一个非常定制的大型 Winforms 网格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆