The application window is locked while the rendering operation is active. That is, when the Content property for ContentControl is set. A user control is drawn, which is the DataTemplate for the content. Freezing lasts from 5 to 10 seconds depending on the PC used.
This user control is not too complicated (about 250 simple controls - images, text fields, text blocks, buttons, etc.). The layout is far from perfect, I did not write it, and I do not have time and do not want to optimize the layout, since the problem can be reduced at best.
The best I could do was wrap the control in a “container” that manages to draw a loading animation and show the loaded cursor before freezing the ui / app window. I give a complete list of codes for it below.
I commented on the “freeze starts here” in the code, at the bottom of the question in the user shell control code. This is when the WPF rendering engine begins to draw a user control (i.e. the Grid inside it).
I used my favorite search engine and I learned that WPF has a special "render" thread that is separate from the user interface thread.
Another, which hides the application window during its freezing and displays the “download” animation window during this time (or some derivative of it), which is easily given to the code below, but is absurd - is there a way to mitigate the situation?
Here is the code, a precedent first:
<UserControl x:Class="MyUserControl"
xmlns:loading="clr-namespace:Common.UI.Controls.Loading;assembly=Common.UI.Controls">
<loading:VisualElementContainer>
<loading:VisualElementContainer.VisualElement>
<Grid>
</Grid>
</loading:VisualElementContainer.VisualElement>
</loading:VisualElementContainer>
</UserControl>
Custom Wrapper Management Layout:
<Style TargetType="{x:Type loading:VisualElementContainer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type loading:VisualElementContainer}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<loading:LoadingAnimation x:Name="LoadingAnimation" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<ContentControl x:Name="ContentHost"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And custom shell control code:
public class VisualElementContainer : Control
{
static VisualElementContainer()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(VisualElementContainer), new FrameworkPropertyMetadata(typeof(VisualElementContainer)));
}
private Window MyWindow;
private ContentControl ContentHost;
private LoadingAnimation LoadingAnimation;
public override void OnApplyTemplate()
{
this.ContentHost = this.GetTemplateChild("ContentHost") as ContentControl;
this.LoadingAnimation = this.GetTemplateChild("LoadingAnimation") as LoadingAnimation;
base.OnApplyTemplate();
this.MyWindow = this.FindVisualParent(typeof(Window)) as Window;
this.SetVisual(this.VisualElement);
}
private static DependencyProperty VisualElementProperty =
DependencyProperty.Register(
"VisualElement",
typeof(FrameworkElement),
typeof(VisualElementContainer),
new PropertyMetadata(null, new PropertyChangedCallback(VisualElementPropertyChanged)));
public FrameworkElement VisualElement
{
get { return GetValue(VisualElementProperty) as FrameworkElement; }
set { SetValue(VisualElementProperty, value); }
}
private static void VisualElementPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var me = sender as VisualElementContainer;
if (me == null || me.ContentHost == null || me.LoadingAnimation == null)
return;
me.RemoveVisual(e.OldValue as FrameworkElement);
me.SetVisual(e.NewValue as FrameworkElement);
}
private void RemoveVisual(FrameworkElement fwElement)
{
this.ContentHost.Content = null;
if (fwElement != null)
fwElement.Loaded -= fwElement_Loaded;
}
private void SetVisual(FrameworkElement fwElement)
{
if (fwElement == null)
{
this.ContentHost.Content = fwElement;
}
else
{
fwElement.Loaded += fwElement_Loaded;
this.SetContentVisibility(false);
this.Dispatcher
.BeginInvoke(
new Action(() => this.ContentHost.Content = fwElement),
System.Windows.Threading.DispatcherPriority.ContextIdle);
}
}
private void fwElement_Loaded(object sender, RoutedEventArgs e)
{
this.SetContentVisibility(true);
}
private void SetContentVisibility(bool isContentVisible)
{
if (isContentVisible)
{
this.MyWindow.Cursor = Cursors.Arrow;
this.LoadingAnimation.Visibility = Visibility.Collapsed;
this.ContentHost.Visibility = Visibility.Visible;
}
else
{
this.MyWindow.Cursor = Cursors.Wait;
this.ContentHost.Visibility = Visibility.Hidden;
this.LoadingAnimation.Visibility = Visibility.Visible;
}
}
}