Автообновление моего взгляда - PullRequest
0 голосов
/ 22 июня 2009

Я создал приложение, которое имеет следующее:

  • База данных, созданная в VS2008, LINQ to SQL
  • Вид базы данных:

XAML часть

<CollectionViewSource x:Key="CustomerView" Source="{Binding Source={x:Static Application.Current}, Path=Customers}" />

C # part

    public IEnumerable<Customer> Customers
    {
        get
        {
            var database = new ApplicationDataContext();
            return from customer in database.Customers
                   select customer ;
        }
    }

В представлении отображаются не только клиенты, но и подтаблицы, такие как Customers.Products (связанная таблица).

Теперь я где-то изменяю свойства Продукта, и я ожидаю, что представление будет автоматически обновляться (потому что я вижу, что таблицы реализуют INotifyPropertyChanged, INotifyPropertyChanging).

Но этого не происходит.

Я мог бы запустить его вручную, но прежде чем начать, я хотел бы знать, должно ли это произойти автоматически. Кто-нибудь? * * 1023

Ответы [ 2 ]

2 голосов
/ 22 июня 2009

Обновление не произойдет, если вы просто выставите клиентов как IEnumerable<Customer>. Вам нужно представить его как коллекцию, которая запускает события при изменении содержимого. Либо представьте его полностью как тип таблицы ваших клиентов (если этот тип вызывает события INotify), либо вам нужно обернуть его в нечто вроде ObservableCollection<>.

0 голосов
/ 22 июня 2009

Я делаю LOB-приложение с WPF + Linq to SQL, и проблема коллекций Linq-To-Sql, не правильно реализующих INotifyCollectionChanged, - это то, над чем мне приходилось работать на каждом аспекте системы.

Лучшее решение, которое я нашел, - это выполнить одно из следующих действий:

  1. Создайте слой модель над вашим классом DataContext, чтобы код GUI взаимодействовал только со слоем модели, а не напрямую с DataContext. В методах бизнес-логики всегда переносите возвращенные коллекции в ObservableCollection

и / или

  1. Реализуйте вторичные свойства коллекции на ваших классах сущностей, так что там, где у вас изначально было Customer.Products , у вас теперь есть * Customer.Products_Observable ", где это новое свойство только для чтения возвращает ObservableCollection, которая упаковывает все, что возвращает Customer.Products.

и / или

  1. Создайте новый класс, производный от ObservableCollection, который поддерживает DataContext. Если вы переопределите методы Add / Insert / Remove такого класса, то любые изменения в коллекции могут автоматически распространяться на вызовы DataContext и InsertOnSubmit / DeleteOnSubmit.

Вот пример такого класса:

Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports System.Linq
Imports System.Data.Linq

Public Class ObservableEntityCollection(Of T As {Class})
    Inherits ObservableCollection(Of T)

    Private _Table As Table(Of T)

    Public Sub New(ByVal Context As DataContext)
        Me._Table = Context.GetTable(Of T)()
    End Sub

    Public Sub New(ByVal Context As DataContext, ByVal items As IEnumerable(Of T))
        MyBase.New(items)
        Me._Table = Context.GetTable(Of T)()
    End Sub

    Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As T)
        _Table.InsertOnSubmit(item)
        MyBase.InsertItem(index, item)
    End Sub

    Public Shadows Sub Add(ByVal item As T)
        _Table.InsertOnSubmit(item)
        MyBase.Add(item)
    End Sub

    Public Shadows Sub Remove(ByVal item As T)
        If MyBase.Remove(item) Then
            _Table.DeleteOnSubmit(item)
        End If
        Dim deletable As IDeletableEntity = TryCast(item, IDeletableEntity)
        If deletable IsNot Nothing Then deletable.OnDelete()
    End Sub

    Protected Overrides Sub RemoveItem(ByVal index As Integer)
        Dim Item As T = Me(index)
        _Table.DeleteOnSubmit(Item)
        MyBase.RemoveItem(index)
    End Sub

End Class


Public Interface IDeletableEntity

    Sub OnDelete()

End Interface

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

Обратите внимание, что классу требуется ссылка DataContext в качестве конструктора, что делает его идеально подходящим для использования в сценарии 1) выше (т. Е. Использование из слоя / класса Model). Если вы хотите реализовать метод 2) [то есть для сущности как свойства], то вы можете дать присоединенным сущностям возможность «найти» их DataContext следующим образом:

[На объекте Class:]

    Public Property Context() As DataContext
        Get
            If _context Is Nothing Then
                _context = DataContextHelper.FindContextFor(Me)
                Debug.Assert(_context IsNot Nothing, "This object has been disconnected from it's DataContext, and cannot perform the requeted operation.")
            End If
            Return _context
        End Get
        Set(ByVal value As DataContext)
            _context = value
        End Set
    End Property
    Private _context As DataContext

[Как служебный класс]:

Public NotInheritable Class DataContextHelper

      Private Const StandardChangeTrackerName As String = "System.Data.Linq.ChangeTracker+StandardChangeTracker"

        Public Shared Function FindContextFor(ByVal this as DataContext, ByVal caller As Object) As JFDataContext
            Dim hasContext As Boolean = False
            Dim myType As Type = caller.GetType()
            Dim propertyChangingField As FieldInfo = myType.GetField("PropertyChangingEvent", BindingFlags.NonPublic Or BindingFlags.Instance)
            Dim propertyChangingDelegate As PropertyChangingEventHandler = propertyChangingField.GetValue(caller)
            Dim delegateType As Type = Nothing

            For Each thisDelegate In propertyChangingDelegate.GetInvocationList()
                delegateType = thisDelegate.Target.GetType()
                If delegateType.FullName.Equals(StandardChangeTrackerName) Then
                    propertyChangingDelegate = thisDelegate

                    Dim targetField = propertyChangingDelegate.Target
                    Dim servicesField As FieldInfo = targetField.GetType().GetField("services", BindingFlags.NonPublic Or BindingFlags.Instance)
                    If servicesField IsNot Nothing Then
                        Dim servicesObject = servicesField.GetValue(targetField)
                        Dim contextField As FieldInfo = servicesObject.GetType.GetField("context", BindingFlags.NonPublic Or BindingFlags.Instance)
                        Return contextField.GetValue(servicesObject)
                    End If
                End If
            Next

            Return Nothing
        End Function

Примечание. Сущность может найти свой DataContext, только если она присоединена к DataContext с включенным ChangeTracking. Вышеуказанный хак (да - это хак!) Опирается на него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...