【问题标题】:WPF - Instance subclass from superclassWPF - 超类的实例子类
【发布时间】:2021-10-10 17:37:42
【问题描述】:

我正在使用 WPF 应用程序,并且我有一个 ViewModel,我在多个视图和 DataGrids 中使用它。

现在我有另一个 View 需要该 ViewModel 的 扩展装饰 版本。所以我决定以这种方式去继承:

public class StandardViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class ExtendedViewModel : StandardViewModel
{
    public string Email { get; set; }
}

但是,我想装饰StandardViewModel现有实例。特别是在 DataGrid 中选定的对象,因此可以将其传递到其他视图中。

新视图需要访问两个类的属性(EmailFirtsNameLastName

所以我正在想办法为我的ExtendedViewModel 创建一个构造函数。

我的想法是直接复制基础实例。

  • 这是正确的吗?
  • 效率高吗?
  • 还有其他方法吗?
public class ExtendedViewModel : StandardViewModel
{
    public string Email { get; set; }

    public ExtendedViewModel(StandardViewModel base)
    {
        this = base
    }
}

编辑

我不仅为一门课,而且为几门课这样做。而且它们不仅有两个属性,所以我试图避免一个一个地复制这些值。

【问题讨论】:

  • 你注意到this = base 不会编译吗?您必须复制属性。
  • 我无法访问我的电脑,所以我在写代码。我是否需要一一复制属性?还有其他方法吗?
  • 我可以使用静态方法来构造新类而不必复制每个变量吗?我不知道这样的东西是否会编译:public static ExtendedViewModel MyConstructor(StandardViewModel base) {ExtendedViewMode vm = new(); vm = base; return vm;}
  • 复制属性可能是这里最简单的解决方案。如果您的基类有copy constructor,那么您可以使用它来复制。你也可以看看record,如果它适合你的需要。

标签: c# inheritance


【解决方案1】:

简单地说,装饰器拥有它所装饰的类的一个实例,并将拥有的类中存在的功能委托给该类,同时添加与拥有的类实例分离的新功能。

public class StandardViewModel
{
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class ExtendedViewModel : StandardViewModel
{
    private StandardViewModel _standard;
    public ExtendedViewModel (StandardViewModel standard)
    {
         if (standard.GetType() != typeof(StandardViewModel )) {
            throw new ArgumentException ("Expected a non derived standard view model", nameof(standard));
         }
        _standard = standard;
    }
    public string Email { get; set; }
    public override string FirstName { 
        get => _standard.Firstname; 
        set  => _standard.Firstname = value; 
    }
    public override string LastName { 
        get => _standard.LastName; 
        set  => _standard.LastName = value; 
    }

}

【讨论】:

  • 我认为大多数 ViewModel 都实现了INotifyPropertyChanged 接口。您将如何在 ExtendedViewModel 中实现它?特别是当装饰类的属性直接改变时也反映变化。有什么棘手的方法吗?
  • 感谢装饰器的示例。但是,正如@Steeeve 所说,我需要以某种方式订阅INotifyPropertyChanged 接口。
  • @Steeve 虽然它真正的大多数虚拟机都实现了 INPC,但它与所提出的问题无关。我只是使用 OP 的代码来展示如何按照装饰模式(或类似的东西)扩展此代码。我不会把业余时间花在免费的在线编码服务上。
【解决方案2】:

最后我将避免使用继承,我将创建一个新类,公开基类并订阅INotifyPropertyChanged,如herehere 所述。

这样我就可以将依赖于Base ViewModel 的属性更新为下面的FullName

生成的 ViewModel 将如下所示:

public class ExtendedViewModel
{
    public StandardViewModel Base { get; set; }
    public string Email { get; set; }

    public string FullName {
        get => Base.FirstName + Base.LastName;
    }

    public ExtendedViewModel(StandardViewModel base)
    {
        Base = base
        Base.PropertyChanged += BaseChanged
    }

    private void BaseChanged(object sender, PropertyChangedEventArgs e)
    {
        // Here check if FirstName or LastName changed and
        RaisePropertyChanged("FullName");
    }
}

在视图中,我将直接绑定到EmailBase.FirstName

【讨论】:

  • 您如何使StandardViewModelExtendedViewModel 的集合保持同步?
  • 我不确定你的问题是否正确。现在 ExtendedViewModel 确实 not 子类 StandardViewModel 我没有任何重复的属性要保持同步。每个视图模型都处理自己的属性。通过BaseChanged 通知,我可以检测到StandardViewModel 中影响ExtendedViewModel 的那些变化。
  • 那么当您在两个单独的数据网格上绑定ItemsSource 时,它们是否绑定到相同的属性/集合?从问题的上下文来看,我假设您现在拥有每种类型的集合,ExtendedViewModel 集合取决于 StandardViewModel 集合的元素。继承或组合与装饰器模式一样有效,无论您选择哪种方式,实际上都取决于您实际尝试做什么以及您可能尝试管理多少元素。
  • 是的。所以我在数据网格中有一个StandardViewModel 的集合。然后,将选中的元素添加到ExtendedViewModel 中以在表单中进行编辑(我在DataGrid 中没有直接编辑)。在 ExtendedViewModel 表单中所做的更改正在按预期显示在 DataGrid 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多