How WorkSpaces are rendered and saving/adding new Customers:
Before getting in
the the design process lets first look at some WPF XAML principles as a
reminder. The following two diagrams show the basic principle of binding
properties to ContentControls and ItemsControls and sourcing them via
DataTemplate for Visual purposes.
Now
regard the second diagram below, which represents an ItemsControl
It can be seen
immediately that the resemblance is obvious. The only difference is the
ItemsControl class versus ContentControl class and the names of the properties
the derived classes expose. However the point here is that both diagrams show
the basic technique of binding a class property (could be a Dependency Property
or a Collection type Property like ObservableCollection) and a DataTemplate. So
in other words, if we have a container control, be it an ItemsControl (like a
ListBox) or a ContentControl (like HeaderedContentControl or GroupBox) the
principle is the same for filling up the container, namely bind it to a class
property and provide the Template (i.e. how it will look visually) via the
DataTemplate.
Another technique
widely used in WPF design as a principle is to use a UserControl under a
DataTemplate, thus wrapping a whole amount of UI Elements into a UserControl
and then present it under a DataTemplate to the Container (ItemsControl or
ContentControl). This approach is also encouraged, as it provides further loose
coupling modularity especially in MVVM design and also by using a UserControl
as a DataTemplate, you are afforded the full design flow of using Expression Blend to write the XAML for the
DataTemplate.
Rendering Process:
There are two
HeaderedContentControls used in the design. Their contents are bound to
ObservableCollection and ReadOnlyCollection type properties at
MainWindowViewModel class respectively. The template resource (for the second
HeaderedContentControl of the WorkSpaces) is called "WorkspacesTemplate" which is
detailed in the XAML Resource file (MainWindowResources).
The diagram above
shows the basic principle of what the Parser needs and how it acts in terms of
obtaining the object and rendering it on the screen. When an ItemsControl or
ContentControl is involved, the XAML Parser needs a definition of the object
first. This means it needs to see a class object which exposes a Collection
type Property (generally ObservableCollection). Also it needs further
definitions for rendering, if the Collection's items are of Class type objects.
In this example, we see that the Collection called "WorkSpaces" may
contain 2 different class type objects, namely CustomerViewModel and
AllCustomersViewModel (Please take note that we could place those two different
type of classes into the same Collection as both classes are derived from base
(abstract) class WorkspaceViewModel).
In this example, the
path for rendering definition to the XAML Parser engine is provided as Global
DataTemplate definitions at the ResourceDictionary (MainWindowResources.xaml)
as shown below.
<!--
This template applies an AllCustomersView to an instance
of the AllCustomersViewModel class shown in the main window.
-->
<DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
<vw:AllCustomersView />
</DataTemplate>
<!--
This template applies a CustomerView to an instance
of the CustomerViewModel class shown in the main window.
-->
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
This template applies an AllCustomersView to an instance
of the AllCustomersViewModel class shown in the main window.
-->
<DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
<vw:AllCustomersView />
</DataTemplate>
<!--
This template applies a CustomerView to an instance
of the CustomerViewModel class shown in the main window.
-->
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
Here there are two
DataTemplates which wrap 2 UserControls (Views). The XAML Parser will read and
memorise it during the runtime setup (launch) process and associate the
ViewModels to their related Views (i.e. AllCustomerViewModel class object to
AllCustomerView UserControl (class) object. When the user clicks on "View
all Customers" or "Create new Customer" hypertext buttons,
either "CustomerViewModel" or "AllCustomersViewModel" class
objects will be added to the ObservableCollection _workspaces. When this
happens, the XAML Parser knowing the relationship between ViewModel and it's
View (i.e. CustomerViewModel vs CustomerView) will automatically know that it
has to use the View as the template for rendering for this particular
ObservableCollection item. So, if "Create new Customer" is clicked,
then it will render the UserControl assosiated (CustomerView) and use the
CustomerViewModel class as the binding source (depending on the XAML definition
on the UserControl). The diagram below shows how the workspace is styled and
rendered.
<Border
Grid.Column="2"
Style="{StaticResource MainBorderStyle}"
>
<HeaderedContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}"
Header="Workspaces"
Style="{StaticResource MainHCCStyle}"
/>
</Border>
Grid.Column="2"
Style="{StaticResource MainBorderStyle}"
>
<HeaderedContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}"
Header="Workspaces"
Style="{StaticResource MainHCCStyle}"
/>
</Border>
Saving/Adding Customers:
We know that the
UserControl "CustomerView" is linked to "CustomerViewModel"
by the XAML code above Notes
on page "MvvmDemoApp Part3" . So for any commands issued on the
UserControl, the Binding Source object is the CustomerViewModel class, unless
set differently. Below,there is the XAML code for the "Save" button.
<!-- SAVE BUTTON -->
<Button
Grid.Row="8" Grid.Column="2"
Command="{Binding Path=SaveCommand}"
Content="_Save"
HorizontalAlignment="Right"
Margin="4,2"
MinWidth="60"
/>
<Button
Grid.Row="8" Grid.Column="2"
Command="{Binding Path=SaveCommand}"
Content="_Save"
HorizontalAlignment="Right"
Margin="4,2"
MinWidth="60"
/>
The command property
of the Button is tied to the SaveCommand property of Icommand type.
The diagram above
describes visually how the "Save" operation works, when the user
clicks on the "Save" button on the new Customer workspace. During
runtime at the initial setup run of the program (when the program is launched),
the XAML Parser Engine reads all necessary binding information, as defined on
the XAML script. From this reading XAML Parser knows that there is a command
binding between the Save Button on the "CustomerView" UserControl and
the CustomerViewModel (public ICommand SaveCommand). The second binding that
the XAML Parser is aware of is the binding between the ObservableCollection of
"AllCustomersViewModel" and the ListView element on the
"AllCustomersView".
Now, when the
"Save" Button is clicked on the "CustomerView" UC (which is
rendered via DataTemplate on the HeaderedContentControl on the MainView), the
Save method at CustomerViewModel will fire (via the Binding object). This
method will create a new "RelayCommand" object instance, which will
run CanSave validation process (described later in the article). If the
validation passes, RelayCommand will delegate next to the Save method of the
CustomerViewModel. The Save method will call the "CustomerRepository"
object and will add the new customer object to the generic
list<customer>. Then an event will be raised at the CustomerRepository,
which is handled at "AllCustomersViewModel", where the CustomerViewModel
is added into the
"AllCustomers" ObservableCollection. As mentioned above, this
collection is bound to the ListView control at "AllCustomerView",
thus the CustomerViewModel properties (such as email,FirstName etc.) will be
rendered under the ViewList as a new element and displayed (i.e. new customer
is added to the "AllCustomers" workspace).
The next article
will describe 3 processes which take place, which merit to be mentioned. These
processes are:
1-Validation during
the Saving process (CanSave method)
2-How
CustomerRepositoy reads XML file data
3-Validation Process
during new Customer entry.
Finally the last
article of the series will make a summary of the MVVM design we have seen so
far.
No comments:
Post a Comment