gpt4 book ai didi

c# - wpf 窗口 (ui) 被长时间渲染操作阻塞 - 可以使用后台线程进行渲染吗?

转载 作者:行者123 更新时间:2023-11-30 12:27:25 25 4
gpt4 key购买 nike

当渲染操作处于事件状态时,应用程序窗口会阻塞。IE。当设置了 ContentControl 的 Content 属性时。绘制一个用户控件,它是内容的 DataTemplate。卡住持续 5 到 10 秒,具体取决于所使用的 PC。

这个用户控件并不太复杂(大约 250 个简单控件 - 图像、文本框、文本 block 、按钮等)。布局远非完美,我没有写,我也没有时间,也不想优化布局,最多可以减少问题。

我能够完成的最好的事情是将控件包装在一个“容器”中,该容器设法绘制一个加载动画并在 ui/app 窗口卡住之前显示一个忙碌的光标。我在下面给出了完整的代码 list 。

我在代码中评论了“卡住从这里开始”,位于包装器自定义控件代码中问题的底部。那是 WPF 呈现引擎开始绘制用户控件(即其中的网格)的时候。

我经常使用我最喜欢的搜索引擎,我了解到 WPF 有一个特殊的“渲染”线程,它与 UI 线程是分开的。

其他的是在应用程序窗口卡住时隐藏它并在此期间显示“加载”动画窗口(或此的某些派生),这很容易给出下面的代码但荒谬 - 有什么方法可以缓解这种情况?

这是代码,首先是用例:

<!-- while I am being rendered, I block the UI thread. -->
<UserControl x:Class="MyUserControl"
xmlns:loading="clr-namespace:Common.UI.Controls.Loading;assembly=Common.UI.Controls">
<loading:VisualElementContainer>
<loading:VisualElementContainer.VisualElement>
<Grid>
<!-- some 500 lines of using other controls with binding, templates, resources, etc..
for the same effect try having a canvas with maaany rectangles..-->
</Grid>
</loading:VisualElementContainer.VisualElement>
</loading:VisualElementContainer>
</UserControl>

包装器自定义控件布局:

<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>

和包装器自定义控件代码:

/// <summary>Hosts the visual element and displays a 'loading' animation and busy cursor while it is being rendered.</summary>
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(
//freeze begins here
new Action(() => this.ContentHost.Content = fwElement),
System.Windows.Threading.DispatcherPriority.ContextIdle);
}
}

private void fwElement_Loaded(object sender, RoutedEventArgs e)
{
this.SetContentVisibility(true);
//freeze ends here.
}

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; //Setting to collapsed results in the loaded event never fireing.
this.LoadingAnimation.Visibility = Visibility.Visible;
}
}
}

最佳答案

我真的不认为你的问题实际上与渲染或布局有关。特别是只有 250 个控件,因为我看到 wpf 处理了 100 多倍而没有任何问题(它的渲染引擎效率低下但不是效率低下)。除非你滥用丰富的效果(位图效果、不透明蒙版)和糟糕的硬件或糟糕的驱动程序,或者做一些非常奇怪的事情。

考虑您需要的所有数据。是否有大图像或其他大型资源可从磁盘加载?网络运营?长计算?

根据答案,可以将某些任务推迟到另一个线程。但是没有更多信息,我可以建议的唯一解决方案是使用 HostVisual 嵌套将存在于另一个线程中的控件。不幸的是,这仅适用于非交互式 child (不需要接收用户输入的 child )。

关于c# - wpf 窗口 (ui) 被长时间渲染操作阻塞 - 可以使用后台线程进行渲染吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25485909/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com