复杂的WPF用户控制在MVVM应用程序

本文关键字:MVVM 应用程序 控制 用户 WPF 复杂 | 更新日期: 2023-09-27 18:12:12

接管了现有WPF应用程序的维护工作后,我惊恐地发现其中两个view和viewmodel有大量几乎相同的代码块。显然,我想对其进行重构,以便它们都可以重用单个功能块,但我不确定如何从架构上实现这一点。

identikit代码处理来自选项卡的UI数据。无论我如何拆分它,其他选项卡中的代码(在这两种情况下是不同的)都必须能够访问我需要拆分的选项卡的属性和对象。

更复杂的是,复制的代码需要数据库访问。我们有一个存储库对象来处理这个。通常在创建新对象时,我通过将存储库的副本传递给构造函数来使它们可测试。然而,如果我在本例中这样做,我将有两个存储库对象的副本—一个在ViewModel中,一个在拆分代码中—需要处理相同的数据,这将导致并发性问题。

我的第一个想法是为这个做一个UserControl,但是我想得越多,上面的两个问题似乎就越成问题。

我考虑过的另一种选择是创建一个Helper类来执行一些相同的处理。但这只能部分解决问题,因为一些相同的UI代码(引发属性改变事件,XAML等)仍然会在两个视图/视图模型中。

这里最好的方法是什么?是否有一种方法,我可以得到过去的存储库/访问问题,并使UserControl?或者是我没有考虑过的基于接口或继承的替代方案?

编辑-代码被要求。给出一个全面的例子有点复杂,但这里是每个VM的一个片段:

public void CheckOrderHist(int months)
{
    var endDate = DateTime.Today.AddMonths(months);
    Dictionary<OrderHistory, bool> orders = new Dictionary<OrderHistory, bool>();
    this.ordersToExclude.Clear();
    foreach (var kvp in rep.OrderHistories.GetRecent(months))
    {
        if (kvp.Key.MailingDate >= endDate)
        {
            orders.Add(kvp.Key, true);
            this.ordersToExclude.Add(((OrderHistory)kvp.Key).OrderID);
        }
        else
        {
            orders.Add(kvp.Key, false);
        }
    }
    BuildOrderExclusionsOnCount(); //code in this is near-identical across VM's too
    OrderHistoryMonths = Math.Abs(months); //OrderHistoryMonths is a property on the ViewModel
    OnPropertyChanged("MajorityOrderBoolean");
}

另一个VM:

private void CheckOrderHist(int months)
{
    var endDate = DateTime.Today.AddMonths(-months);
    ObservableCollection<Tuple<OrderHistory, bool>> orders = new ObservableCollection<Tuple<OrderHistory, bool>>();
    this.ordersToExclude.Clear();
    foreach (var tuple in rep.OrderHistories.GetRecent(-months))
    {
        if (tuple.Item1.MailingDate >= endDate)
        {
            orders.Add(new Tuple<OrderHistory,bool>(tuple.Item1, true));
            this.ordersToExclude.Add(tuple.Item1.OrderID);
        }
        else
        {
            orders.Add(new Tuple<OrderHistory, bool>(tuple.Item1, false));
        }
    }
    BuildOrderExclusionsOnCount(); //code in this is near-identical across VM's too
    OrderHistoryMonths = months; //OrderHistoryMonths is a property on the ViewModel
    OnPropertyChanged("OrderHistories");
    OnPropertyChanged("GroupedOrders");
}

这很好地说明了问题——函数本质上是相同的,但一个使用Dictionary,另一个使用Tuple(没有很好的理由——它们都需要Tuple,以便于排序)。一个任意取负int形参,另一个取正int形参。

两者都包含不同的OnPropertyChanged事件,并且将使用存储库对象的不同副本,因此很难使用Helper类正确地分离它们。然而,将它放在UserControl中会将它们与主ViewModel上的OrderHistoryMonths隔离开来。

如果我没有听错当前的注释,这里最好的解决方案是将主要的ForEach循环外包给一个助手类,而只是忍受其余的重复?

复杂的WPF用户控制在MVVM应用程序

无论如何,在可能的地方提取公共逻辑到每个ViewModel可以构建的新'helper'类中;这是通过组合进行重用的标准模式。您在问题中展示的代码是这种重构的一个很好的候选。

然而,就样板文件而言,它有点棘手。这是一个难以笼统解决的问题,必须在个案的基础上加以审查。有多种方法可以简化属性更改通知,例如(封装属性更新的助手方法,AOP等),但这些通常是MVVM框架的一部分,并且在应用程序范围内都可以使用。就XAML复制而言,您通常可以使用样式、数据模板和值转换器来改进,但同样,它需要仔细分析您的特定代码库,以确定可能值得这种处理的模式。如果你有更具体的例子,你认为是明显的重复,但不确定如何重构,这些可能是很好的问题。