0

I made my own FilePreviewHandler for preview some files like Explorer because currently I made custom file browser.

When I click items on ListView, I can preview files like explorer.

The PreviewHandler which is in my file browser Contains WebBrowser Class for load Movie/Text files, PictureBox for load Image files, and ShellExPreviewHandler for other extensions.

The problem which engaged is like this.

When I attempt to load large file such 100mb or more large file or big image file like 8000x4000 pixels, form freezes until it load file.

Code Area for PreviewHandler :

    public void Open( String FileName )
    {
        if ( !Enabled || Last == FileName )
        {
            return;
        }
        //FileName = DriveUtil.RevealPath( FileName );

        String FileType = MIMEAssistant.GetMIMEType( FileName );
        // Image Files
        if ( ImagePreviewTarget.Where( x => FileType.Contains( x ) ).Count() > 0 )
        {
            IsProgress = true;
            try
            {
                if ( ImagePreviewHandler.Image != null )
                {
                    ImagePreviewHandler.Image.Dispose();
                    GC.Collect();
                }
                Image TargetImage = Image.FromFile( FileName );
                if ( TargetImage.Width > this.Width || TargetImage.Height > this.Height )
                {
                    ImagePreviewHandler.SizeMode = PictureBoxSizeMode.Zoom;
                }
                else
                {
                    ImagePreviewHandler.SizeMode = PictureBoxSizeMode.CenterImage;
                }
                ImagePreviewHandler.Image = TargetImage;
                ImagePreviewHandler.BringToFront();
            }
            catch
            {
                ImagePreviewHandler.Image = null;
                GC.Collect();
                ShellExPreviewHandler.BringToFront();
                IsProgress = false;
            }
        }


        // WebPreviewArea
        // This area doesn't make any problem. So I hide my codes on here.


        // PdfPreviewArea
        // Currently I don't use Pdf PreviewHandler. So I also hide my codes on here.

        // Other extensions
        else
        {
            ShellExPreviewHandler.Open( FileName );
            ShellExPreviewHandler.BringToFront();
        }

        Last = FileName;
    }

What I Tried :

I tried to use Thread, Task.Run, Backgroundworker for this, but all of this requires MyForm.Invoke to draw controls.

Thread Part =>

        Thread Preview = new Thread( (ThreadStart) =>
        {
            try
            {
                if ( FilePreview != null && Browser.PreviewOpened )
                {
                    Console.WriteLine( "FilePreview.Open Task Start" );
                    FilePreview.Open( Browser.SelectedPath );
                    Console.WriteLine( "FilePreview.Open Task Completed" );
                }
            }
            catch ( Exception ex )
            {
                Console.WriteLine( ex.StackTrace );
            }

            Process = false;
        } );

        Preview.Start();
        if ( Preview.Join( TimeSpan.Zero ) )
        {
            DateTime dt = DateTime.Now;
            TimeSpan Interval = TimeSpan.FromMilliseconds( 50 );
            while ( Process )
            {
                if ( dt + Interval < DateTime.Now )
                {
                    Application.DoEvents();
                    dt = DateTime.Now;
                }
            }
        }

Task.Run Part =>

var t = Task.Run( () =>
        {
            try
            {
                this.Invoke( new MethodInvoker( delegate
                {
                    if ( Browser.SelectedItems.Count == 0 )
                    {
                        FilePreview.Initialize(); 
                        return;
                    }
                    else if ( Browser.SelectedItems[0].ForeColor == Color.Pink )
                    {
                        FilePreview.Initialize();
                        return;
                    }

                    if ( FilePreview != null && Browser.PreviewOpened )
                    {
                        FilePreview.Open( Browser.SelectedPath );
                    }
                } ) );
            }
            catch
            {

            }
        } );

BackgroundWorker =>

    private void PreviewWorker_DoWork( object sender, DoWorkEventArgs e )
    {
        FilePreviewHandler FilePreview = null;
        GCS.Windows.Forms.FileFolderList Browser = null;

        switch ( PreviewStatus ) {
            case BrowserStatus.Local:
                FilePreview = LocalFilePreviewHandler;
                Browser = LocalBrowser;
                break;
            case BrowserStatus.Network:
                FilePreview = NetFilePreviewHandler;
                Browser = NetworkBrowser;
                break;
        }

        if ( FilePreview == null )
        {
            return;
        }

        this.Invoke( new MethodInvoker( delegate
        {
            FilePreview.Open( Browser.SelectedPath );
        } ) );
    }

I think this part makes my program freezing.

any idea to avoid form freezing during load large files for PreviewHandler?

Arphile
  • 841
  • 6
  • 18
  • 6
    [Asynchronously Load an Image from a Url to a PictureBox](https://stackoverflow.com/a/37764953/3110834) – Reza Aghaei Jan 30 '19 at 08:26
  • 1
    @bommelding sorry, carried away. There's probably a dozens identical questions per day, asking for a way to modify the UI from another thread. Typically, it's tables, not files – Panagiotis Kanavos Jan 30 '19 at 08:28
  • 1
    @arphile - you didn't show _how_ you were using Task.Run etc. To get this right you need to get the whole process right. `async` is preferred, starting with an async void event handler and awaiting all the way down to a Task.Run() or an Image.LoadAsync() – bommelding Jan 30 '19 at 08:32
  • 1
    When you call Preview.Join() right after Preview.Start() you are still single threaded. – bommelding Jan 30 '19 at 09:15
  • 2
    A Task.Run() that does _everything_ inside an Invoke() is also single threading. – bommelding Jan 30 '19 at 09:16
  • 1
    A BackgroundWorker should also avoid Invoke() for the same reason. Invoke means 'execute on main thread' and that's what is freezing you. – bommelding Jan 30 '19 at 09:17
  • 1
    The duplicat from Reza shows some good but incomplete solutions. The tricky part is how you call al this from the main thread, ie the event that starts it all. – bommelding Jan 30 '19 at 09:20
  • 1
    If you ever code `GC.Collect()` or `Application.DoEvents()` then you are probably doing something very wrong. – Enigmativity Jan 30 '19 at 09:31
  • @bommelding Thanks for tip! – Arphile Jan 30 '19 at 23:31
  • Solved with comment tips! – Arphile Jan 31 '19 at 00:40
  • what I did was add thread and do tasks without invoke with this property – Arphile Jan 31 '19 at 00:40
  • CheckForIllegalCrossThreadCalls = false; – Arphile Jan 31 '19 at 00:40
  • `CheckForIllegalCrossThreadCalls = false` is very wrong. That's just like disabling the smoke detector. It won't stop the fire. – bommelding Jan 31 '19 at 08:04
  • @bommelding that's okay for this purpose. what I need was prevent freezing. and it doesn't make other problems. if it's required, I think this way is right way to solve the problem. – Arphile Jan 31 '19 at 08:43
  • No, you didn't solve anything. You now have a bug that is just very hard to reproduce. – bommelding Jan 31 '19 at 08:46
  • @bommelding then how can I solve this problem without using invoke? any solutions? – Arphile Jan 31 '19 at 08:58
  • 1
    See the first 1st and 3rd comments here. It's a standard problem that has been asked and answered many times. Separate your UI work from the heavy load stuff. For async, see Stephen Cleary's blog. – bommelding Jan 31 '19 at 09:31

0 Answers0