在Windows窗体/C#中动态创建折叠面板

3
我需要编写一个Windows窗体,用户可以查看按“客户”分组的许多“合同”。每个客户必须是可展开/折叠的面板,该客户的合同必须位于相应面板内部。
我已经尝试过伟大的ExpandCollapsePanel,但当客户数量很大时,即使将AutoScroll属性设置为true,面板也不会自动滚动。
有人知道其他替代方案吗?请记住,面板必须是动态创建的,因为有许多客户和许多属于每个客户的合同。

1
当使用 winforms 时,您应该尽可能保持 UI 界面的简单性,如果有必要,可以考虑使用一些支持丰富控件集的第三方 UI 库。只有在使用 WPF 时才建议复杂化 UI 界面,自定义复杂控件很麻烦,而且容易出错。我曾经在一个项目中遇到过这样的问题,难以忘怀。 - King King
同意@KingKing的观点,您可以在WPF中使用简单的ListBox实现此目标,同时保持UI虚拟化,以处理任何数量的数据而不会失去性能,而在winforms中尝试这样做将会像地狱一样。 - Federico Berasategui
我明白你的意思,@HighCore 发送的这个视频展示了WPF在性能方面比WinForms更加优越。我以前从未使用过WPF :( - wind39
@wind39 这个视频展示了为什么在.Net Windows应用程序中,WPF比winforms更受欢迎。如果您正在一个现有的winforms项目中工作,并且需要引入复杂(丰富)的UI功能,您可以使用ElementHost来托管WPF内容到一个Form或面板中。如果这对您是一个选项,我可以提供一些示例代码。 - Federico Berasategui
@HighCore 很棒的视频!我之前不知道 ElementHost,它非常适合我们的应用程序。如果您能提供一些示例代码,我会很高兴的,谢谢! - wind39
建议另一个接口系统并不是回答所提出的问题。有许多其他系统可以使用。请就C# WinForms的使用回答问题。 - Mike Pliam
1个回答

3
好的,我创建了一个示例,使用 ElementHost 来托管一个 WPF UserControl,它看起来像这样:

enter image description here

我已经上传了完整的源代码在这里,但无论如何,以下是最相关的部分:
Form1:
public partial class Form1 : Form
{
    public CustomerContractsViewModel ContractsVM { get; set; }

    public Form1()
    {
        InitializeComponent();

        ContractsVM  = new CustomerContractsViewModel();

        var customercontractsview = new CustomerContractsView(){DataContext = ContractsVM};

        var elementHost = new ElementHost() { Dock = DockStyle.Fill };
        elementHost.Child = customercontractsview;

        panel1.Controls.Add(elementHost);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ContractsVM.LoadCustomers(DataSource.GetCustomers());
    }
}

WPF视图:

(出于简洁起见省略设计师代码)

<UserControl x:Class="ElementHostSamples.CustomerContractsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <!-- This style is applied to all Label elements within the UserControl-->
        <Style TargetType="Label">
            <Setter Property="FontWeight" Value="Bold"/>
            <Setter Property="HorizontalAlignment" Value="Right"/>
        </Style>

        <!-- This DataTemplate will be used to render the Contract items-->
        <DataTemplate x:Key="ContractTemplate">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Label Grid.Row="0" Grid.Column="0" Content="Contract Date:"/>
                <Label Grid.Row="1" Grid.Column="0" Content="Amount:"/>

                <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding ContractDate, StringFormat='MM/dd/yyyy'}" VerticalAlignment="Center"/>
                <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Amount, StringFormat=C}" VerticalAlignment="Center"/>
            </Grid>
        </DataTemplate>

        <!-- This DataTemplate will be used to render the Customer Items -->
        <DataTemplate x:Key="CustomerTemplate">
            <Expander Header="{Binding Name}">
                <ListBox ItemsSource="{Binding Contracts}" ItemTemplate="{StaticResource ContractTemplate}">
                    <ListBox.Template>
                        <ControlTemplate TargetType="ListBox">
                            <ItemsPresenter/>
                        </ControlTemplate>
                    </ListBox.Template>
                </ListBox>
            </Expander>
        </DataTemplate>
    </UserControl.Resources>

    <ListBox ItemsSource="{Binding Customers}"
             ItemTemplate="{StaticResource CustomerTemplate}"/>
</UserControl>

代码后台:

public partial class CustomerContractsView : UserControl
{
    public CustomerContractsView()
    {
        InitializeComponent();
    }
}

视图模型:
public class CustomerContractsViewModel:PropertyChangedBase
{
    public List<Customer> Customers { get; set; }

    public void LoadCustomers(List<Customer> customers)
    {
        Customers = customers;
        OnPropertyChanged("Customers");
    }
}

注意这个简单的、不到100行代码的、20分钟的WPF示例比你在Winforms中能够实现的任何东西都要好,而且不需要任何“owner draw”、“P/Invoke”(不管那是什么)或者可怕的庞大的代码后台。也不会强迫你花费大量资金购买DevExpress或Telerik等第三方组件。这就是为什么WPF是所有.Net Windows桌面应用程序开发的最佳选择,无论它是一个简单的Hello World类型的东西还是其他。
我正在使用ItemsControl来托管Customer项目,里面我使用了一个带有自定义DataTemplate的ListBox来显示Contract项目。
两个ItemsControl(外部和内部)都是Virtualized的,以便立即响应时间,即使有20万个项目也是如此。
请注意,没有一行代码与UserControls的UI元素交互,一切都在XAML中定义,并通过DataBinding填充数据。这使得UI完全脱离应用逻辑/业务逻辑,具有很高的可扩展性和可维护性。这就是WPF的方式。
表单代码(初始化代码除外)只与ViewModel交互,不需要与WPF View交互。
当从winforms升级到WPF时,你需要认真接受The WPF Mentality,也就是说,如前所述,你几乎从不在过程代码中操作UI元素或使用过多的代码后台,而是为所有事情使用DataBinding并拥抱The MVVM Pattern
WPF很棒。下载链接的源代码并亲自查看结果。
如果需要进一步帮助,请告诉我。

2
无论是WPF还是你,@HighCore,都非常棒!我该如何表达我的感激之情呢?你以最佳方式解决了我们的问题:1)在短短几分钟内;2)以最佳方式;3)以最快的方式;4)使用干净、直观、可维护的代码;5)提供高质量的文档,让我相信WPF比WinForms更优越。最重要的是,现在我将学习这项技术,并在其他情况下使用它。非常感谢你! - wind39

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接