NoWar
NoWar

Reputation: 37632

WPF Application freezes on moving

I just did a simple application to get a screenshot periodically.

The main issue I have got is that application freezes when I move it.

So the main goal is to take out this influence of the screenshot, threads and etc.

I put here all code and it works so u can reproduce it.

Here is some .NET Profiling info of this code.

enter image description here

enter image description here

Any clue how do I can fix it?

XAML

<Window x:Class="Screenshot.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" >
    <Grid Height="Auto">
        <Image Name="Image1"/>
    </Grid>
</Window>

C#

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();            
        }

        ScreenGrabber grabber;

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            grabber = new ScreenGrabber(5);
            grabber.Changed += new ChangedEventHandler(grabber_Changed);
        }

        void grabber_Changed(object sender, EventArgs e)
        {
            Image1.Dispatcher.Invoke(new Action(() => {
                BitmapSource bs = ((ScreenGrabber)sender).GetImage();
                Image1.Width = bs.Width;
                Image1.Height = bs.Height;
                Image1.Source = bs;
            } ));
        } 
    }

C# DLL

namespace MyScreenGrabber
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

    public class ScreenGrabber : Window
    {
        public event ChangedEventHandler Changed;

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        byte[] BitmapData { set; get; }

        int Interval { set; get; }

        DispatcherTimer Timer { set; get; }

        public ScreenGrabber(int interval)
        {
            Interval = interval;
            Timer = new DispatcherTimer();
            Timer.Interval = new TimeSpan(0, 0, Interval);
            Timer.Tick += new EventHandler(Timer_Tick);
            Timer.Start();
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            WindowInteropHelper windowInteropHelper = windowInteropHelper = new WindowInteropHelper(this);
            Screen screen = Screen.FromHandle(windowInteropHelper.Handle);

            using (MemoryStream ms = new MemoryStream())
            {
                if (screen != null)
                {
                    using (Bitmap bitmap = new Bitmap(screen.Bounds.Size.Width, screen.Bounds.Size.Height))
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            g.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size, CopyPixelOperation.SourceCopy);
                        }
                        ImageCodecInfo myImageCodecInfo;
                        myImageCodecInfo = GetEncoderInfo("image/jpeg");
                        System.Drawing.Imaging.Encoder myEncoder;
                        myEncoder = System.Drawing.Imaging.Encoder.Quality;
                        EncoderParameters encoderParameters = new EncoderParameters();
                        EncoderParameter encoderParameter = new EncoderParameter(myEncoder, 25L);
                        encoderParameters.Param[0] = encoderParameter;
                        bitmap.Save(ms, myImageCodecInfo, encoderParameters);
                        BitmapData = ms.ToArray();
                        OnChanged(EventArgs.Empty);
                    }
                }
            }
        }

        static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        public BitmapSource GetImage()
        {
            using (MemoryStream ms = new MemoryStream(this.BitmapData))
            {
                var decoder = BitmapDecoder.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                return decoder.Frames[0];
            }
        }
    }
}

Optimized code:

namespace MyScreenGrabber
{
    public delegate void ChangedEventHandler(object sender, EventArgs e);

    public class ScreenGrabber : Window
    {
        public event ChangedEventHandler Changed;

        protected virtual void OnChanged(EventArgs e)
        {
            if (Changed != null)
                Changed(this, e);
        }

        byte[] BitmapData { set; get; }

        int Interval { set; get; }

        WindowInteropHelper windowInteropHelper;
        Screen screen;

        DispatcherTimer Timer { set; get; }

        BackgroundWorker worker = new BackgroundWorker();

        public ScreenGrabber(int interval)
        {
            Interval = interval;

            windowInteropHelper = windowInteropHelper = new WindowInteropHelper(this);
            screen = Screen.FromHandle(windowInteropHelper.Handle);

            isDone = true;

            Timer = new DispatcherTimer();
            Timer.Interval = new TimeSpan(0, 0, Interval);
            Timer.Tick += new EventHandler(Timer_Tick);
            Timer.Start();

            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }

        void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            OnChanged(EventArgs.Empty);
            isDone = true;
        }

        bool isDone;

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            GetScreenshot();
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            if (isDone)
            {
                isDone = false;
                worker.RunWorkerAsync();
            }
        }

        void GetScreenshot()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                if (screen != null)
                {
                    using (Bitmap bitmap = new Bitmap(screen.Bounds.Size.Width, screen.Bounds.Size.Height))
                    {
                        using (Graphics g = Graphics.FromImage(bitmap))
                        {
                            g.CopyFromScreen(screen.Bounds.X, screen.Bounds.Y, 0, 0, screen.Bounds.Size, CopyPixelOperation.SourceCopy);
                        }
                        ImageCodecInfo myImageCodecInfo;
                        myImageCodecInfo = GetEncoderInfo("image/jpeg");
                        System.Drawing.Imaging.Encoder myEncoder;
                        myEncoder = System.Drawing.Imaging.Encoder.Quality;
                        EncoderParameters encoderParameters = new EncoderParameters();
                        EncoderParameter encoderParameter = new EncoderParameter(myEncoder, 25L);
                        encoderParameters.Param[0] = encoderParameter;
                        bitmap.Save(ms, myImageCodecInfo, encoderParameters);
                        BitmapData = ms.ToArray();
                    }
                }
            }
        }

        static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        public BitmapSource GetImage()
        {
            using (MemoryStream ms = new MemoryStream(this.BitmapData))
            {
                var decoder = BitmapDecoder.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
                return decoder.Frames[0];
            }
        }
    }
}

Upvotes: 0

Views: 1628

Answers (1)

Nikola Sivkov
Nikola Sivkov

Reputation: 2852

I believe you can avoid this by using a background worker process to execute the function that takes the screenshot.

As the background worker uses another thread and the main thread continues to render the UI , it should not get stuck.

EDIT :// I found this question on SO that might clarify things on Background Workers VS Delegates

Good luck!

Upvotes: 2

Related Questions