【问题标题】:How do I put a guard property in this code?我如何在这段代码中放置一个保护属性?
【发布时间】:2012-06-09 20:12:59
【问题描述】:

我现在正在学习 MVVM。由于我看到很多教程或项目只使用 View 和 ViewModel,我有点困惑。这是我的代码。

型号:

public class StudentModel : PropertyChangedBase
{
    private String _firstName;
    public String FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;
            NotifyOfPropertyChange(() => FirstName);
        }
    }

    private Double _gradePoint;
    public Double GradePoint
    {
        get { return _gradePoint; }
        set
        {
            _gradePoint = value;
            NotifyOfPropertyChange(() => GradePoint);
        }
    }
}

查看:

<UserControl x:Class="MVVMLearningWithCaliburnMicro.Views.StudentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org">
    <Grid Width="525" Height="300" Background="Lavender">
        <DockPanel>
            <TextBlock HorizontalAlignment="Center" Text="Student Data"
                       DockPanel.Dock="Top" FontSize="20" />
            <StackPanel Orientation="Vertical" HorizontalAlignment="Center"
                        VerticalAlignment="Stretch"
                        Margin="0,8" DockPanel.Dock="Top">
                <StackPanel Orientation="Horizontal" Margin="0,5">
                    <TextBlock Text="Name" FontSize="15" Margin="5,0" />
                    <TextBox Name="txtName" Text="{Binding Path=Student.FirstName}" Width="250" />
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0,5">
                    <TextBlock Text="Grade" FontSize="15" Margin="5,0" />
                    <TextBox Name="txtGrade" Text="{Binding Path=Student.GradePoint}" Width="250" />
                </StackPanel>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0,5" HorizontalAlignment="Center"
                        VerticalAlignment="Bottom"
                        DockPanel.Dock="Bottom">
                <Button Name="btnSave" Width="100" Height="40"
                        cal:Message.Attach="SaveStudent">
                    <TextBlock Text="Save" FontSize="15" />
                </Button>
            </StackPanel>
        </DockPanel>
    </Grid>
</UserControl>

视图模型:

public class StudentViewModel
{
    public StudentModel Student { get; set; }

    public void SaveStudent()
    {
        MessageBox.Show(String.Format("Saved: {0} - ({1})", Student.FirstName, Student.GradePoint));
    }

    public StudentViewModel()
    {
        Student = new StudentModel { FirstName = "Tom Johnson", GradePoint = 3.7 };
    }

    private Boolean CanSaveStudent()
    {
        return Student.GradePoint >= 0.0 || Student.GradePoint <= 4.0;;
    }
}

问:
1. 既然NotifyOfPropertyChange() 在模型中,我该如何放置我的保护属性?
2.(愚蠢的问题)我的 MVVM 模式是否指向了正确的方法?

【问题讨论】:

  • 顺便说一句,在这种情况下您可能甚至不需要视图模型。通常 View 应该绑定到 ViewModel 属性,该属性会触发 NotifyPropertyChanged 并将更改传播到模型。并不是说你做错了只是我的几分钱。
  • Guard 属性是用于保护事件的属性。这就像触发事件时的验证。

标签: c# mvvm caliburn.micro


【解决方案1】:

一种解决方案是从 PropertyChangedBase 继承 viewmodel 并订阅 StudentModel 的属性更改。然后将guard方法转换为属性,如下所示:

public class StudentViewModel: PropertyChangedBase
{
    public StudentModel Student { get; set; }

    public void SaveStudent()
    {
        MessageBox.Show(String.Format("Saved: {0} - ({1})", Student.FirstName, Student.GradePoint));
    }

    public StudentViewModel()
    {
        Student = new StudentModel { FirstName = "Tom Johnson", GradePoint = 3.7 };
        Student.PropertyChanged += delegate { NotifyOfPropertyChanged( () => CanSaveStudent)};
    }

    public Boolean CanSaveStudent
    {
        get 
        {
            return Student.GradePoint >= 0.0 || Student.GradePoint <= 4.0;
        }
    }
}

希望它有效。

【讨论】:

  • 但是在这种情况下,我建议你合并视图模型和模型。
  • 那么我的模式就是 VVM。如果我想要单独的 VM 和 M 有没有更清洁的方法?
  • 在 MVVM 中,在视图模型中包含子视图模型没有设计限制。在我的回答中,我展示了通知父 viewModel 的 PropertyChangedBase 的内部实例。
  • VIEWMODEL 是 VIEW 的模型。考虑提供视觉控制/用户输入所需的数据。 MODEL 可以是 DTO,或域模型,或视图模型将挂钩的任何数据结构,用于获取/设置将反映在视图中的属性和逻辑。视图模型可以调用获取/发布数据的服务。
  • 谢谢。我总是想象模型就像我下面的答案。干杯:)
【解决方案2】:

通知事件应该出现在view model 中,因为它会从model 传达更改并将它们推送到 UI,反之亦然。这是根据 MVVM 设计指南。

