为什么我的WPF应用程序加载动画钢冻结?

问题描述 投票:0回答:1

我正在开发用于管理林目录的桌面应用程序。启动应用程序时,必须从MySql数据库中显示某些数据,这会减慢应用程序的启动速度,因此我想显示一个对话框,其中显示动画gif,直到数据加载完成。问题在于应用程序冻结,并且直到数据加载完成才显示对话框。我搜索了其他帖子,但找不到解决方案。感谢您的帮助,谢谢。

这是我的MainWindow XAML:

<mui:ModernWindow x:Class="ModernUINavigationApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
Title="Media Copy Manager" IsTitleVisible="True"
WindowStartupLocation="CenterScreen"


ContentSource="/gui/Pages/PHome.xaml" WindowState="Maximized">

<mui:ModernWindow.MenuLinkGroups>
    ( ...)
</mui:ModernWindow.MenuLinkGroups>

这是我的PHome.xaml页面:

<UserControl x:Class="MCP.gui.Pages.PHome"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mui="http://firstfloorsoftware.com/ModernUI"
         mc:Ignorable="d">

<Grid Style="{StaticResource ContentRoot}" Margin="0 0 0 0" Name="_contentRoot">
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
     </Grid.ColumnDefinitions>

    <TabControl Grid.Row="0" Grid.Column="0" Name="_TAB" TabStripPlacement="Left"/>
</Grid>

这是我的Home.cs代码:

{
    public PHome()
    {
        InitializeComponent();
        this.Loaded += ContentLoaded;
    }
    private void ContentLoaded(object sender, RoutedEventArgs e)
    {
         Thread t = new Thread(() =>
         {
             Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
               (Action)(() =>
               {
                   new LoadingDialog().Show();
               }));
         });
         t.SetApartmentState(ApartmentState.STA);
         t.IsBackground = true;
         t.Start();
        Task.Run(() =>
        {
            _TAB.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
                (Action)(() =>
                {
                    Populate_Tab(_TAB);
                }));
        });
    }
    private async void Populate_Tab(TabControl tabControl)
    {
        tabControl.Items.Clear();
        tabControl.ClipToBounds = true;

        List<categoria> ListaCategorias = await DBManager.CategoriasRepo.ListAsync;
        foreach (categoria categ in ListaCategorias)
        {
            TabItem tabitem = new TabItem();
            tabitem.Header = categ.categoria1;
            Thread.Sleep(1000);   //Do some long execution process
            tabControl.Items.Add(tabitem);
        }
    }
}

这是我的LoadingDialog XAML:

                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                  xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                  xmlns:mui="http://firstfloorsoftware.com/ModernUI"
                  mc:Ignorable="d" d:DesignWidth="300"
                  Title="ModernDialog" Background="{x:Null}" Height="197.368">
    <StackPanel>
        <TextBlock>LOADING</TextBlock>
        <mui:ModernProgressRing IsActive="True" Name="_LoaderGif" Width="100" Height="100" Style="{StaticResource ThreeBounceProgressRingStyle}" Margin="47,43,68,65" />
    </StackPanel>
</mui:ModernDialog>

c# wpf mui
1个回答
0
投票

您需要更好地了解所使用的方法,尤其是用来尝试将方法移动到其他线程上的方法。在我解释之前,请记住以下几点:

  • WPF应用程序中的默认主主线程可以称为UI线程。在该线程上处理所有用于呈现和布局应用程序的代码。除非另有说明,否则您编写的所有代码也会在此线程上处理。
  • [线程一次运行一个方法,因此UI线程上任何长时间运行的方法都将阻止它运行布局和呈现应用程序所需的方法(即它将“冻结”)。
  • 对UI元素的更改-应用程序的视觉方面-必须通过UI线程完成。

您似乎不明白的是Dispatcher.Invoke

就像Dispatcher.Invoke将代码执行移至新线程上,Task.Run将代码执行移至[[返回UI thead。

让我们采用您的方法Dispatcher.Invoke。在您的代码中,您尝试同时使用ContentLoadedThread类将某些代码移到单独的线程上。但是,您从这些新线程中所做的所有操作都会立即调用Task,它将在UI线程上执行代码。从理论上讲,这实际上使您的应用程序

slower

,因为它浪费时间创建新线程并无故在它们之间跳转。您需要做的是将Dispatcher.Invoke分成两种(或多种)不同的方法:一种会影响UI,而另一种则不会。

[C0和C0之类的调用必须在UI线程上进行,因为它们直接引用UI元素,但是您长期运行的操作(如访问数据库)可以并且应该移至另一个线程上。

顺序应为:

    显示加载图标。
  • 在另一个线程上开始长时间运行的操作。我建议将Populate_TabtabControl.Items.Cleartabitem.Header = categ.categoria1一起使用,因为这是当前的最佳做法。
  • 获取所需的所有数据,并在另一个线程上进行任何计算。
  • 将所需数据返回到UI线程(使用Task并根据需要更新UI。
  • 删除加载图标。
© www.soinside.com 2019 - 2024. All rights reserved.