
The application is not responding?
Many of those who program WPF applications have probably written a structure like this a thousand times:
{ Binding Items }
If the items in the
Items collection are retrieved in the main application thread and take some time, we get a dead user interface. The application will not display state changes for a while and will respond to user input.
And if the processing time exceeds a certain time limit defined in the Windows window system, the system will mark this window as not responding to requests: a white mask will overlap the image of the last successful window rendering and a special marker (Not responding) will be added to the title (
Russian localization ):

Yes, you yourself probably many times observed a similar picture, when any application as a result of a failure or the synchronous operation "goes into itself." And, of course, you know how annoying the user is. Most
will fight hysterically, breaking the keyboard on the monitor, will look at the screen with incomprehension, trying to understand what is happening with the program. More “advanced” users, without waiting for the end of a lengthy operation, will close the “delinquent” window with the help of the task manager.
Solution # 1: Asynchronous ObjectDataProvider
The solution is very simple and is ideal for those who use
ObjectDataProvider in current projects as a data source.
')
Step # 1: Implement a simple static data provider
The provider is a regular static class with one method:
// Emulates a long items getting process using some delay of getting of each item <br/>
public static class AsyncDataProvider<br/>
{ <br/>
private const int _DefaultDelayTime = 300 ; <br/>
<br/>
public static ReadOnlyCollection < string > GetItems ( ) <br/>
{ <br/>
return GetItems ( _DefaultDelayTime ) ; <br/>
} <br/>
<br/>
public static ReadOnlyCollection < string > GetItems ( int delayTime ) <br/>
{ <br/>
List < string > items = new List < string > ( ) ; <br/>
foreach ( string item in Enum . GetNames ( typeof ( AttributeTargets ) ) . OrderBy ( item => item. ToLower ( ) ) ) <br/>
{ <br/>
items. Add ( item ) ; <br/>
// Syntetic delay to emulate a long items getting process <br/>
Thread. Sleep ( delayTime ) ; <br/>
} <br/>
<br/>
return items. AsReadOnly ( ) ; <br/>
} <br/>
}
Step 2: Declare the asynchronous data source in XAML
<Window.Resources > <br/>
<ObjectDataProvider x:Key = "AsyncDataSource" <br/>
IsAsynchronous = "True" <br/>
ObjectType = "Providers:AsyncDataProvider" <br/>
MethodName = "GetItems" /> <br/>
<Converters:NullToBooleanConverter x:Key = "NullToBooleanConverter" /> <br/>
</Window.Resources >
The
NullToBooleanConverter Converter is just an auxiliary object, the purpose of which can be read in the name (
its implementation can be found in the project attached to the topic ). All magic lies in the attribute
IsAsynchronous =
"True" of the
ObjectDataProvider object. This attribute is responsible for controlling the way in which data is received — if this attribute is set to
“True”, the WPF core will create a
Dispatcher background object to get the value of this property and thus the binding will be executed in the background thread without interfering with the main flow of the application to handle user input.
Step 3: Use the data provider in the code
<ListBox x:Name = "ItemsListBox" <br/>
ItemsSource = "{Binding Source={StaticResource AsyncDataSource}, IsAsync=True}" > <br/>
<ListBox.Style > <br/>
<Style TargetType = "{x:Type ListBox}" > <br/>
<Style.Triggers > <br/>
<Trigger Property = "ItemsSource" Value = "{x:Null}" > <br/>
<Setter Property = "Template" Value = "{StaticResource WaitControlTemplate}" /> <br/>
</Trigger > <br/>
</Style.Triggers > <br/>
</Style > <br/>
</ListBox.Style > <br/>
</ListBox >
Please note that a trigger is used for the list, which allows you to visualize the process of receiving data for the user. This is very important - if you do not inform the user about some lengthy operation, he will think that the application simply does not work, because no action on the list will be available (
with proper processing of their states, of course - more on that later ).
Step # 4: Do not forget to handle the availability of actions
<Button Grid.Column = "1" <br/>
Content = "Edit" <br/>
Width = "70" <br/>
IsEnabled = "{Binding SelectedItem, ElementName=ItemsListBox, Converter={StaticResource NullToBooleanConverter}}" <br/>
Click = "EditButton_Click" /> <br/>
Step 5: In action
Here is what the main window looks like after launching with all our changes:

And this is how it looks after the data is received:

