MVVM应用程序中的复杂WPF UserControl

时间:2014-07-09 10:38:23

标签: c# wpf mvvm user-controls

接管了现有WPF应用程序的维护后,我惊恐地发现其中两个视图和ViewModel具有大块几乎相同的代码。显然,我想重构这一点,以便它们可以重用一个功能块,但我不确定在架构上如何最好地解决它。

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

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

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

我考虑的另一个选择就是让Helper类进行一些相同的处理。但是这只会部分地解决问题,因为一些相同的UI代码(引发属性更改事件,XAML等)仍然会出现在Views / ViewModels中。

这里最好的方法是什么?有没有办法可以通过存储库/访问问题并进行UserControl?或者不是基于接口或继承的替代方案我还没考虑过?

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

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参数,另一个采用正数。

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

如果我听到当前的评论是正确的,那么这里最好的解决方案是将主要的ForEach循环分配给一个帮助类,并且只是忍受其余的重复吗?

1 个答案:

答案 0 :(得分:1)

通过各种方法,尽可能地将通用逻辑提取到每个ViewModel可以构造的新“帮助器”类;这是通过构图重复使用的标准模式。您在问题中显示的代码是进行此类重构的良好候选者。

至于样板,但它有点棘手。这是一般难以解决的问题,必须根据具体情况进行审查。有多种方法可以简化属性更改通知,例如(封装属性更新的辅助方法,AOP等),但这些方法通常是MVVM框架的一部分,并且在应用程序范围内受到欢迎。就XAML复制而言,您通常可以使用样式,数据模板和值转换器来改进,但同样需要仔细分析您的特定代码库,以确定可能值得进行此处理的模式。如果你有更多具体的例子,你认为这些例子是明确的重复,但不确定如何重构,那些可能会提出好的问题。