Reputation: 4107
I have created an application which has the following:
XAML part
<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 ;
}
}
The view show not only the customers, but also subtables like Customers.Products (a linked table).
Now I change the properties of a Product somewhere, and I would expect the View to be automatically updated (because I see that the tables implement INotifyPropertyChanged, INotifyPropertyChanging).
But this does not happen.
I could all trigger it manualy, but before I start doing that I wonder if it should happen automatically. Anyone?
Upvotes: 0
Views: 464
Reputation: 9438
I'm doing an LOB app with WPF + Linq to SQL, and the problem of the Linq-To-Sql collections not correctly implementing INotifyCollectionChanged is something that I 've had to work around on every facet of the system.
The best solution I've found so far is to do any one of the following:
and/or
and/or
Here is an example of such a class:
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
The IDeletable interface allows you to implement specific logic on your entity classes (like cleaning up foreign-keys and deleting child objects).
Notice that the class requires a DataContext reference as a constructor, which makes it ideally suited for use with scenario 1) above (i.e. use from a Model layer/class). If you want to implement it method 2) [ie on the entity as a property], then you can give attached entities the ability to "find" their DataContext as follows:
[On the entity 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
[As a utility class]:
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
Note: An entity can only find it's DataContext if it is attached to a DataContext with ChangeTracking switched on. The above hack (yes - it is a hack!) relies on it.
Upvotes: 0
Reputation: 27509
It wont update if you just expose customers as an IEnumerable<Customer>
. You need to expose it as a collection that triggers events when it's contents change. Either expose it fully as whatever type your Customers table is (if that type raises INotify events), or you need to wrap it up in something like an ObservableCollection<>
.
Upvotes: 2