C# 如何在UserControl中扩展模型?

C# 如何在UserControl中扩展模型?,c#,wpf,xaml,mvvm,user-controls,C#,Wpf,Xaml,Mvvm,User Controls,我想了解如何在WPF中编写适当的用户控件/视图模型。为了简单起见,我发明了以下示例: 比如说,我们有一个DateRange类,定义为: 使用系统; 名称空间MyDateApp { 公共类日期范围 { 公共日期时间开始 { 得到; 设置 }=新的日期时间(); 公共整数长度//以天为单位 { 得到; 设置 } = 0; } } (为了便于论证,我们假设该类不能以任何方式修改。) 类的实例用作窗口的数据上下文: 使用系统; 使用System.Windows; 名称空间MyDateApp { 公共部

我想了解如何在WPF中编写适当的用户控件/视图模型。为了简单起见,我发明了以下示例:

比如说,我们有一个
DateRange
类,定义为:

使用系统;
名称空间MyDateApp
{
公共类日期范围
{
公共日期时间开始
{
得到;
设置
}=新的日期时间();
公共整数长度//以天为单位
{
得到;
设置
} = 0;
}
}
(为了便于论证,我们假设该类不能以任何方式修改。)

类的实例用作窗口的数据上下文:

使用系统;
使用System.Windows;
名称空间MyDateApp
{
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DateRange=new DateRange();
range.Start=新日期时间(2020,1,1);
范围:长度=5;
数据上下文=范围;
}
}
}
我想实现一个自定义视图/控件,它允许应用程序的用户选择开始日期和结束日期,而不是开始日期和范围。它将被包装在名为
DateControl
UserControl
中:


我能够得到这个
DateControl
工作的基本实现:

使用系统;
使用System.Windows;
使用System.Windows.Controls;
名称空间MyDateApp
{
公共部分类DateControl:UserControl
{
公共数据控制()
{
初始化组件();
}
公共日期范围
{
获取{return(DateRange)GetValue(RangeProperty);}
set{SetValue(RangeProperty,value);}
}
公共静态只读DependencyProperty RangeProperty=
Register(“Range”、typeof(DateRange)、typeof(DateControl)、newpropertyMetadata(new DateRange());
公共日期时间结束
{
get=>Range.Start+newtimespan(Range.Length,0,0,0);
set=>Range.Length=(value-Range.Start).Days;
}
}
}
使用以下XAML:


它适用于开始日期,但结束日期的值是错误的。因此,我的问题是:

我怎样才能正确地实现这一点

我成功的条件是:

  • 原始模型类必须保持不变
  • 用户控件必须更新主窗口数据上下文

好处:我必须实现什么才能将此控件用作
ListBoxItem

创建一个位于视图和模型之间的视图模型,并将视图中的控件绑定到视图模型,而不是直接绑定到视图,例如:

public class ViewModel
{
    //error handling and validation omitted for brevity...

    private readonly DateRange _model = new DateRange();

    private DateTime _startDate;
    public DateTime StartDate
    {
        get
        {
            return _model.Start;
        }
        set 
        {
            _model.Start = value;
        }
    }

    private DateTime _endDate;
    public DateTime EndDate
    {
        get { return _endDate; }
        set 
        { 
            _endDate = value;
            _model.Length = (int)_endDate.Date.Subtract(_startDate.Date).TotalDays;
        }
    }
}

直接向视图公开模型通常是个坏主意

这包括“包装”模型属性的方法

这对于普通的应用程序来说是可行的,但是您会发现大量的边缘情况使得它不具有吸引力。 例如,用户编辑模型类。 验证失败。 您的模型中现在有错误的数据。 它也相当笨重

我建议将模型类作为DTO来考虑

使用相应的属性和您不可避免地需要的额外属性构建viewmodel

实例化该视图模型,并从模型中复制数据以将其显示给视图

数据库>模型>视图模型>视图

在编辑/插入时,执行相反的操作并实例化一个模型以传递回存储库或实体框架

视图>视图模型>模型>数据库

这样,您的DataAnnotation就可以愉快地生活在viewmodel中,而不会污染模型。如果您使用的是ORM,比如实体框架,那么这将非常方便

对于特别复杂的场景,您甚至可能需要另一层用于特定于域的逻辑的类

对属性的直接复制,请考虑AutoPaul.

编辑:

使用这种方法,viewmodel与模型完全断开连接,因此视图不会更改它。从而满足要求1

此视图模型应由mainwindowviewmodel作为私有成员实例化。主窗口的公共属性将设置为实例。对于列表,属性将是这些viewmodels的列表或observablecollection

我会考虑删除USER控件中的依赖属性,而将逻辑放进新的VIEW模型。

我不明白这个逻辑到底是什么,但我希望你不会对你设计的每个viewmodel提出问题,这里的任何答案都应该说明原理

因此,viewmodel可能看起来像:

public class DateRangeViewModel : BindableBase
{
    public DateRangeViewModel(DateTime _datefrom, int _length)
    {
        length = _length;
        fromDate = _datefrom;
        setUpDate();
    }
    private void setUpDate()
    {
        ToDate = ((DateTime)fromDate).AddDays(length);
    }

    private int length = 0;

    public int Length
    {
        get => length;
        set { setUpDate(); SetProperty(ref length, value, nameof(Length)); }
    }
    private DateTime? fromDate;

    public DateTime? FromDate
    {
        get => fromDate;
        set => SetProperty(ref fromDate, value, nameof(FromDate));
    }

    private DateTime? toDate;

    public DateTime? ToDate
    {
        get => toDate;
        set { setUpDate(); SetProperty(ref fromDate, value, nameof(ToDate)); }
    }
 }
windowviewmodel读取数据,这些数据来自作为模型的任何地方


对于每个新建的对象,都会创建一个DateRangeViewModel,将fromdate和length属性传递给ctor。

数据上下文会自动传递给用户控件。不需要依赖项属性。因此,您可以简单地绑定到ViewModel中的属性开始和结束,并回答您的条件:“原始模型类必须保持不变”,然后可能从模型继承。让用户界面尽可能的愚蠢。否则,业务逻辑总是依赖于UI。第二:这不是您的模型,而是您的viewmodel。@Klamsi“DataContext自动传递给您的UserControl”是什么意思?我可以只做
而不在主窗口绑定?@Klamsi我想分离会很好,但我的业务逻辑必须存在于某个地方。。。我不知道我将如何执行你的想法。我以前已经了解了你所说的基本要点。然而,我的问题是,虽然我可能在概念层面上理解它,但我无法将其转化为代码。视频创作者和博客作者遗漏了所有这些小的实现细节。这些事实上可能微不足道