📜 ⬆️ ⬇️

Automatic BusyIndicator for asynchronous operations and not only

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:
  1. Provide automatic display for any lists that use the value of the ItemsSource property as a data source.
  2. A sign that the data is received will be considered as not an empty value of the ItemsSource property.
  3. As a BusyIndicator you can use any control as long as it has the boolean IsBusy property.
  4. 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.

Source: https://habr.com/ru/post/141731/


All Articles