繁体   English   中英

如何为 C# WPF 中的用户控件集合设计基于页面的视图?

[英]How do I design a page based View for a collection of UserControls in C# WPF?

前言:用问题的形式解释我的问题有点困难,所以让我在下面解释一下。

背景:我正在为音频混音器开发一个 UI(见图),作为其中的一部分,我有一排 16 个“通道条”(一个用户控件),每个都带有一个推子。 不过,在 model 中,混音器有 32 个通道(+ 辅助)。 为了消除对带有滚动条的超大 ItemsControl 的需求,我想实现一个页面系统来切换 model 中的哪些通道也绑定了 UI。

UI 的模拟。 UI 的模拟。

在阅读了一些关于 MVVM 架构的示例后,我将其范围缩小到实现这一点的太多方法,但我对每个都有问题。

  1. 将 UI 中的通道条直接绑定到 ViewModel 中的 ObservableCollection,然后使用属性通知器将其绑定到 model 中的数据:
<ItemsControl x:Name="FaderPane1_8" Background="{DynamicResource FaderPanel}" Margin="0" ItemsSource="{Binding Faders}" ScrollViewer.CanContentScroll="False" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Disabled">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <local:ChannelStrip MaxWidth="50" FaderValue="0"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

主窗口.xaml

 public ObservableCollection<ChannelStrip> Faders
 {
    get { return _faders; }
    set { SetProperty(ref _faders, value); }
 }

 public void Init()
 {
    //Create a new mixer
    VMixer = new Mixer(32, 8, 8);

    VMixer.MixChannels.CollectionChanged += MixChannels_CollectionChanged;
 }
 //More code to handle "MixChannels_CollectionChanged" as well as changes from ViewModel to Model

视图模型.cs

问题在于,这会为各个方向的属性更改创建大量事件处理程序,并最终生成 memory 中的推子副本。 所有这些对我来说似乎都是一个糟糕的设计,但这是我所看到的约定俗成的,因为它允许 Model 与 ViewModel 和 View 完全分离,并且没有从 View 到 Model 的直接链接。

  1. 每次更改页面时,直接绑定到 model 的一部分并使用 ViewModel 将 UI 通道条重新绑定到一组不同的通道。
    这似乎更明智,因为我没有创建不必要的数据和事件副本,并且可能会减少混乱的代码。
<Grid x:Name="FaderPane9_16" Background="{DynamicResource FaderPanel}" Margin="0" Grid.Column="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <local:ChannelStrip/>
                <local:ChannelStrip Grid.Column="1"/>
                <local:ChannelStrip Grid.Column="2"/>
                <local:ChannelStrip Grid.Column="3"/>
                <local:ChannelStrip Grid.Column="4"/>
                <local:ChannelStrip Grid.Column="5"/>
                <local:ChannelStrip Grid.Column="6"/>
                <local:ChannelStrip Grid.Column="7"/>
            </Grid>

主窗口.xaml

 public void UpdateFaderBindings(ChannelType faderMode, int faderModePage)
 {
     //[Code omitted for simplicity]

     for (int i = 0; i < channels; i++)
     {
         int newFaderIndex = -1;
         //[Code omitted for simplicity]

         Binding b = new Binding("VMixer.Channels[newFaderIndex].FaderValue");//I know this is wrong I'm trying to demonstrate the idea
         ChannelStrips[i].SetBinding(ChannelStrip.FaderValue, b);
     }
 }

视图模型.cs

这两种架构显然都有自己的缺点,经过数小时的阅读,我无法确定在这种情况下正确的架构是什么。

我以写WPF机场值机系统为生,一直以飞机座位选择的形式遇到这个问题。 没有足够的空间来展示 A380 的所有 525 个座位,所以我必须展示机身的各个部分并让乘客滚动浏览它们。 类似地,一个值机亭可能需要向 select 的乘客提供 50 家不同的航空公司,因此再次使用分页来滚动它们。

您在这里真正要做的是实现一种虚拟化形式。 反过来,这需要视图逻辑。 每当您有不需要与实际 GUI 元素直接交互的视图逻辑时,放置它的正确位置几乎总是在视图 model 层中。 在商业应用程序中,这绝对是您想要进行单元测试的行为,但是使用选项#2,如果没有实际存在的 GUI 元素,您将无法做到这一点。

选项 #1 看起来很混乱的原因是,尽管它更接近于纯 MVVM。 你真正应该做的是在你的视图和你的视图模型之间创建一个 1:1 的关系。 我个人会为每个混音器(可见或不可见)创建一个 MixerViewModel class,其中包含该混音器所需的信息,并维护当前可见的列表:

private IList<MixerViewModel> AllMixers;
public ObserveableCollection<MixerViewModel> VisibleMixers {get; set;} // would probably also need INPC

第一个列表适用于所有 32 台混音器,并在启动时创建。 第二个列表是当前可见的,只要当前页面发生变化,您就可以使用第一个列表中的元素填充它。 这样做有助于完全分离关注点,它可以很容易地更改项目的总数(总数或一次可见),并且您现在还可以进行单元测试。 是的,这意味着每当页面更改时都会创建和销毁 GUI 项目,但这就是 WPF 的设计用途,只要您不使用 go,您的应用程序就会保持响应。

绝对第一个解决方案更合适,但我可能还会尝试通过简单地在 UI 中创建 2 个单独的 ItemsControls(绑定到同一个 ObservableCollection)来避免所有处理程序,它们彼此叠加,第二个隐藏和更改通道的按钮简单地还原两个 ItemsControls 的可见性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM