【问题标题】:MVVM - Who is responsible forMVVM - 谁负责
【发布时间】:2016-04-27 08:13:36
【问题描述】:

我现在有点困惑,因为我不确定“谁负责验证模型数据”之类的事情。

举个例子:

我有一个应用程序,其中人员和特定设备之间的关系被可视化。

public class Person{
    public string Firstname {get; set;}
    public string Lastname {get; set;}
    public SomeSortOfDevice SomeSortOfDevice {get; set;}
}

public class SomeSortOfDevice{
    public DateTime DeviceExpiration {get; set;}
    public string DeviceSerialCode {get; set;}
    //public bool IsSerialCodeValid{get{
    //     SomeValidationLogic()
    //}
}

在视图中,应用用户可以更改设备的数据。 现在有一些规则 - 例如,DeviceExpiration 绑定到一个 TextBox,但只有在设置了有效的 SerialCode 时才应该启用。

但是谁来决定 SerialCode 是否有效?模型本身?视图模型?模型是否应该拥有一个额外的属性“IsSerialValid”,或者如何将 TextBox 的 IsEnabled 属性绑定到该规则?

编辑 1

public class ViewModel{
    public Person SelectedPerson {get; set;}
}

这就是我如何使用上面的模型 - 视图将在 SeletedPerson 上获得一些绑定。

编辑 2

我认为这比我之前解释的要复杂一些。假设设备是一个令牌,我想确定用户是否输入了一些东西 - 如果他输入了一些东西,它是否有效?

如果验证应该在模型中处理(正如我现在所知道的那样),模型将如下所示:

public Token{
    public DateTime ExpirationDate {get; set;}
    public string Serial {get; set;}
    public bool IsTokenExpired{
        get{
            return ExpirationDate.Date < DateTime.Now;
        }
    }
    public bool IsTokenValid{
        get{
            return new Regex("[0-9]{8,12}").Match().Success;
        }
    }
    public bool IsTokenSet{
        get{
            return TokenSerial.Length > 0;
        }
    }
}

现在启用用户应输入(或选择)日期的文本框,绑定如下所示:

<TextBox IsEnabled="{Binding SelectedPerson.Token.IsTokenValid}"/>

这很好用,但我对模型中的验证规则并没有很好的感觉。

【问题讨论】:

  • ViewModel 应该处理验证
  • 如果验证通过,您能告诉我们您要执行的命令吗?
  • 查看IDataErrorInfo,WPF控件支持。
  • 我听说过 IDataErrorInfo 但我不想通知用户串行的无效状态 - 我只想禁用另一个控件。
  • 那么我们不是在讨论验证本身,而是在讨论 UI 逻辑,这是 VM 的一项任务。

标签: c# wpf validation mvvm


【解决方案1】:

嗯,这只是一个意见,但我会这样做。

首先,你的模型应该有一个验证方法,比如public bool ValidateSerial(string serial);

然后,在您的 ViewModel 中,您将有一个绑定到 TextBox 的属性,您将在那里进行检查,类似于:

_private string _serialNumber;
public string SerialNumber
{
    get
    {
         return this._serialNumber;
    }
    set
    {
         this._serialNumber=value;
         RaisePropertyChanged("SerialNumber");
         this.IsSerialValid=Model.ValidateSerial(string serial);
         RaisePropertyChanged("IsSerialValid");
    }
    public bool IsSerialValid { get; set;}
}

IsSerialValid 属性是您将绑定到另一个 TextBox 的 IsEnabled 属性的属性。

希望这对你有意义。

【讨论】:

  • 这看起来很像我目前的解决方案 - 正如我在编辑中提到的那样,我对此并不满意
  • 我注意到在这种情况下 SerialNumber 将是您的 ViewModel 的一个属性 - 如果 SerialNuber 已经包含在我的模型中,我为什么要这样做?
  • 因为在模型中不会是一个属性,而是一个验证方法。我更改了名称,因为它令人困惑@Chill-X
【解决方案2】:

我认为这完全取决于具体的设计。一般来说,您可以在模型中实现一些逻辑检查,但模型不应该(也不能)与 UI 通信。因此,如果出现问题,模型通常会拒绝存储数据。

控件的可见性应在 ViewModel 中处理,正如您在代码中正确建议的那样。但是,在您的情况下,当有人输入错误的序列号时,不会更改 IsValid 属性(因为 in 是按需计算的)。更好的方法是检查 DeviceSerialCode 设置器中的序列号,并从那里设置 IsSerialCodeValid。为了让 UI 反映更改,您的 ViewModel 还应该实现 INotifyPropertyChanged 接口,并在其设置器中向属性发出更改信号。

【讨论】:

  • 当你说模型不应该与视图通信时——这是否意味着引用例如不正确SelectedPerson.Firstname (因为 SelectedPerson 的类型是 Person (Model)),而是在视图模型本身中实现了一个额外的 firstname 属性?好像这会导致一团糟......
  • 我认为在数据绑定中引用模型对象是可以的,但通常你不应该在模型中创建额外的字段来反映一些 UI 属性(例如元素的可见性等)。模型应该反映问题域,视图模型应该处理所有的视图逻辑。
【解决方案3】:

无需持有额外的财产。只需实现 INotifyPropertyChanged 接口,它会在属性值更改时发出事件。

您可以在视图模型中监听属性的属性更改事件并在那里进行检查。这也是视图在 MVVM 模型中更新的方式。

您还可以将文本框的 IsEnabled 属性绑定到在检查它是否包含有效值期间设置的 viewmodel 属性。

如果需要,也可以对 IsEnabled 属性使用转换器,并将文本作为命令参数传递,以便转换器检查文本并根据需要设置文本框 IsEnabled 属性的 bool 值。

【讨论】:

  • 这就是问题 - 如果我想验证输入并将其表示为布尔值,它是验证还是对话。假设我的视图上有 10 个文本框和一个按钮 - 如果所有 TB 都持有有效的文件路径,则应启用该按钮 - 如果我的 ViewModel 处理 10 个附加属性“IsFileOneValid”等以“通知”其他控件查看验证状态?
  • 不,您可以使用单个属性对其进行管理。您可以侦听属性或类的属性更改事件,并在事件发生时进行检查,并将布尔值设置为 true 或 false。此 bool 属性由按钮的 IsEnabled 属性绑定。所以即使有一个文本框无效,该属性也会为false,按钮也会失效。
猜你喜欢
  • 2010-12-07
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
  • 2012-10-04
  • 1970-01-01
  • 2017-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多