0

In my application i have two Forms (that's my 1st quite big app)

After clicking start button in parent form i want loading panel to appear, and some logic to be done.

Loading panel (it is just another widowss form) contains bunifu loading circle animation (and some text). Logic part is responsible for collecting names from directory tree, then replacing some text in Ms.Word files on the tree.

When i open loading panel without executing the logic, loading panel is animated properly and everything works fine.

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();
    }

After enabling logic loading panel appears (appearance isn't smooth), but animation doesn't work (it starts to work only when logic part did the work - i tested it by disabling LP.Close(). What can be reason of this problem ?

Additinal question. In .NET environment the code is compiled to work with multiple processor threads or i have to do it manually ?

EDIT 06/08/2018 7:21 CEST

I cant access LogList from GetDir Method (due to processing it by other thread). I tried multiple Invoke construction, but none of it seemed to work ;/ I am just too rookie to figure it out. I specified more details in code below:

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
            ....
        }
    .....
    }
}
TamaraL96
  • 47
  • 6

1 Answers1

1

You need to make the method async, then use await to wait for the logic to complete. This way the LP form won't be interrupted by the heavy logic.

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();
}

You have to make your code thread-safe by using Invoke to access not-thread-safe objects such as UI objects, otherwise, it will throw a System.Threading.ThreadAbortException or a System.InvalidOperationException.


Invoke syntax may differ based on your project but you may see this post to understand the proper ways of using Invoke()


update

You must never try to access a UI object outside invoke. invoke is provided by System.Windows.Forms.Control and depends on the thread which originally created that control. therefore having Invoke on some other random class simply does not work.

In the second part, you need to change

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
}

to

(you need to send the whole invoke line as the action parameter)

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

or

(you need to set Dispatcher to LogList before starting the Task)

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"); }));
}
Bizhan
  • 16,157
  • 9
  • 63
  • 101
  • Thank You very much. In this case invoke method should look like: this.Invoke((Action)(() => { LogList.Items.Add(DateTime.Now + "List isn't empty"); })); I found this in this thread: [link](https://stackoverflow.com/questions/14380996/cannot-convert-lambda-expression-to-type-system-delegate-because-it-is-not-a-d). I pretty don't understand anything except concept. Tt makes me knowledge hungry anyway :D – TamaraL96 Aug 06 '18 at 15:56
  • Now i wonder, whether i can add something to the ListBox from GetDir method (when i place the code related to LogList inside this method). Will it work ? Should i use invoke in this case ? – TamaraL96 Aug 06 '18 at 16:07
  • @TamaraL96 the syntax is just a matter of included namespaces and taste. the only thing that you should consider when writing multithread code is whether or not a specific operation by-nature is thread-safe or not. putting extra logic into the invoke method puts the load onto the UI thread which is unwanted. – Bizhan Aug 06 '18 at 16:27
  • I think i am unable to properly use Invoke inside GetDir method. Could you take a look on updated listings please (updated the question) ? – TamaraL96 Aug 06 '18 at 17:27
  • @TamaraL96 now you're just overcomplicating it, why do you have to pass the `LogList` to the `GetTarget` method? it does not make sense. if it's a get method it should return some value. otherwise you have to break it into a get and a set method. where get method is thread-safe and set method isn't. then invoke the set method. – Bizhan Aug 06 '18 at 17:34
  • @TamaraL96 the problem with your code is you are still accessing a UI object in a non-thread-safe way. you need to be more careful how you access UI object from another thread. – Bizhan Aug 06 '18 at 17:36
  • Inside GetDir i use some additional methods (from the class GetDir belongs) for processing files (i didn't show them on listings to save space), which make cause exceptions etc. That's why i want them to throw informations directly to LogList. – TamaraL96 Aug 06 '18 at 17:38
  • @TamaraL96 you can find many ways to achieve that. take a look at my answer – Bizhan Aug 06 '18 at 17:55
  • It works !! . You're incredible. Wish i had so much knowledge :) I used the dispatcher solution due to bigger flexibility. I passed Listbox to GetDir, then set to dispatcher. – TamaraL96 Aug 06 '18 at 18:08
  • @TamaraL96 i'm glad to help :) please don't forget to accept my answer to its top left corner. – Bizhan Aug 06 '18 at 18:12