Preface
This article provides an in-depth reference of how to build performance-oriented applications in WPF for both Windows and the Web. This article talks about all
major aspects with examples, including design pattern trade-offs, multi-threading, WCF Service calls (client server architecture or /smart Client
Model), managing memory, parallelism, etc., which are a must to start building an
enterprise class application from scratch.
Contents
-
Overview.
-
Introduction.
-
WPF/E or
Silverlight
-
Factors
need to considered for enterprise class application with WPF/Silverlight
-
WPF
Programming Model.
-
Initial
Architecture.
-
KSmart
pattern for WPF.
-
Model
Layer implementation.
-
Advance
Memory Management.
-
Caching
objects.
-
Weak
References.
-
Garbage
Collection.
-
Unmanaged
Objects.
-
Asynchronous Programming.
-
Dispatcher.
-
Using the
Dispatcher.
-
Updating
the UI.
-
Updating
the UI Asynchronously.
-
2.
Background Worker.
-
Using
Background Worker in WPF.
-
Binding.
-
Direction
of Data Flow.
-
What
Triggers Source Updates.
-
Page and
Navigation.
-
Refreshing the current Page.
-
Navigation Lifetime.
-
Programming Guidelines for Improving Performance.
-
Conclusion.
Overview
This article provides in-depth reference on how to build WPF applications which
is targeted for high performance and still will work similar to Silverlight or
any other third party plug-ins for browser.
This article also targets on the common mistake developer or architects
do in choosing design patterns, implementing multi-threading or making WCF
Service calls in building smart client applications. This also discuss key
design issues which end in large memory consumptions, decreasing application
performance, resolution issue, Managing user sessions, Parallelism etc.
This concept applies to WPF, WPF running in client (XBAP), Silverlight, all
applications which sticks onto the concept of WPF.
Introduction
Today large numbers of developers are switching to WPF, XBAP or Silverlight due
to level of flexibility it offers in building applications. Now Microsoft has
stopped its updates on the windows control from .NET 4.0 and is focusing
completely on to WPF. This helps to build Jazzy applications which current
industry needs. Microsoft has added numerous enhancements to WPF 4.5 or
Silverlight 5, which helps to build enterprise class applications seamlessly on
Windows and Web, which can be deployed over windows/Web with little or no
change.
A large number of cool features with WPF/Silverlight, from which developers can
easily debug UI, create more stable applications which target both Windows/Web.
It becomes extremely important for the designer to understand the framework,
memory, parallelism and other aspects to build advanced applications with WPF/E
or Silverlight.
WPF/E or
Silverlight
The WPF/E refers to Windows presentation foundation – Everywhere and also
Silverlight are the next generation UI development platforms for the .NET
developers. Microsoft is unifying the development tools which makes possible to
build apps, with these tools which targets desktop, laptops, touch-pads, mobile,
all kind of devices with same kind of UI with WPF.
In few years most of the applications build on windows OS targeted to both web
and windows will be with WPF and Silverlight. The WPF being extremely rich in
UI, with new style of development can become extremely complex and pose several
issues to designers and developers including memory leakage, large performance
pitfalls, and huge memory consumptions.
In today’s world as many users utilizing network bandwidth in data basis, it
becomes extremely important for the developers and designer to concentrate more
on lower bandwidth consumption. If not given proper importance this can become
trivial issue and can affect the whole network and make applications unusable in
the enterprise.
Factors need
to considered for enterprise class application with WPF/Silverlight
There are various factors that need to consider while building enterprise class
applications in WPF. It’s very important to understand WPF architecture at the
initial stage on how WPF application runs in the windows operating system.
WPF Programming Model
WPF programming happens in the managed code. The WPF becomes more robust,
stabilized when running through the managed code as the CLR provides number of
features that makes development productive (including memory management, CTS
etc) which comes with a cost.
The WPF architecture is as illustrate below:
The red sections of the diagram are the code portion of the WPF which includes
Presentation Framework, Presentation Core, and milcore. These are the code
portion of the WPF. There is one unmanaged component milcore. This is directly coupled with DirectX
Engine; all display happening through WPF is through DirectX engine which make
effective hardware and software rendering and make the application looks Jazzy.
Initial Architecture
The architecture consideration for the WPF is extremely important in building
WPF/Silverlight applications, this determines the performance, it’s possible to
build applications which are highly robust than web applications, consumes less
bandwidth compared to ASP.NET, low
memory intensive and can take advantage of hardware acceleration to deliver high
quality graphics through the web. The XBAP application running in full trust can
run outside the browser sandbox and can take the advantage of hardware which is
impossible in ASP.NET or any other web technologies offers without plug-ins
which need to be separately installed on the client.
Since .NET framework is shipped with windows operating systems with windows
server 2003 hosting .NET framework 1.1, Vista hosting .NET 3.0, Windows 7 with
.NET 3.5, Windows 8 and server 2008 hosting .NET 4.0, it is possible that all
applications demand .NET framework as it remain only model for building next
generation applications in Windows.
Windows 8 supporting metro-style applications, the application becomes very
powerful and capable of providing high end graphics, high quality UI, which can
be run on variety of platform like Windows, web, touch pads, laptops, mobiles ,
all with same or little code change when building applications on WPF, which
becomes ideal platform for all developers.
Considering the initial architecture, WPF provides various programming model
like MVVM (Model View View Model) being the most famous one, MEF (Manage
extensibility framework), PRISM which embeds MVVM design, MVP etc.
The MVVM is coined based on various other design patterns of its predecessor
like MVP (Model view presenter), MVC (Model View Controller). The MVC is a
design pattern which is coined on Ruby on Rails development which became
extremely popular and has been adapted extensively in ASP.NET applications which
provides new platform for the development.
Note: The main point what I want to highlight here is these design pattern,
though several website indicates these are the design patterns which provides
enormous benefits while building enterprise class application, most of the
designers or developers intend to follow the pattern as most WPF applications
falling under MVVM or PRISM and few built on MVC, MVP or MEF frameworks.
The detail descriptions of these architecture are beyond the scope of this
article, I am trying to say based on my experience in developing more than 20
Application across 6 companies with around 15+ clients, these architectures
makes extremely cumbersome for the development and leads to various flaw in the
development and can lead to various problems which expects only expert
developers to work for implementation, implementing unwanted commands, makes
cumbersome to pass events argument to the function, decreasing performance,
increasing memory consumption and also increase in network bandwidth.
However, I am not pointing against this, but will be required only in certain
kind of development and should not be usual choice for developers and designers.
These add lots of additional complexity in development more than resolving
issues and also make application unusable in the production.
The MVVM has the following architecture:
The User general Choice for developers will be using is PRISM design patterns
which has MVVM in-built for application development. This architecture has View
Layer which is usually a presentation layer or UI, The PRISM or MVVM design
pattern indicates there will be no .cs file for XAML.
This can become more cumbersome for the developers as there will be no .xaml.cs
file and all the events are to be pushed to View Model as methods, and also for
the events which requires complex parameters it becomes extremely difficult for
the developers especially who are switching from ASP.NET and doesn’t bring any
additional benefits, this can be helpful when UI developers develop in
Expression Blend and use in .NET by developers. This however adds additional
complexity and majority of the project involves developers to be involved in UI
development and can become extremely cumbersome for those.
It’s possible to attach ICommand objects than delegate commands and can be done
without PRISM or MVVM.
The MVVM architecture expects all calls from model to service in Async patterns,
this may look beneficial at the outer look, but this raises lot of overhead and
complexity in application development, Since the service calls are async it’s
possible to trigger multiple service calls at one shot, but results are to be
captured in Completed event. (This supports only CallBack functionality on
remote service with normal configurations).
For example, consider you were filling employee history in the form from
database which has multiple service calls, till all the service call happens the
UI should be blocked, in this case it very difficult to manage in async
programming in CallBack fashion.
The Calculator service demonstrated below represents the scenario:
public void Add()
{
MathClient.AddAsync();
}
Public void Sub()
{
MathClient.SubAsync();
}
public void Add_Completed(object sender, EventArgs e)
{
}
Public void Sub_Completed(object sender, EventArgs e)
{
}Colourised in 6ms
In the above code which returns the result first and which returns result last,
the developers will not be able to predict, it will be hard to block the UI
thread till all results are returned. Hard to synchronize all items. It will be
very difficult to dispose service. The services have the inner Channel which is
unmanaged and need to be dispose or else memory consumption will be high. There
are many instance we see where Silverlight or WPF application hangs the UI,
which makes the application very unfeasible at the production.
The service call has to be made in synchronous fashion to avoid all these
issues.
Since almost all the code have delegates and events, it consumes more memory,
long processing time, and since everything is asynchronous here it can lead to
instability in the code, which has to be addressed.
There are chances that same service is called many times, causing unnecessary
roundtrip to the server, may also impact on the bandwidth, where you see only
data-transfer consumes more memory than web applications, the performance will
also have an impact, causing long time to update UI.
KSmart pattern for WPF
Here by I present my custom design pattern which solves most of the problems
incurred in WPF development, by making application development easier, keeping
low memory, increased stability, increased performance, lesser network
bandwidth.
The detail of the KSmart pattern will be presented separately in-detail in
another code project article, which contains comparisons of various design
patterns with KSmart pattern.
The Ksmart pattern has view, which generally
contains presentation logic for the UI.
The controller can be .xaml.cs or can be
viewmodel or combination of both, this contains code for binding data, handling
events etc. All processing logic will be done by business
layer and layer can be shared across different controllers.
Model contains code for making service calls,
disposing service after its use.
The service contains WCF Services for model
calls.
Business layer at the service handles all the
business logic at server side.
The DAL will make call to the Database and return
result to business Layer.
The service interface and data contract project
will be share across all layers and will have flexibility to type cast to
service, or service disposal in another layer and also data is share across
through data contracts in the application.
This design pattern provides greater flexibility in implementing effective code
for WPF/E or Silverlight.
Model Layer implementation
Even though the model layer provides service interface to WCF client, this will
be the key component in improving performance, providing better memory
management, reducing network bandwidth.
Let see how model layer improves overall performance of the application. The
model layer make calls to WCF layer, it’s possible than model layer can make
async calls for set of operations in WCF, whose results are required parallel,
the model layer can wait till all of it returns result thus enhancing
performance of the application and still returns result in synchronous style to
the business layer.
Its responsible for better memory management, the memory has to release as it
make use of unmanaged channel for communication, once result is returned, the
service should be disposed.
public class CustomerModel
{
public int Add()
{
using (CalcService service = new CalcService())
{
return service.Add();
}
}
public int Sub()
{
using (CalcService service = new CalcService())
{
return service.Sub();
}
}
}Colourised in 6ms
In the above code the service is created, the call to service is made, and
service is disposed. This kind of code provides large memory reductions, make
WPF/Silverlight applications healthier and perform better, which is must in WCF.
CalcService is wrapper, which interns takes care of opening channel and
returning service.
This is very critical to application performance, better
memory management and reduces network bandwidth. As compared to:
public void Add()
{
MathClient.AddAsync();
}
Public void Sub()
{
MathClient.SubAsync();
}
public void Add_Completed(object sender, EventArgs e)
{
}
Public void Sub_Completed(object sender, EventArgs e)
{
}Colourised in 4ms
Here, it very hard to dispose objects, hard to maintain
synchronization among objects consumes more memory, bandwidth and hinders
performance as everything is based on delegates and events.
Advance Memory Management
Caching objects
Keep all the objects like view Model objects, Model objects, business objects,
static objects etc.. in a centralized Cache. Disposing centralized cache can
promote greater memory reductions.
Microsoft provides built-in Cache in .NET runtime, but I recommend strongly not
to use it, because if there are exceptions cache is invalidated automatically.
Beside it will be hard time to remove all the cache items during session
timeout, applications sign-out, reloading new controls etc. The Cache has to be
invalidated during all these times.
The custom light weight, application specific cache looks like below:
public class ApplicationCache
{
Dictionary<string, object> CentralCache = new Dictionary<string, object>();
public void Add(string key, object Value)
{
if (!CentralCache.ContainsKey(key))
{
CentralCache.Add(key, Value);
}
}
public object GetItem(string key)
{
if (CentralCache.ContainsKey(key))
{
return CentralCache[key];
}
return null;
}
static ApplicationCache cache = null;
public static ApplicationCache CreateInstance()
{
if(cache == null)
{
return new ApplicationCache();
}
return cache;
}
}Colourised in 9ms
In this, assigning null to cache will invalidate the whole cache, and programmer
has more control on cache and will not be invalidated automatically during
exceptions.
Use this Cache similar to WeakReference. Like keeping objects in weak reference,
maintain the object in this cache, which provide more benefits,
Weak Reference
Avoid keeping objects in WeakReference as at any time the object may be removed
by garbage collection and reference will be destroyed automatically.
Garbage Collection
It’s very important to understand, how garbage collection works, to build high
performance, low memory intensive applications, which stands good after
production.
All managed objects in .NET will be held in Managed Heap, there will be no
information on what the size of managed heap is or when the garbage collection
is triggered.
It’s always good to trigger garbage collection at regular intervals or an
actions like Changing the Navigation, application Sign-out, Session timeout etc.
Garbage collection happens on two types of objects:
Strong reference objects, by default all objects
are of strong reference, the Garbage Collector requires the objects to be
explicitly marked for collection.
ApplicationCache cache = new ApplicationCache();
object item = cache.GetItem("itemKey");
cache = null;
GC.Collect();Colourised in 1ms
WeakReference objects, which are automatically picked up by the garbage
collector. These objects need not to be marked explicitly and these are short
lived objects.
class Program
{
static void Main(string[] args)
{
WeakReference refe = new WeakReference(new Test());
GC.Collect();
Console.WriteLine(refe.IsAlive);
Console.Read();
}
}Colourised in 5ms
In the above code the
refe.IsAlive
prints
False, as the object is wiped by the Garbage
Collector.
One has to see the proper trade-off between them before implementing a solution.
Unmanaged objects
The objects which performs the operation and doesn’t contains managed code, like
FileStream objects, DataSet, DataTable, ComObjects, WCF service objects, Channel objects, SQL connection
objects, Command objects, windows GDI objects etc. requires manually Dispose
those objects for memory cleanups.
It’s important to understand that these unmanaged objects consume large amount
of memory and ignoring to dispose these objects can result in large memory
leakage in the production, user can experience these while development also.
All the above mentioned objects, except ComObjects has Dipose()
interface, which
can be called after its use. The
using
block can be used and recommended for this kind of scenario. The following code
demonstrates the same:
class Program
{
static void Main(string[] args)
{
DataSet ds = null;
using (SqlConnection conn = new SqlConnection())
{
using (SqlCommand comm = new SqlCommand())
{
}
ds.Dispose();
}
}
}Colourised in 8ms
In the above code all the unmanaged code like connection, command are embedded
in using block and disposed automatically after going out of the scope.
The ds of type
DataSet is disposed by calling dispose interface.
The COM objects can be disposed as shown below:
while (Marshal.ReleaseComObject(comObject) > 0){}Colourised in 0ms
The COM objects are wrapped inside Callable Wrapper. The Runtime Callable
Wrapper has reference count every time COM interface pointed to it. The
ReleaseComObject method decrements
the reference count of runtime callable wrapper, if there are more than one
reference then this method decrements and return the number of remaining
references.
To ensure runtime callable wrapper and the original COM objects are released,
the loop should be constructed until the return reference count is a zero.
Asynchronous Programming
Asynchronous programming has both Advantages and Disadvantages. However we can
convert disadvantages to advantages if we properly use them, otherwise this can
become more cumbersome to program or manage, cause instability in the
application, take longer processing time, can consume more bandwidth and make
application almost not usable in the production.
The Calculator service demonstrated below represents the scenario:
public void Add()
{
CalcModel.Add();
}
Public void Sub()
{
CalcModel.Sub();
}
public void Add_Completed(object sender, EventArgs e)
{
}
Public void Sub_Completed(object sender, EventArgs e)
{
}Colourised in 5ms
The call is made as below:
CalcViewModel.Add();
CalcViewModel.Sub();Colourised in 0ms
In the above code which returns the result first and which returns result last,
the developers will not be able to predict, it will be hard to block the UI
thread till all results are returned. Hard to synchronize all items. It will be
very difficult to dispose service. The services have the inner Channel which is
unmanaged and need to be dispose or else memory consumption will be high. There
are many instance we see where Silverlight or WPF application hangs the UI,
which makes the application very unfeasible at the production.
The same implementation with
Async
and
Await keywords, available in .NET 4.5, or Async CTP for 2010.
public int Task<int> Add(int a, int b)
{
return CalcModel.Add(a, b);
}
public int Task<int>Sub(int a, int b)
{
return CalcModel.Sub(a, b);
}Colourised in 3ms
The call made as below:
int res = await CalcViewModel.Add();
int res1 = await CalcViewModel.Sub();Colourised in 0ms
This bridges the gap between asynchronous and synchronous programming and
returns the result in efficient fashion, and also addresses all the problems
stated above.
Dispatcher
WPF run on multi-threaded apartment, since there are multi-threads running it’s
very easy to get into situation of deadlocks, concurrency management is trivial
of WPF applications. Most objects in WPF derive from
DispatcherObject. WPF is based on messaging system implemented by the
Dispatcher. This is very similar to
Win32 message pumps.
There are two main things with respect to concurrency in WPF.
- Dispatcher
- Thread Affinity
The Dispatcher
class is used to perform work on his attached thread. It has a
queue of work items and it is in charge of executing the work items on the
dispatcher thread.
Almost every WPF element has thread affinity. This means that access to such an
element should be made only from the thread that created the element. In order
to do so, every element that requires thread affinity is derived, eventually,
from DispatcherObject
class. This class provides a property named Dispatcher
that returns the Dispatcher
object associated with the WPF element.
If you want to make Async calls you should only make using:
Dispatcher.CurrentDispatcher.BeginInvoke(
....
);Colourised in 0ms
i.e. only through dispatcher otherwise get runtime errors, that thread Id is
different from thread which updated it.
Dispatchers object hierarchy:
In the above we can see all objects are derived from dispatcher object and
handles concurrency in more effective way.
Using the Dispatcher
The Dispatcher
class provides a gateway to the message pump in WPF and provides
a mechanism to route work for processing by the UI thread. This is necessary to
meet the thread affinity demands, but since the UI thread is blocked for each
piece of work routed through the Dispatcher, it is important to keep the work
that the Dispatcher does small and quick. It is better to break apart larger
pieces of work for the user interface into small discrete blocks for the
Dispatcher to execute. Any work that doesn't need to be done on the UI thread
should instead be moved off onto other threads for processing in the background.
Typically you will use the Dispatcher
class to send worker items to the UI
thread for processing. For example, if you want to do some work on a separate
thread using the Thread class, you could create a ThreadStart
delegate with some
work to do on a new thread as shown below.
Updating UI with Non-UI Thread—The Wrong Way
ThreadStart start = delegate()
{
statusText.Text = "From Other Thread";
};
new Thread(start).Start();Colourised in 5ms
This code fails because setting the Text property of the statusText
control (a
TextBlock
) is not being called on the UI thread. When the code attempts to set
the Text on the TextBlock
, the TextBlock
class internally calls its
VerifyAccess
method to ensure that the call is coming from the UI thread. When it determines
the call is from a different thread, it throws an exception. So how can you use
the Dispatcher to make the call on the UI thread?
The Dispatcher
class provides access to invoke code on the UI thread directly.
The below shows the use of the Dispatcher's Invoke method to call a method
called SetStatus
to change the TextBlock
's
Text
property for you.
Updating the UI
ThreadStart start = delegate()
{
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<string>(SetStatus),
"From Other Thread");
};
new Thread(start).Start();Colourised in 4ms
The Invoke
call takes three pieces of information: the priority of the item to
be executed, a delegate that describes what work to perform, and any parameters
to pass into the delegate described in the second parameter. By calling Invoke,
it queues up the delegate to be called on the UI thread. Using the Invoke method
ensures that you are going to block until the work is performed on the UI
thread.
As an alternative to using the Dispatcher synchronously, you can use the
BeginInvoke
method of the Dispatcher to asynchronously queue up a work item for
the UI thread. Calling the BeginInvoke
method returns an instance of the
DispatcherOperation
class that contains information about the execution of the
work item, including the current status of the work item and the result of the
execution (once the work item has completed). The use of the BeginInvoke
method
and the DispatcherOperation
class is shown below.
Updating the UI Asynchronously
ThreadStart start = delegate()
{
DispatcherOperation op = Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action<string>(SetStatus),
"From Other Thread (Async)");
DispatcherOperationStatus status = op.Status;
while (status != DispatcherOperationStatus.Completed)
{
status = op.Wait(TimeSpan.FromMilliseconds(1000));
if (status == DispatcherOperationStatus.Aborted)
{
}
}
};
new Thread(start).Start();Colourised in 9ms
Background Worker
There are many instances, where developer encounters taking long time to update
UI, for example moving from login screen to application or clicking on menu
opens the particular module or screen. It takes long time if amount of data to
be displayed to be huge or have complex graphics to be displayed.
In WPF, it’s very easy to handle these components through Background worker, for
better UI responsiveness and fast loading on the screen so that user doesn’t see
the delay.
Now that you have a sense of how the Dispatcher works, you might be surprised to
know that you will not find use for it in most cases. In Windows Forms 2.0,
Microsoft introduced a class for non-UI thread handling to simplify the
development model for user interface developers. This class is called the
BackgroundWorker
. The below shows typical usage of the
BackgroundWorker
class.
Using a BackgroundWorker in WPF
BackgroundWorker _backgroundWorker = new BackgroundWorker();
...
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted +=
_backgroundWorker_RunWorkerCompleted;
_backgroundWorker.RunWorkerAsync(5000);
...
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
}
void _backgroundWorker_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}Colourised in 8ms
The BackgroundWorker
component works well with WPF because underneath the covers
it uses the AsyncOperationManager
class, which in turn uses the
SynchronizationContext
class to deal with synchronization. In Windows Forms, the
AsyncOperationManager
hands off a WindowsFormsSynchronizationContext
class that
derives from the SynchronizationContext
class. Likewise, in ASP.NET it works
with a different derivation of SynchronizationContext
called
AspNetSynchronizationContext
. These SynchronizationContext
-derived classes know
how to handle the cross-thread synchronization of method invocation.
In WPF, this model is extended with a DispatcherSynchronizationContext
class. By
using BackgroundWorker
, the Dispatcher is being employed automatically to invoke
cross-thread method calls. The good news is that since you are probably already
familiar with this common pattern, you can continue using BackgroundWorker
in
your new WPF projects.
Binding
In WPF, as being one of the rich clients has huge graphics to display and takes
more, since today ppl. Are using advance systems it’s not major concern. But if
we attach manually all the data and update UI, will be most critical factor and
will have large impact on the performance, this can also be one of the major
factor for WPF. One should note that in WPF almost all the UI stuffs has to be
updated on the binding for better performance.
This section contains the following subsections.
Typically, each binding has these four components: a binding target object, a
target property, a binding source, and a path to the value in the binding source
to use. For example, if you want to bind the content of a
TextBox
to the Name property of an Employee object, your target object is the
TextBox,
the target property is the
Text
property, the value to use is Name, and the source object is the Employee
object.
The target property must be a dependency property. Most
UIElement properties are dependency properties and most dependency
properties, except read-only ones, support data binding by default. (Only
DependencyObject types can define dependency properties and all
UIElements derive from
DependencyObject.)
Although not specified in the figure, it should be noted that the binding source
object is not restricted to being a custom CLR object. WPF data binding supports
data in the form of CLR objects and XML. To provide some examples, your binding
source may be a
UIElement, any list object, a CLR object that is associated with
ADO.NET data or Web Services, or an XmlNode that contains your XML data. For
more information, see
Binding
Sources Overview.
As you read through other software development kit (SDK) topics, it is important
to remember that when you are establishing a binding, you are binding a binding
target to a binding source. For example, if you are displaying some underlying
XML data in a
ListBox
using data binding, you are binding your
ListBox
to the XML data.
To establish a binding, you use the
Binding
object. The rest of this topic discusses many of the concepts associated with
and some of the properties and usage of the
Binding
object.
Direction of the Data Flow
As mentioned previously and as indicated by the arrow in the figure above, the
data flow of a binding can go from the binding target to the binding source (for
example, the source value changes when a user edits the value of a
TextBox)
and/or from the binding source to the binding target (for example, your
TextBox
content gets updated with changes in the binding source) if the binding source
provides the proper notifications.
You may want your application to enable users to change the data and propagate
it back to the source object. Or you may not want to enable users to update the
source data. You can control this by setting the
Mode
property of your
Binding
object. The following figure illustrates the different types of data flow:
OneWay
binding causes changes to the source property to automatically update the target
property, but changes to the target property are not propagated back to the
source property. This type of binding is appropriate if the control being bound
is implicitly read-only. For instance, you may bind to a source such as a stock
ticker or perhaps your target property has no control interface provided for
making changes, such as a data-bound background color of a table. If there is no
need to monitor the changes of the target property, using the
OneWay
binding mode avoids the overhead of the
TwoWay
binding mode.
TwoWay
binding causes changes to either the source property or the target property to
automatically update the other. This type of binding is appropriate for editable
forms or other fully-interactive UI scenarios. Most properties default to
OneWay
binding, but some dependency properties (typically properties of user-editable
controls such as the
Text
property of
TextBox
and the
IsChecked property of
CheckBox)
default to
TwoWay
binding. A programmatic way to determine whether a dependency property binds
one-way or two-way by default is to get the property metadata of the property
using
GetMetadata and then check the Boolean value of the
BindsTwoWayByDefault property.
OneWayToSource is the reverse of
OneWay
binding; it updates the source property when the target property changes. One
example scenario is if you only need to re-evaluate the source value from the
UI.
Not illustrated in the figure is
OneTime
binding, which causes the source property to initialize the target property, but
subsequent changes do not propagate. This means that if the data context
undergoes a change or the object in the data context changes, then the change is
not reflected in the target property. This type of binding is appropriate if you
are using data where either a snapshot of the current state is appropriate to
use or the data is truly static. This type of binding is also useful if you want
to initialize your target property with some value from a source property and
the data context is not known in advance. This is essentially a simpler form of
OneWay
binding that provides better performance in cases where the source value does
not change.
Note that to detect source changes (applicable to
OneWay
and
TwoWay
bindings), the source must implement a suitable property change notification
mechanism such as
INotifyPropertyChanged. See
How to:
Implement Property Change Notification for an example of an
INotifyPropertyChanged implementation.
The
Mode
property page provides more information about binding modes and an example of
how to specify the direction of a binding.
What Triggers Source Updates
Bindings that are
TwoWay
or
OneWayToSource listen for changes in the target property and
propagate them back to the source. This is known as updating the source. For
example, you may edit the text of a TextBox to change the underlying source
value. As described in the last section, the direction of the data flow is
determined by the value of the
Mode
property of the binding.
However, does your source value get updated while you are editing the text or
after you finish editing the text and point your mouse away from the TextBox?
The
UpdateSourceTrigger property of the binding determines what triggers
the update of the source. The dots of the right arrows in the following figure
illustrate the role of the
UpdateSourceTrigger property:
If the
UpdateSourceTrigger value is
PropertyChanged, then the value pointed to by the right arrow of
TwoWay
or the
OneWayToSource bindings gets updated as soon as the target property
changes. However, if the
UpdateSourceTrigger value is
LostFocus, then that value only gets updated with the new value when
the target property loses focus.
Similar to the
Mode
property, different dependency properties have different default
UpdateSourceTrigger values. The default value for most dependency
properties is
PropertyChanged, while the
Text
property has a default value of
LostFocus. This means that source updates usually happen whenever the
target property changes, which is fine for
CheckBoxes
and other simple controls. However, for text fields, updating after every
keystroke can diminish performance and it denies the user the usual opportunity
to backspace and fix typing errors before committing to the new value. That is
why the
Text
property has a default value of
LostFocus instead of
PropertyChanged.
See the
UpdateSourceTrigger property page for information about how to find
the default
UpdateSourceTrigger value of a dependency property.
The following table provides an example scenario for each
UpdateSourceTrigger value using the
TextBox
as an example:
UpdateSourceTrigger value
|
When the Source Value Gets Updated
|
Example Scenario for TextBox
|
LostFocus (default for
TextBox.Text)
|
When the TextBox control loses focus
|
A
TextBox
that is associated with validation logic (see Data Validation section)
|
PropertyChanged
|
As you type into the
TextBox
|
TextBox
controls in a chat room window
|
Explicit
|
When the application calls
UpdateSource
|
TextBox
controls in an editable form (updates the source values only when the user
clicks the submit button)
|
Page and Navigation
This is one of the amazing features in WPF; this is the major component, which helps in reducing memory, increasing application speed, reduces network
bandwidth and be a critical component to success of any application while in production.
There are lot of cool features in web page development like Stateless, low
memory consumption as only one page is loaded in the application at a time, the
content will be static content in HTML format with JS scripts, this are the
features which makes the web technologies like ASP.NET as the obvious choice for internet or
intranet based application.
Microsoft has provided the same features in WPF and user will have the option to
do the same with Page, Navigation and other components and achieve all those
benefits and make the ideal choice for both web and windows development.
Windows Presentation Foundation (WPF) supports browser-style navigation that can
be used in two types of applications: standalone applications and XAML browser
applications (XBAPs). To package content for navigation, WPF provides the
Page
class. You can navigate from one
Page
to another declaratively, by using a Hyperlink,
or programmatically, by using the
NavigationService. WPF uses the journal to remember pages that have
been navigated from and to navigate back to them.
Page,
Hyperlink,
NavigationService, and the journal form the core of the navigation
support offered by WPF. This overview explores these features in detail before
covering advanced navigation support that includes navigation to lose Extensible
Application Mark-up Language (XAML) files, HTML files, and objects.
In WPF, you can navigate to several content types that include .NET Framework
objects, custom objects, enumeration values, user controls, XAML files, and HTML
files. However, you'll find that the most common and convenient way to package
content is by using
Page.
Furthermore,
Page
implements navigation-specific features to enhance their appearance and simplify
development.
Using
Page,
you can declaratively implement a navigable page of XAML content by using
mark-up like the following.
XAML
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />Colourised in 0ms
A
Page
that is implemented in XAML markup has Page
as its root element and requires the WPF XML namespace declaration. The
Page
element contains the content that you
want to navigate to and display. You add content by setting the Page.Content
property element, as shown in the following markup.
XAML
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Page.Content>
-->
Hello, Page!
</Page.Content>
</Page>Colourised in 2ms
Page.Content
can only contain one child element; in the preceding example, the
content is a single string, "Hello, Page!" In practice, you will usually use a
layout control as the child element (see
Layout
System) to contain and compose your content.
The child elements of a Page
element are considered to be the content of a
Page
and, consequently, you don't need to use the explicit Page.Content
declaration.
The following markup is the declarative equivalent to the preceding sample.
Programmatic Navigation to a Page Object
The following example shows how to use the
NavigationService to programmatically navigate to a
Page.
Programmatic navigation is required because the
Page
that is being navigated to can only be instantiated using a single, non-default
constructor. The
Page
with the non-default constructor is shown in the following markup and code.
XAML
<Page
x:Class="SDKSample.PageWithNonDefaultConstructor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PageWithNonDefaultConstructor">
-->
</Page>Colourised in 2ms
C#
using System.Windows.Controls;
namespace SDKSample
{
public partial class PageWithNonDefaultConstructor : Page
{
public PageWithNonDefaultConstructor(string message)
{
InitializeComponent();
this.Content = message;
}
}
}Colourised in 6ms
The
Page
that navigates to the
Page
with the non-default constructor is shown in the following markup and code.
XAML
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSNavigationPage">
<Hyperlink Click="hyperlink_Click">
Navigate to Page with Non-Default Constructor
</Hyperlink>
</Page>Colourised in 3ms
C#
using System.Windows; using System.Windows.Controls; using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSNavigationPage : Page
{
public NSNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
PageWithNonDefaultConstructor page = new PageWithNonDefaultConstructor("Hello!");
this.NavigationService.Navigate(page);
}
}
}Colourised in 13ms
When the
Hyperlink on this
Page
is clicked, navigation is initiated by instantiating the
Page
to navigate to using the non-default constructor and calling the
NavigationService.Navigate method.
Navigate
accepts a reference to the object that the
NavigationService will navigate to, rather than a pack URI.
Programmatic Navigation with a Pack URI
If you need to construct a pack URI programmatically (when you can only
determine the pack URI at run time, for example), you can use the
NavigationService.Navigate method. This is shown in the following
example.
XAML
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSUriNavigationPage">
<Hyperlink Click="hyperlink_Click">Navigate to Page by Pack URI</Hyperlink>
</Page>Colourised in 3ms
C#
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSUriNavigationPage : Page
{
public NSUriNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
Uri uri = new Uri("AnotherPage.xaml", UriKind.Relative);
this.NavigationService.Navigate(uri);
}
}
}Colourised in 19ms
Refreshing the Current Page
A
Page
is not downloaded if it has the same pack URI as the pack URI that is stored in
the
NavigationService.Source
property. To force WPF to download the
current page again, you can call the
NavigationService.Refresh
method, as shown in the following example.
XAML
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSRefreshNavigationPage">
<Hyperlink Click="hyperlink_Click">Refresh this page</Hyperlink>
</Page>Colourised in 5ms
C#
using System.Windows; using System.Windows.Controls; using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSRefreshNavigationPage : Page
{
...
void hyperlink_Click(object sender, RoutedEventArgs e)
{
this.NavigationService.Refresh();
}
}Colourised in 9ms
Navigation Lifetime
There are many ways to initiate navigation, as you've seen. When navigation is
initiated, and while navigation is in progress, you can track and influence the
navigation using the following events that are implemented by
NavigationService:
-
Navigating. Occurs when a new navigation is requested. Can be used
to cancel the navigation.
-
NavigationProgress. Occurs periodically during a download to provide
navigation progress information.
-
Navigated. Occurs when the page has been located and downloaded.
-
NavigationStopped. Occurs when the navigation is stopped (by calling
StopLoading), or when a new navigation is requested while a current
navigation is in progress.
-
NavigationFailed. Occurs when an error is raised while navigating to
the requested content.
-
LoadCompleted. Occurs when content that was navigated to is loaded
and parsed, and has begun rendering.
-
FragmentNavigation. Occurs when navigation to a content fragment
begins, which happens:
-
Immediately, if the desired fragment is in the current content.
-
After the source content has been loaded, if the desired fragment is in
different content.
The navigation events are raised in the order that is illustrated by the
following figure.
In general, a
Page
isn't concerned about these events. It is more likely that an application is
concerned with them and, for that reason, these events are also raised by the
Application class:
Every time
NavigationService
raises an event, the
Application
class raises the corresponding event.
Frame
and
NavigationWindow
offer the same events to detect navigation within
their respective scopes.
Programming Guidelines for improving performance
Use asynchronous programming where possible to
improve performance, this can lead to performance degradations if not properly
used.
Write code smartly, use objects where it is
necessary, and declare only in the scope.
Avoid mistakes like:
bool test = false;
if (test == true)
{
}Colourised in 1ms
We can write instead:
if (test)
{
}Colourised in 1ms
Use centralized cache so that disposal of objects
are easy similar to WeakReference.
Dispose static objects and event when not
required by assigning to null.
Use LINQ or lambda expressions carefully avoid
programming mistake which is similar to SQL joins and can take long time if not
properly used.
Try to do without recursion and implement while
loop to exhibit same behaviour.
Avoid using delegate and event, use Action
command from .NET 4.0 and higher versions and release those command immediately
after use.
Build application with proper design from the
start, which helps to follow later and can provide large benefits.