c# – 需要一个非常自定义的大型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的答案所证明的那样,winforms对几乎所有内容的回答是“你不能在winforms中做到这一点,因此你需要创建一个更适合winforms限制的更差的替代UI设计.” – 这不是我对任何体面的UI框架的期望.

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

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

>您可能希望向Item类添加Row和Column属性,以便您可以跟踪实际包含值的行/列.然后您可以像这样使用LINQ:

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

并获取一个Items列表,并获取每个项目的item.Row和Item.Column.
>忘记winforms.这完全没用. – 此时,winforms已经完全过时了.无论您使用winforms实现什么,您都可以在WPF中实现相同的功能,代码量为10%,并且结果可能更好.不建议将winforms用于任何新项目,仅用于维护旧版应用程序.这是一项古老的技术,不适合满足当今用户界面的需求.这就是微软创建WPF来取代它的原因.
> WPF Rocks.只需将我的代码复制并粘贴到文件中 – >新项目 – > WPF应用程序并亲自查看结果.
>如果您需要进一步的帮助,请告诉我.

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).

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”.

https://stackoverflow.com/questions/21895592/need-a-very-customized-large-winforms-grid

转载注明原文:c# – 需要一个非常自定义的大型Winforms网格