I heave two user controls. The first one contains one ItemsControl
and I have to fill it dynamically with my second user control. The second user control may contains another ÌtemsControl
itself recursively so it is quite a heavy control to initialize.
Here is what I did for the SubControl
:
public sealed partial class SubControl : UserControl
{
public SubControl(ModelA item)
{
this.InitializeComponent();
this.DataContext = item;
//if the current item has child items, add them to the ItemsControl:
if(item.SubItems != null)
{
BuildUI(item.SubItems);
}
}
private void BuildUI(List<AbstractModel> data)
{
foreach(var item in data)
{
var dataItem = item as ModelA;
//we only want item of type ModelA in our ItemsControl:
if(dataItem != null)
{
SubElements.Items.Add(new SubControl(dataItem));
}
}
}
}
Now here is what I wrote first for the MainControl
whose ItemsControl
(called Elements
) will contain a set of these SubControl
:
public sealed partial class MainControl : UserControl
{
public MainControl(List<AbstractModel> data)
{
this.InitializeComponent();
BuildUI(data);
}
private void BuildUI(List<AbstractModel> data)
{
foreach(var item in data)
{
var dataItem = item as ModelA;
if(dataItem != null)
{
Elements.Items.Add(item);
}
}
}
}
I noticed small freezes of the UI while this "tree" was being built, but I currently work on a very powerful computer. I would like the application to run smoothly even on lesser devices such as the Windows Surface RT. So I changed the MainControl
code:
public sealed partial class MainControl : UserControl
{
public MainControl(List<AbstractModel> data)
{
this.InitializeComponent();
BuildUI(data);
}
private async void BuildUI(List<AbstractModel> data)
{
var list = new List<SubControl>();
await Task.Run(() =>
{
foreach(var item in data)
{
var dataItem = item as ModelA;
if(dataItem != null)
{
list.Add(new SubControl(dataItem));
}
}
});
foreach(var item in list)
{
Elements.Items.Add(item);
}
}
}
The idea is to build all the SubControl
in a different thread so the UI is not blocked, and when all the user controls have been initialized we would add them to the ItemsControl
in the MainControl
.
However this does not work because of the marshalling of the data, even though not a single SubControl
is actually present on the UI! It crashes while building the SubControl
which is really weird, because it does not have any impact on the actual UI; they are just added to a temporary List
.
What could be a trick to build these user controls in a background task so the UI does not freeze?
Windows UI is very single-threaded. Each UI control must be created and only used from a single thread. There is no way around this.
So, it's time to think about the solution a bit differently. It's no problem to create dozens of controls; the UI would handle that just fine. You're talking about adding hundreds or thousands of items to a list control, and that's just an unusable UI. So the proper solution is to rethink your UI design. Perhaps you could divide the results into categories or something.
If you've thought about your UI design and are still sure that you want to display hundreds or thousands of items to the user, then the answer is to use virtualization . This is a bit harder to code than just a simple foreach
loop, but it is the only way to efficiently display large amounts of data.
There are many ways to do this.
You could use this workflow:
[...] because it does not have any impact on the actual UI; they are just added to a temporary List
By the way: Your code does crash, because you are adding the Controls in a async method, which is actually a non GUI-thread. So you are wrong with your proposition.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.