使用WebRequest抓取图像会导致UI冻结

时间:2016-09-14 11:51:19

标签: wpf image user-interface httpwebrequest freeze

我想每隔3秒从ip相机中获取一张图像并将其放入Image控件(WPF应用程序)中。

我正在使用此代码:

DispatcherTimer定义

DispatcherTimer dispatcherTimer = new DispatcherTimer(TimeSpan.FromSeconds(3), DispatcherPriority.Background, DispatcherTimer_Tick, Application.Current.Dispatcher);

图片抓取代码

private void DispatcherTimer_Tick(object sender, EventArgs evA)
    {
        Dispatcher.Invoke(DispatcherPriority.Background,
         new Action(() =>
         {
             try
             {
                 BitmapFrame src;
                 var webRequest = (HttpWebRequest)WebRequest.Create(@"http://camera_ip/cgi-bin/video.cgi?msubmenu=jpg");
                 webRequest.Credentials = new NetworkCredential("user", "password");
                 webRequest.Proxy = null;

                 var response = (HttpWebResponse)webRequest.GetResponse();
                 var stream = response.GetResponseStream();
                 var streamReader = new StreamReader(stream);
                 src = BitmapFrame.Create(streamReader.BaseStream);

                 imageTelecamera.BeginInit();
                 imageTelecamera.Source = src;
                 imageTelecamera.EndInit();

              }
             catch (Exception ex)
             {
                 DoLogD($"Error: {ex.Message}");
             }
         }
        ));
    }

它工作正常但是当我抓取图像时,UI会冻结几毫秒。

为什么调度员在不影响用户界面的情况下不在后台工作?

1 个答案:

答案 0 :(得分:0)

你误解了调度员是什么

调度程序允许您将命令发送到GUI线程,这意味着当您的命令执行时,GUI线程暂停等待它完成。

正确使用Dispatcher是将线程特定任务发送到正确的线程来执行它们,即你的控件有一个子列表,列表只能从控件安全地编辑,所以如果你需要添加或删除一个child使用控件Dispatcher来获取控件以执行添加新的Item命令

这是一个完整的例子

<强> XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <DockPanel>
        <Button x:Name="butStart" DockPanel.Dock="Top" Click="Button_Click">start</Button>
        <TextBlock x:Name="output"/>
    </DockPanel>
</Window>

代码背后:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        timer.Interval = new TimeSpan(0, 0, 3);
        timer.Tick += Timer_Tick;
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
        //start taking photo
        Task.Run(()=> TakePhoto() );
    }

    public DispatcherTimer timer { get; } = new DispatcherTimer();

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if(timer.IsEnabled)
        {
            timer.Stop();
            butStart.Content = "Start";

            //this works as you are in the main thread
            output.Text += "Time Stopped" + Environment.NewLine;
        }
        else
        {
            timer.Start();
            butStart.Content = "Stop";
            output.Text += "Time Started" + Environment.NewLine;
        }
    }
    public async void TakePhoto()
    {
        //with out the dispatcher this would error as you are not in the Gui thread so have no access to the Text property
        output.Dispatcher.Invoke(()=>output.Text += "Take Photo" + Environment.NewLine);
        //Simulate a long running task
        await Task.Delay(5000);
        output.Dispatcher.Invoke(()=>output.Text += "Take Photo - Complete" + Environment.NewLine);
    }
}

现已应用于您的代码

private void DispatcherTimer_Tick(object sender, EventArgs evA)
{
    Task.Run() =>
     {
         try
         {
             BitmapFrame src;
             var webRequest = (HttpWebRequest)WebRequest.Create(@"http://camera_ip/cgi-bin/video.cgi?msubmenu=jpg");
             webRequest.Credentials = new NetworkCredential("user", "password");
             webRequest.Proxy = null;

             var response = (HttpWebResponse)webRequest.GetResponse();
             var stream = response.GetResponseStream();
             var streamReader = new StreamReader(stream);
             src = BitmapFrame.Create(streamReader.BaseStream);

             imageTelecamera.Dispatcher.Invoke(()=>
             {
                 imageTelecamera.BeginInit();//not actually required
                 imageTelecamera.Source = src;
                 imageTelecamera.EndInit();//not actually required
             });
          }
         catch (Exception ex)
         {
             DoLogD($"Error: {ex.Message}");
         }
     }
    ));
}