Using such a component as
BusyIndicator will bring pleasant (process indication) and useful (control lock) qualities into our application. However, until recently, I rarely used it, because during the asynchronous receiving of the data source, it was necessary to constantly write additional code for switching on / off. When working synchronously, the situation seems to be simplified, but using the
MVVM model still requires additional gestures. Especially if the
BusyIndicator
added at the very end of form development.
Therefore, I decided to automate this process as much as possible so that I would not have to write a single line of code. So:
Formulation of the problem:- Provide automatic display for any lists that use the value of the
ItemsSource
property as a data source. - A sign that the data is received will be considered as not an empty value of the
ItemsSource
property. - As a
BusyIndicator
you can use any control as long as it has the boolean
IsBusy
property. - All additional coding should be implemented in the form view (View) and the XAML code should have the following pattern:
<BusyIndicator ...> <ListBox ItemsSource="{Binding DataList, IsAsync=true}" ...> ... </ListBox> <BusyIndicator>
The IsAsync
IsAsync=true
binding property in the example can be omitted, in this case, the reasoning is not particularly changed, but in this article I will give examples of the exact data acquisition, since if process indication is required, data acquisition takes considerable time, and if so, then we don’t want our application to freeze, and if so, then our asynchronous binding is all. Moreover, it costs us nothing to implement it: IsAsync = true in XAML and in the ViewModel code:
private IEnumerable _dataList = null; public IEnumerable DataList { get { if (_dataList == null) _dataList = Model.GetDataList(...); return _dataList; } private set { if (_dataList == value) return; _dataList = value; NotifyPropertyChanged("DataList"); } } public void RefreshDataList() { DataList = null; }
')
The first thing that occurred to me (and it even worked visually) was to write something like this:
<BusyIndicator IsBusy="{Binding DataList, IsAsync=true, Converter={StaticResource NullToBool}"> <ListBox ItemsSource="{Binding DataList, IsAsync=true}" ...> ... </ListBox> <BusyIndicator>
Well, who can immediately criticize this code?So I didn’t like it either. Debugging confirmed my concerns: getting the list happened twice - for
BusyIndicator
and for the list.
“Do not worry!” - I said and slightly changed the method of obtaining the list:
private object _dataListSync = new nbject(); private IEnumerable _dataList = null; public IEnumerable DataList { get { lock (_dataListSync) { if (_dataList == null) _dataList = Model.GetDataList(...); return _dataList; } } }
Now the data acquisition happened once and everything worked as it was intended, but I didn't like this method anyway.
Firstly, you need to register the converter, secondly, write quite a few identical
bukaffs , not forgetting to synchronize the bindings of the list and the indicator, if something changes, thirdly, I’m not sure that getting the
DataList
for the indicator will
always be called
before receiving
DataList
for the list.
The next idea was to use
attached property
.
Until now, I have not been given the opportunity to understand this deeply with a real interesting example, so in the next article,
Creating Attached Property for BusyIndicator, I will tell
you step by step what I did.
Thanks to those who read to the end.