Как мне поместить охранное свойство в этот код?

Я изучаю 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?


person asakura89    schedule 09.06.2012    source источник
comment
Кстати, в этом случае вам, вероятно, даже не нужна модель представления. Обычно View должен привязываться к свойствам ViewModel, которые запускают NotifyPropertyChanged и распространяют изменения в модели. Не подразумевая, что вы делаете это неправильно, просто мои несколько центов.   -  person Sandeep Singh Rawat    schedule 10.06.2012
comment
Свойство охраны — это свойство, используемое для охраны события. Это похоже на проверку, когда событие запускается.   -  person asakura89    schedule 10.06.2012


Ответы (4)


Одним из решений является наследование модели представления от PropertyChangedBase и подписка на изменения свойств StudentModel. Затем преобразуйте метод защиты в свойство, например:

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;
        }
    }
}

Надеюсь, что это работает.

person Jone Polvora    schedule 09.06.2012
comment
Но в этом случае я предлагаю вам объединить модель представления и модель. - person Jone Polvora; 10.06.2012
comment
Тогда мой шаблон будет VVM. Есть ли более чистый способ сделать, если я хочу разделить VM и M? - person asakura89; 10.06.2012
comment
В MVVM нет конструктивных ограничений на наличие дочерних моделей представления внутри модели представления. В своем ответе я показал внутренний экземпляр PropertyChangedBase, уведомляющий родительскую модель представления. - person Jone Polvora; 10.06.2012
comment
VIEWMODEL — это модель для VIEW. Думайте о данных, необходимых для подачи визуальных элементов управления/вводимых пользователем данных. МОДЕЛЬ может быть DTO, моделью предметной области или любой структурой данных, к которой модель представления будет подключаться для получения/установки свойств и логики, которые будут отражены в представлении. Модель представления может вызывать службы, которые получают/отправляют данные. - person Jone Polvora; 10.06.2012
comment
Спасибо. Я всегда представляю, что Модель похожа на мой ответ ниже. Ваше здоровье :) - person asakura89; 12.06.2012

Событие уведомления должно присутствовать в view model, потому что оно сообщает об изменениях из model и передает их в пользовательский интерфейс и наоборот. Это соответствует рекомендациям по проектированию MVVM.

В вашем конкретном случае можно или оставить как есть, но убрать ненужные view model, или перенести нотификаторы в view model

person Tigran    schedule 09.06.2012
comment
Модель — это то, что отражает вашу базу данных, или то, что является данными само по себе. - person Tigran; 10.06.2012

Ваши уведомления об изменениях должны быть в модели, поскольку это то, к чему вы тоже привязаны. По сути, ваша модель представления должна загружать список объектов и предоставлять связанную коллекцию для вашего пользовательского интерфейса для большинства вещей. Вышеупомянутая реализация выглядит как подробная панель с одним объектом (например, режим редактирования).

Подвести итог.

ViewModel — должен реализовывать интерфейс INotifyPropertyChangeed. Модель — должен реализовывать интерфейс INotifyPropertyChangeed.

Чтобы включить / отключить команды / кнопки, вы должны использовать привязку команд. У меня есть руководство по WPF и Silverlight, на которое вы можете сослаться.

http://tsells.wordpress.com/2010/06/23/command-binding-with-wpf-and-silverlight-net-4-0-with-mv-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-mv-vm-example/

person tsells    schedule 09.06.2012

Хорошо, у меня есть вывод в моем коде
Вот мои источники вдохновения

@tsells -- спасибо за третью ссылку

@john polvora -- спасибо за указание на свойство
ВАЖНО : оно должно быть общественной собственностью

Предложение Guard не срабатывает
Ошибка в защитной оговорке с 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; }
    }
}
person asakura89    schedule 10.06.2012
comment
Да, вы сделали то, что я рекомендовал (объединил вашу StudentModel внутри StudentViewModel). Вам больше не нужно общедоступное свойство для StudentModel, оно может быть частным. И в представлении вам не нужно объявлять explcit Binding {}... просто измените имена элементов управления, соответствующие общедоступным свойствам модели представления, и Caliburn.Micro создаст привязки. - person Jone Polvora; 10.06.2012
comment
Ага. Я знаю конвенцию. Просто люблю старый подход - person asakura89; 12.06.2012