2013-10-15 18 views
5

私のWPFアプリケーションにユーザーGravatarを表示したい。これはImage-Controlをバインドする方法です。WPFのImagecontrolがUIをフリーズする

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

ここで、GravatarConverterは指定されたEmailのURLを返します。残念ながら、これは、最初のイメージを読み込むときにUIを完全にブロックしています。私は "IsAsync = True"を使用していることに注意してください。 後、私はアプリケーションの起動時に別のスレッドでFindServicePointを呼び出すときにこの問題を回避するハックできることを発見したいくつかの研究:

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

しかし、私のアプリケーションがすでにダウンロードしている間FindServicePointが終了していない場合は、この作業されていません画像。 誰かがWPF-AppにこのFindServicePointが必要な理由、なぜこれがUIをブロックしているのか、ブロックを回避する方法を説明できますか?

おかげ

更新:それは私の問題を結局のところ、私はインターネットエクスプローラー「インターネットオプション」の「自動設定を検出」をチェックした後に消えた - >「接続」 - >「LANの設定」。

テキストボックスに画像のURLを挿入してボタンをクリックするだけで、このVERYシンプルなWPF-Applicationを使用して問題を再現しました。 「自動検出の設定」を有効にすると、画像が初めて読み込まれるときに数秒間フリーズします。このオプションを使用すると、読み込みがすぐに無効になります。

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非同期方法のみ結合プロセスにおいてのisAsync = trueを実行するため

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ブロッキング。あなたのケースでは、プロセスの変換中に長い実行中の操作があります。これはあなたが非同期的にこのような結果を提示し、コンバータを作成する必要があります解決するために(this answerに基づく):

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

} 

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実装する非同期コンバータを作成します。

タスクcomplition通知を作成します。

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

ありがとうございます。残念ながら、これはまだ私のUIを凍結しています。これを非同期的に実行すると問題はないようです。問題はこのServicePointManager.FindServicePoint呼び出しのようです。これは、リモートイメージが最初にダウンロードされたときにアプリケーションごとに1回実行されます。 – user1130329

+0

私の場合は、すべてが凍結することなく動作します。どこでServicePointManager.FindServicePointを呼び出しますか?いくつかのコードを提供できますか?また、私がIsAsync = Trueを設定していることを忠告してください。 – Deffiss

+1

私は "バグ"を見つけましたが、これは本当に奇妙です。インターネットエクスプローラのインターネット設定と関係があります。私は "設定" - > "接続" - > "LAN設定"で "自動検出設定"を有効にしました。私がそれを無効にすると、すべてが完璧にロードされます。私はそれを起動すると、フリーズします。これはOS問題と思われますか? – user1130329

関連する問題