2013-10-15 112 views
5

我想在我的WPF應用中顯示用戶的Gravatar。這是我如何綁定圖像控件:WPF的Imagecontrol凍結UI

<Image Source="{Binding Path=Email, Converter={StaticResource GravatarConverter},IsAsync=True}"> 

其中GravatarConverter返回給定電子郵件的URL。不幸的是,當加載第一張圖片時,這完全阻止了我的UI。請注意,我正在使用「IsAsync = True」。 經過一番研究,我發現我可以在應用程序啓動時在一個單獨的線程中調用FindServicePoint時解決此問題破解:

 Task.Factory.StartNew(() => ServicePointManager.FindServicePoint("http://www.gravatar.com", WebRequest.DefaultWebProxy)); 

但這個時候,而我的應用程序已經下載的FindServicePoint沒有完成不工作圖片。 是否有人可以解釋爲什麼一個WPF的應用程序需要這個FindServicePoint可言,這是爲什麼阻塞UI,以及如何避免阻塞?

感謝

更新:事實證明我的問題消失後,我未選中「自動檢測設置」,在互聯網探索者「Internet選項」 - >「連接」 - >「局域網設置」。

我用這個非常簡單的WPF的應用程序只需插入一個URL在文本框的圖像重現該問題,並單擊按鈕。啓用「自動檢測設置」後,圖像首次加載時,應用程序會凍結數秒。使用此選項可立即禁用其加載。

MainWindow.xaml

<Window x:Class="WpfGravatarFreezeTest.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"> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*" /> 
     <ColumnDefinition Width="Auto" /> 
    </Grid.ColumnDefinitions> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto" /> 
     <RowDefinition Height="*" /> 
    </Grid.RowDefinitions> 
    <TextBox Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" x:Name="tbEmail" /> 
    <Button Grid.Column="0" Grid.Row="0" Click="buttonLoad_OnClick" HorizontalAlignment="Right">Set Source</Button> 
    <Image x:Name="img" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" /> 
</Grid>   

MainWindow.xaml.cs

using System; 
using System.Windows; 
using System.Windows.Media.Imaging; 

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

     private void buttonLoad_OnClick(object sender, RoutedEventArgs e) 
     { 
      try { this.img.Source = new BitmapImage(new Uri(this.tbEmail.Text)); } 
      catch(Exception){}    
     } 
    } 
} 

回答

2

阻斷UI happans因爲IsAsync =真在異步方式運行只裝訂處理。在您的情況下,您在轉換過程中需要長時間運行。爲了解決這個問題,你應該創建轉換器呈現的結果異步這樣的(基於this answer):

創建任務complition通知:

public sealed class TaskCompletionNotifier<TResult> : INotifyPropertyChanged 
{ 
    public TaskCompletionNotifier(Task<TResult> task) 
    { 
     Task = task; 
     if (task.IsCompleted) return; 
     task.ContinueWith(t => 
     { 
      var temp = PropertyChanged; 
      if (temp != null) 
      { 
       temp(this, new PropertyChangedEventArgs("Result")); 
      } 
     }); 
    } 

    // Gets the task being watched. This property never changes and is never <c>null</c>. 
    public Task<TResult> Task { get; private set; } 

    // Gets the result of the task. Returns the default value of TResult if the task has not completed successfully. 
    public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); } } 

    public event PropertyChangedEventHandler PropertyChanged; 

} 

創建異步轉換器實現MarkupExtention:

public class ImageConverter: MarkupExtension, IValueConverter 
{ 

    public ImageConverter() 
    { 
    } 

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value == null) return new BitmapImage(); 
     var task = Task.Run(() => 
     { 
      Thread.Sleep(5000); // Perform your long running operation and request here 
      return value.ToString(); 
     }); 

     return new TaskCompletionNotifier<string>(task); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    public override object ProvideValue(IServiceProvider serviceProvider) 
    { 
     return this; 
    } 

} 

使用它在XAML:

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="Auto"/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 

    <TextBox x:Name="uri" Grid.Row="0" Text="{Binding ImageUri, ElementName=main}"/> 
    <Image Grid.Row="1" DataContext="{Binding Text, ElementName=uri, Converter={local:ImageConverter}}" Source="{Binding Path=Result, IsAsync=True}"/> 

</Grid> 

更新2 好像圖片控制負載的圖像異步本身。你是對的第一次加載需要很多時間。您可以使用這樣的代碼:

try 
    { 
     var uri = Uri.Text; 
     var client = new WebClient(); 
     var stream = await client.OpenReadTaskAsync(uri); 
     var source = new BitmapImage(); 
     source.BeginInit(); 
     source.StreamSource = stream; 
     source.EndInit(); 
     Img.Source = source; 


    } 
    catch (Exception) { } 

但其性能比豈不等於你的變型更好。

+0

非常感謝你。不幸的是,這仍然凍結了我的用戶界面。看起來,如果我異步執行此操作並不重要 - 問題似乎是此ServicePointManager.FindServicePoint調用在第一次下載遠程圖像時會爲每個應用程序執行一次。 – user1130329

+0

在我的情況下,所有的作品都沒有凍結。你在哪裏調用ServicePointManager.FindServicePoint?你能提供一些代碼嗎?另外支付attantion,我設置IsAsync = True。 – Deffiss

+1

好吧,我發現了「bug」 - 但這真的很奇怪:它與Internet Explorer中的Internet設置有關。我在「設置」 - >「連接」 - >「局域網設置」中激活了「自動檢測設置」。當我禁用它時,所有的東西都加載完美,當我激活它時會凍結。所以這似乎是一個操作系統問題? – user1130329