The
Edit button is attached to the selected item through a simple converter. If the selected item in the main
ItemsListBox list
is missing, the button will be unavailable. You can select an element only after the asynchronous data provider
AsyncDataSource fills the list with elements. The
Close button is added to visualize the ability to control the application - nothing prevents you from clicking on it during the data acquisition process and closing the main window. The application will respond to our request regularly and close, which would not have happened if our data source were synchronous.
Solution # 2 Asynchronous Binding
The second solution to this problem uses the
MV-VM pattern
(Model-View-ViewModel) , probably one of the most popular patterns of building modular applications for WPF and Silverlight. Discussion of this pattern is beyond the scope of this article - if you wish, you can easily find a lot of information about it on the network (
if you are too lazy to look, look at the Links section at the end of the article ).
Step # 1:
Create a view model for the main application window:
public class MainViewModel<br/>
{ <br/>
private ICommand _commandClose ; <br/>
<br/>
private ICommand _commandEdit ; <br/>
<br/>
private ReadOnlyCollection < string > _items ; <br/>
<br/>
public ReadOnlyCollection < string > Items<br/>
{ <br/>
get<br/>
{ <br/>
if ( _items == null ) <br/>
{ <br/>
_items = AsyncDataProvider. GetItems ( ) ; <br/>
} <br/>
<br/>
return _items ; <br/>
} <br/>
} <br/>
<br/>
public ICommand CommandClose<br/>
{ <br/>
get<br/>
{ <br/>
if ( _commandClose == null ) <br/>
{ <br/>
_commandClose = new RelayCommand ( p => OnClose ( ) ) ; <br/>
} <br/>
<br/>
return _commandClose ; <br/>
} <br/>
} <br/>
<br/>
public ICommand CommandEdit<br/>
{ <br/>
get<br/>
{ <br/>
if ( _commandEdit == null ) <br/>
{ <br/>
_commandEdit = new RelayCommand ( p => OnEdit ( p ) , p => CanEdit ) ; <br/>
} <br/>
<br/>
return _commandEdit ; <br/>
} <br/>
} <br/>
<br/>
public string SelectedItem<br/>
{ <br/>
get ; <br/>
set ; <br/>
} <br/>
<br/>
private void OnClose ( ) <br/>
{ <br/>
App. Current . Shutdown ( ) ; <br/>
} <br/>
<br/>
private void OnEdit ( object parameter ) <br/>
{ <br/>
MessageBox. Show ( String . Format ( "Edtiting item: {0}" , <br/>
parameter != null ? parameter. ToString ( ) : "Not selected" ) ) ; <br/>
} <br/>
<br/>
private bool CanEdit<br/>
{ <br/>
get<br/>
{ <br/>
return SelectedItem != null ; <br/>
} <br/>
} <br/>
}
Step # 2: Change the binding declaration a bit in the main view XAML code
<ListBox x:Name = "ItemsListBox" <br/>
Grid.Row = "0" <br/>
ItemsSource = "{Binding Items, IsAsync=True}" <br/>
SelectedItem = "{Binding SelectedItem}" > <br/>
<ListBox.Style > <br/>
<Style TargetType = "{x:Type ListBox}" > <br/>
<Style.Triggers > <br/>
<Trigger Property = "ItemsSource" Value = "{x:Null}" > <br/>
<Setter Property = "Template" Value = "{StaticResource WaitControlTemplate}" /> <br/>
</Trigger > <br/>
</Style.Triggers > <br/>
</Style > <br/>
</ListBox.Style > <br/>
</ListBox >
In this scenario, the attribute specified in the binding markup is responsible for asynchrony:
"{Binding Items, IsAsync = True}" . As with the
ObjectDataProvider example, the WPF kernel will create a separate background dispatcher to get the binding value in a separate asynchronous context.
Separately, it should be noted that in this scenario we do not need to resort to coding the visibility rules of controls in the XAML code of the main window view. In the above view model code, the
MainViewModel.CanEdit property, which is part of the
MainViewModel.CommandEdit command, is responsible for the visibility of the
Edit button on the form. You can learn more about the
Command pattern by looking in the
Links section. Here it will be appropriate to note only that we do not have to do anything manually - the
CommandManager class will take care of
everything . All that is required of us is the proper implementation of the
ICommand contract, which is provided by the
RelayCommand class (you can familiarize yourself with the implementation of this class in the attached project).
Homework
It is done at will - I won’t check it, don’t even ask :) You can slightly improve the
WaitControlTemplate template
by turning it into a full control inherited from the
Border class or from its ancestor
Decorator , if you want to make a full control according to all the rules. The behavior and logic of this element will be simple:
- Only ItemsControl or any of its descendants ( ListBox, ListView, TreeView etc ) can be added to the element - it is desirable to control this at the most hard level, up to throwing an exception if the Content property is not ItemsControl
- When the Content property changes, the control tries to cast the contents to ItemsControl and get the data binding value of the ItemsSource property.
- If the previous step was successful - transfer the binding to asynchronous mode
- The visualization of the control can be partially built on the logic of the Substitute pattern (Proxy) - while the data is loaded asynchronously, the control shows its contents (a spinning load indicator and an inscription asking to wait), after the download is complete, the contents of the Content property are displayed
Summary
The main customers of developers are users. And from time to time it is useful for each developer to put himself in the place of a simple user, conduct a full cycle of testing his application and try to analyze what exactly he (as a user) annoys and what he (as a user) would like to improve.
Source
AsyncBinding.zipLinks
WPF applications with a design pattern model-view-view modelCommand System OverviewAsynchronous Data Binding in Windows Presentation FoundationAsynchronous WPFThe original master image for the topic is taken
from here.