Реализация INotifyPropertyChanged не работает со свойством навигации Entity Framework 4.1

Используя EF 4.1, я добавил интерфейс INotifyPropertyChanged, чтобы уведомлять мое представление об изменении свойств.

 public class Department : INotifyPropertyChanged
{
    public Department()
    {
        this.Courses = new ObservableCollection<Course>();
    }

    // Primary key
    public int DepartmentID { get; set; }
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            RaisePropertyChanged("Name");
        }
    }

    // Navigation property
    public virtual ObservableCollection<Course> Courses { get; private set; }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class Course : INotifyPropertyChanged...

В сценарии Master Detail у меня есть комбинация поиска для изменения отдела: когда реализован INotifyPropertyChanged, свойство отдела не обновляется, но при удалении реализации INotifyPropertyChanged из класса Department и Course оно обновляется:

XAML

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <DataGrid 
        AutoGenerateColumns="False" 
        EnableRowVirtualization="True" 
        Height="173" 
        HorizontalAlignment="Left" 
        ItemsSource="{Binding CourceViewSource}" 
        x:Name="departmentDataGrid" 
        RowDetailsVisibilityMode="VisibleWhenSelected" 
        VerticalAlignment="Top" 
        Width="347">
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="CourseID" Binding="{Binding Path=CourseID}" 
                            Header="CourseID" Width="SizeToHeader" />
            <DataGridTextColumn x:Name="nameColumn" Binding="{Binding Path=Title}" 
                            Header="Title" Width="SizeToHeader" />
            <DataGridTextColumn x:Name="nameColumnw" Binding="{Binding Path=Department.Name}" 
                            Header="Department" Width="SizeToHeader" />
        </DataGrid.Columns>
    </DataGrid>

        <ComboBox Grid.Row="1"
                  ItemsSource="{Binding DepartmentLookUp}"
                  SelectedItem="{Binding CourceViewSource/Department}"   />


    <Button Grid.Row="2" Content="Save" Click="Button_Click"/>
</Grid>

Код позади ...

private SchoolEntities _context = new SchoolEntities();
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public ICollectionView CourceViewSource { get; private set; }
    public ICollectionView DepartmentLookUp { get; private set; }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _context.Departments.Load();     
        _context.Courses.Load();

        DepartmentLookUp = new ListCollectionView(_context.Departments.Local);
        CourceViewSource= new ListCollectionView(_context.Courses.Local);

        RaisePropertyChanged(() => DepartmentLookUp);
        RaisePropertyChanged(() => CourceViewSource);
    }

...

Я включил образец проблемы сюда.

При выборе Отдела в деталях Отдел в Мастере не обновляется, при изменении Кредитного% на Мастере Кредиты на детали обновляются.!

Теперь изменим SchoolModel.cs, чтобы класс Notify не реализовывал интерфейс INotifyPropertyChanged (открытый класс Notify //: INotifyPropertyChanged):

При выборе Отдела в деталях, Отдел в Главном DO обновляется, при изменении Кредитного% на Мастере Кредиты в детали НЕ обновляются.

Я не понимаю, может быть, чего-то не хватает, чтобы заставить обоих работать?


person Worshound    schedule 20.09.2011    source источник


Ответы (2)


Не знаю C # достаточно хорошо, чтобы комментировать это, но ниже моя реализация INotifyPropertyChanged в VB.NET. Я не проверяю статус обработчика, а просто вызываю событие в любом случае. Никогда не подводил меня.

Public Event PropertyChanged As PropertyChangedEventHandler _
    Implements INotifyPropertyChanged.PropertyChanged

Protected Sub OnPropertyChanged(ByVal info As String)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
person CodeWarrior    schedule 20.09.2011
comment
Привет, танки @CodeWarrior за то, что нашли время, но мой вопрос больше связан с этим вопросом: [ссылка] stackoverflow.com/questions/2790054/ Моя только для WPF и не использует службы RIA - person Worshound; 21.09.2011
comment
Так являются ли ваши сущности сущностями по умолчанию, созданными EF? Если это так, вам не нужно реализовывать INotifyPropertyChanged, поскольку сгенерированные EF сущности (не POCO) реализуют это путем наследования от базового класса сущности. Кроме того, опять же, я не так хорош в C #, но я думаю, что ваши вызовы PropertyChanged должны быть больше похожи на RaisePropertyChanged (DepartmentLookup); - person CodeWarrior; 21.09.2011

Заглянув в код, который вы загрузили, проблема, похоже, заключается в том, что у вас есть обработчик событий в коде позади (CourceViewSource_CurrentChanged), который устанавливает свойство Credits, когда CreditPersentage в DataGrid изменяется, но нет аналога - обработчика событий, который устанавливает CreditPersentage при изменении Credits TextBox.

Если ваше отношение между Credits и CreditPersentage всегда равно 100, то нет смысла иметь два столбца базы данных для обоих полей. Я бы рассмотрел одно из полей как «вычисленное» и «зависимое» от другого и прямо выразил бы это в модели. В базе данных я бы сохранил только одно из значений. Это могло выглядеть так:

public class Course : Notify
{
    //...

    private double _credits; // only one backing field for both properties
    public double Credits
    {
        get { return _credits; }
        set
        {
            _credits = value;
            RaisePropertyChanged(() => Credits);
            RaisePropertyChanged(() => CreditPersentage);
            // notify WPF that also CreditPersentage has changed now
        }
    }

    [NotMapped] // add using System.ComponentModel.DataAnnotations; for this
    public double CreditPersentage
    {
        get { return _credits / 100; }
        set
        {
            _credits = value * 100;
            RaisePropertyChanged(() => Credits);
            // notify WPF that also Credit has changed now
            RaisePropertyChanged(() => CreditPersentage);
        }
    }

    public int DepartmentID { get; set; }

    // also add change notification to Department
    private Department _department;
    public virtual Department Department
    {
        get { return _department; }
        set
        {
            _department = value;
            RaisePropertyChanged(() => Department);
        }
    }
}

Атрибут [NotMapped] гарантирует, что в базе данных нет столбца CreditPersentage. Это свойство просто вычисляется в памяти на основе поля _credits.

Затем вы можете удалить CourceViewSource_CurrentChanged в коде позади. И для своего текстового поля вы можете добавить UpdateSourceTrigger:

<TextBox Grid.Row="4" Grid.Column="1"
         Text="{Binding Path=CourceViewSource/Credits,
                UpdateSourceTrigger=PropertyChanged}" />

Теперь должна быть возможность редактировать процентное значение в DataGrid (текстовое поле обновляется при записи) и значение Credits в текстовом поле (процентный столбец в DataGrid обновляется, когда вы пишете). И когда вы выбираете новую запись в поле со списком, столбец «Отдел» в DataGrid обновляется.

person Slauma    schedule 21.09.2011