【问题标题】:Form doesn't animate properly表单没有正确动画
【发布时间】:2023-03-21 20:43:01
【问题描述】:

在我的应用程序中,我有两个表单(这是我的第一个相当大的应用程序)

点击父窗体中的开始按钮后,我希望加载面板出现,并完成一些逻辑。

加载面板(它只是另一个寡妇形式)包含 bunifu 加载圆圈动画(和一些文本)。 逻辑部分负责从目录树中收集名称,然后替换树上 Ms.Word 文件中的一些文本。

当我在不执行逻辑的情况下打开加载面板时,加载面板动画正常,一切正常。

private void bunifuFlatButton1_Click(object sender, EventArgs e)
    {

        int x = this.Location.X+this.Width/2-75;
        int y = this.Location.Y +this.Height/2-175;
        Loader_panel LP = new Loader_panel();
        LP.Left = x;
        LP.Top = y;
        LP.Show();
        //System.Threading.Thread.Sleep(5000); \\this doesn't help animation to start

        if (FormLogic._dataList.Count > 0) \\Here Logic part starts
            {
            for (int i = 0; i < FormLogic._dataList.Count; i++)
                GetDir.GetTarget(FormLogic._dataList[i]);
            /*foreach (var directory in FormLogic._dataList)
                    GetDir.GetTarget(directory);*/
                LogList.Items.Add(DateTime.Now + "List isn't empty");// for testing
                FormLogic.ClearData();
            }
        LP.Close();
    }

启用逻辑加载面板后出现(外观不流畅),但动画不起作用(仅在逻辑部分完成工作时才开始工作 - 我通过禁用 LP.Close() 对其进行了测试。可以是什么这个问题的原因?

附加问题。在 .NET 环境中,代码被编译为与多个处理器线程一起工作,还是我必须手动完成?

编辑 06/08/2018 7:21 CEST

我无法从 GetDir 方法访问 LogList(由于其他线程处理它)。 我尝试了多个 Invoke 构造,但似乎都不起作用;/ 我只是太菜鸟,无法弄清楚。我在下面的代码中指定了更多细节:

namespace Docr
{
    public partial class DocrForm : Form
    {
.....
private async void Button1_Click(object sender, EventArgs e)
         {

             int x = this.Location.X + this.Width / 2 - 75;
             int y = this.Location.Y + this.Height / 2 - 175;
             Loader_panel LP = new Loader_panel();
             LP.Left = x;
             LP.Top = y;
             LP.Show(); //animation

             int count = FormLogic._dataList.Count;
             var list = FormLogic._dataList;
             await Task.Run(() =>// processing logic during showing animation
             {
                 if (count > 0)
                 {
                     for (int i = 0; i < count; i++)
                     {
                         GetDir.GetTarget(list[i],LogList); // Passing LogList as and argument
                     }
                     Invoke((Action)(() => { LogList.Items.Add(DateTime.Now + "Hi LogList"); }));\\ works fine
                 }
             });

             FormLogic.ClearData();
             LP.Close();
         }
....
}

namespace DocrLogic
{
    class GetDir
    {
    .....
        static public void GetTarget(string UserDirectory, ListBox List)// passing ListBox as an Argument
        {
            var path = UserDirectory;
            var TargetDir = new DirectoryInfo(path);
            var AllDocs1 = TargetDir.GetFiles("*.doc*", SearchOption.AllDirectories);
            var ProperPrefixes = new List<string> { };
            Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); })); // THIS DOESN'T WORK
            ....
        }
    .....
    }
}

【问题讨论】:

  • 逻辑需要使用线程或任务。

标签: c# winforms performance


【解决方案1】:

你需要创建方法async,然后使用await等待逻辑完成。这样LP表格就不会被繁重的逻辑打断了。

private async void bunifuFlatButton1_Click(object sender, EventArgs e)
{

    int x = this.Location.X+this.Width/2-75;
    int y = this.Location.Y +this.Height/2-175;
    Loader_panel LP = new Loader_panel();
    LP.Left = x;
    LP.Top = y;
    LP.Show();

    int count = FormLogic._dataList.Count;
    var list = FormLogic._dataList;
    await Task.Run(()=>
    {
         if(count > 0)
         {
             for (int i = 0; i < count; i++)
             {
                 GetDir.GetTarget(list[i]);
             }
             this.Invoke(() => { LogList.Items.Add(DateTime.Now + "List isn't empty"); });
         }
    });

    FormLogic.ClearData();
    LP.Close();
}

您必须通过使用Invoke 使您的代码线程安全 来访问非线程安全的对象,例如 UI 对象,否则,它会抛出 System.Threading.ThreadAbortException 或System.InvalidOperationException。


调用语法可能因您的项目而异,但您可以 see this post了解Invoke()的正确使用方法


更新

您必须绝不尝试在调用之外访问 UI 对象。调用由System.Windows.Forms.Control 提供,取决于最初创建该控件的线程。因此,在其他一些随机类上调用根本不起作用

在第二部分,你需要改变

public static void GetTarget(string UserDirectory, ListBox List)// passing ListBox as an Argument
{
   ...
   Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); })); // THIS DOESN'T WORK
}

(您需要将整个调用行作为操作参数发送)

public static void GetTarget(string UserDirectory, Action action)// passing the action as an Argument
{
   ...
   action();
}

(您需要在启动Task之前将Dispatcher设置为LogList

public static Control Dispather;
public static void GetTarget(string UserDirectory)// passing the action as an Argument
{
   ...
   Dispather.Invoke((Action)(() => { List.Items.Add(DateTime.Now + "Hi Log List, GetDir here"); }));
}

【讨论】:

  • 非常感谢。在这种情况下,调用方法应如下所示: this.Invoke((Action)(() => { LogList.Items.Add(DateTime.Now + "List is not empty"); }));我在这个线程中找到了这个:link。除了概念,我什么都不懂。无论如何,Tt让我对知识感到饥饿:D
  • 现在我想知道,我是否可以从 GetDir 方法向 ListBox 添加一些内容(当我将与 LogList 相关的代码放在此方法中时)。它会起作用吗?在这种情况下我应该使用调用吗?
  • @TamaraL96 语法只是包含命名空间和品味的问题。编写多线程代码时唯一需要考虑的是特定操作是否天生是线程安全的。在调用方法中添加额外的逻辑会将负载放到不需要的 UI 线程上。
  • 我认为我无法在 GetDir 方法中正确使用 Invoke。你能看看更新的列表吗(更新了问题)?
  • @TamaraL96 现在你只是把它复杂化了,为什么你必须将LogList 传递给GetTarget 方法?它没有任何意义。如果它是一个 get 方法,它应该返回一些值。否则,您必须将其分解为 get 和 set 方法。其中 get 方法是线程安全的,而 set 方法不是。然后调用 set 方法。
猜你喜欢
  • 2015-09-02
  • 1970-01-01
  • 2021-06-23
  • 2012-06-21
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多