Which means it wasn't until I started slapping my head up against the datagrid that I first really looked at the Data Context object.
Afterwards, I thoroughly smacked my forehead repeatedly. Imagine it, thousands of lines of code, and a good bit of it can be culled if I had the time or energy to go retrofit it all with Data Contexts.
Now, for the uninitiated, a Data Context is just as it says: an object which holds the data that a WPF form uses as a display source for its UI elements.
So, looking at the eComic application; every CBR file can potentially have a xml fragment/document within it which contains details about that CBR file. This would contain information such as number of pages, page descriptions, artists, writers, series title, issue title, chapter number, volume number, etc. All the important meta data, that's truly unimportant to reading an actual issue of a comic.
So, what we have is this standard kind of programming structure: UI - Business Logic - Data Store. These three things are supposed to work together, while remaining within their own domains and never quite touching. Now, the way I had always done this in the past is that either the UI was aware of the Logic layer or the other way around.
In my mind, they had to be in order to talk to one another. It just made sense.
Enter the joys of WPF.
What happens here is that I have my UI and Data Store as usual. Then I have my business logic, and within it is this Data Context object which holds the data from the data store that I am currently interested in.
Taking eComic, let's keep it simple, and only deal with Series Title and Issue Title.
What happens is that I'll create a class, which exposes properties for each of those elements. I can either have the class populate itself, or populate it elsewhere, that's fundamentally irrelevant to the discussion. All we need is the class itself.
The next step is that when we're building our UI in XAML, we need to utilize the Binding command.
So, if have a Series Title textbox that I want to display in the UI, I'd have this code:
<Textbox Name="SeriesTitle" Margin="5,5,5,5" Text="{Binding SeriesTitle}" />
Now, here's where it gets real fun. All I have to do to get the SeriesTitle from my Data Context to that UI is just assign it to that SeriesTitle's parent container's Data Context. Any changes to the textbox will instantly be promulgated back to the Data Context so I never have to manually check the textbox or any other UI element. All I have to look at is the Data Context. This is accomplished via the PropertyChanged event, which is brought to us courtesy of the INotifyPropertyChanged interface found in the System.ComponentModel Namespace.
So, I'm using the Data Context, and now I have to determine if I need to save changes to the data store based on user input or not.
It is important here that I not waste CPU cycles in saving data that hasn't changed. So, this is where the PropertyChanged event really comes into play.
First off, since we were smart, we implemented access to the PropertyChanged event via a OnPropertyChanged method which takes in the name of the property being changed.
Once the event has been risen, then we would set an IsDirty flag to true. Then, we can check that flag to determine if we need to attempt to save the data or not.
This Data Context example can be viewed here:
1: Public Class eComicDataContext
2: Implements INotifyPropertyChanged
3:
4: Private _isDirty As Boolean
5: Private _series As String
6: Private _issue As String
7:
8: Public Sub New()
9: SeriesTitle = ""
10: IssueTitle = ""
11: IsDirty = False
12: End Sub
13:
14: Public Property SeriesTitle() As String
15: Get
16: Return _series
17: End Get
18: Set(ByVal value As String)
19: _series = value
20: OnPropertyChanged("SeriesTitle")
21: End Set
22: End Property
23:
24: Public Property IssueTitle() As String
25: Get
26: Return _series
27: End Get
28: Set(ByVal value As String)
29: _series = value
30: OnPropertyChanged("IssueTitle")
31: End Set
32: End Property
33:
34: Public Property IsDirty() As Boolean
35: Get
36: Return _isDirty
37: End Get
38: Private Set(ByVal value As Boolean)
39: _isDirty = value
40: End Set
41: End Property
42:
43: Public Sub ResetDirtyBit()
44: Me.IsDirty = False
45: End Sub
46:
47: Public Sub OnPropertyChanged(ByVal propertyName As String)
48: RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
49: IsDirty = True
50: End Sub
51:
52: Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
53: End Class