在您的具体情况下,您可以或保持原样,但删除不必要的view model,或在view model 中移动通知器

【讨论】:

  • 模型是反映您的数据库的东西,或者是本身数据的东西。
【解决方案3】:

您的通知更改应该在模型中,因为这也是您的约束。基本上,您的视图模型应该加载对象列表并为您的 UI 提供一个绑定集合来处理大多数事情。您上面的实现似乎是一个带有单个对象的详细面板(例如编辑模式)。

总结一下。

ViewModel - 应该实现 INotifyPropertyChangeed 接口 模型 - 应该实现 INotifyPropertyChangeed 接口

要启用/禁用命令/按钮,您应该使用命令绑定。我有一个 WPF 和 Silverlight 教程供您参考。

http://tsells.wordpress.com/2010/06/23/command-binding-with-wpf-and-silverlight-net-4-0-with-m-v-vm/

在过去的几年里,我也围绕这件事发布了一些教程。喜欢就去看看吧……

http://tsells.wordpress.com/2011/02/08/using-reflection-with-wpf-and-the-inotifypropertychanged-interface/

http://tsells.wordpress.com/2010/06/02/wpf-model-view-viewmodel-m-v-vm-example/

【讨论】:

    【解决方案4】:

    好的,我的代码中有一个结论
    这是我的灵感

    @tsells -- 感谢第三个链接

    @john polvora -- 感谢您指出该属性
    重要提示:它应该是公共属性

    Guard Clause Not Firing
    Error on guard clause with Caliburn.Micro


    这是我认为更简洁的代码

    型号:

    public class StudentModel
    {
        public String FirstName { get; set; }
        public Double GradePoint { get; set; }
    }
    

    查看:

    <UserControl x:Class="MVVMWithCMTwo.Views.StudentView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                 xmlns:cal="http://www.caliburnproject.org">
        <Grid Width="525" Height="300" Background="Lavender">
            <DockPanel>
                <TextBlock HorizontalAlignment="Center" Text="Student Data"
                           DockPanel.Dock="Top" FontSize="20" />
                <StackPanel Orientation="Vertical" HorizontalAlignment="Center"
                            VerticalAlignment="Stretch"
                            Margin="0,8" DockPanel.Dock="Top">
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <TextBlock Text="Name" FontSize="15" Margin="5,0" />
                        <TextBox Name="txtName" Text="{Binding Path=StudentName}" Width="250" />
                    </StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="0,5">
                        <TextBlock Text="Grade" FontSize="15" Margin="5,0" />
                        <TextBox Name="txtGrade" Text="{Binding Path=StudentGrade}" Width="250" />
                    </StackPanel>
                </StackPanel>
                <StackPanel Orientation="Horizontal" Margin="0,5" HorizontalAlignment="Center"
                            VerticalAlignment="Bottom"
                            DockPanel.Dock="Bottom">
                    <Button Name="btnSave" Width="100" Height="40"
                            cal:Message.Attach="SaveStudent">
                        <TextBlock Text="Save" FontSize="15" />
                    </Button>
                </StackPanel>
            </DockPanel>
        </Grid>
    </UserControl>
    

    视图模型:

    public class StudentViewModel : PropertyChangedBase
    {
        public StudentModel Student { get; set; }
    
        public String StudentName
        {
            get { return Student.FirstName; }
            set
            {
                Student.FirstName = value;
                NotifyOfPropertyChange(() => Student.FirstName);
            }
        }
    
        public Double StudentGrade
        {
            get { return Student.GradePoint; }
            set
            {
                Student.GradePoint = value;
                NotifyOfPropertyChange(() => Student.GradePoint);
                NotifyOfPropertyChange(() => CanSaveStudent);
            }
        }
    
        public void SaveStudent()
        {
            MessageBox.Show(String.Format("Saved: {0} - ({1})", Student.FirstName, Student.GradePoint));
        }
    
        public StudentViewModel()
        {
            Student = new StudentModel { FirstName = "Tom Johnson", GradePoint = 3.7 };
        }
    
        public Boolean CanSaveStudent
        {
            get { return Student.GradePoint >= 0.0 && Student.GradePoint <= 4.0; }
        }
    }
    

    【讨论】:

    • 是的,您按照我的建议做了(将您的 StudentModel 合并到 StudentViewModel 中)。您不再需要 StudentModel 的公共属性,它可以是私有的。在视图中,您不需要声明显式绑定 {} ...只需更改与 viewmodel 的公共属性匹配的控件名称,Caliburn.Micro 将创建绑定。
    • 是的。我知道公约。只是喜欢旧方法
    猜你喜欢
    • 2020-08-31
    • 2016-10-09
    • 2011-05-25
    • 1970-01-01
    • 2016-07-09
    • 2018-08-03
    • 2014-10-22
    • 2015-01-04
    • 2012-03-07
    相关资源
    最近更新 更多