2013-10-15 10 views
5

Chcę wyświetlić użytkownika Gravatar w mojej aplikacji WPF. W ten sposób wiążę kontrolę obrazu:Imagecontrol WPF zamarza UI

Gdzie GravatarConverter zwraca adres URL dla podanego adresu e-mail. Niestety to całkowicie blokuje mój interfejs podczas ładowania pierwszego obrazu. Zwróć uwagę, że używam "IsAsync = True". Po kilku badań okazało się, że mogę włamać się wokół tej kwestii podczas wywoływania FindServicePoint w osobnym wątku na starcie aplikacji:

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

Ale to nie działa, gdy FindServicePoint nie jest zakończona, gdy moja aplikacja jest już Pobieranie obraz. Czy ktoś może wyjaśnić, dlaczego aplikacja WPF w ogóle potrzebuje tego FindServicePoint, dlaczego blokuje interfejs użytkownika i jak uniknąć blokowania?

Dzięki

Aktualizuj: Jak się okazuje mój problem zniknął po I niezaznaczone "Automatyczne wykrywanie ustawień" w Internet Explorers "Opcje internetowe" -> "Połączenia" -> "Ustawienia sieci LAN".

Użyłem tej BARDZO prostej aplikacji WPF do odtworzenia problemu przez wstawienie adresu URL do obrazu w polu tekstowym i kliknięcie przycisku. Po włączeniu opcji "Automatyczne wykrywanie ustawień" aplikacja zatrzymuje się na kilka sekund po pierwszym załadowaniu obrazu. Z tą opcją natychmiast wyłączyło ładowanie.

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){}    
     } 
    } 
} 

Odpowiedz

2

Blokowanie happans interfejsu ponieważ IsAsync = True przebiega w sposób asynchroniczny tylko proces wiązania. W twoim przypadku masz długą operację podczas procesu konwersji. Aby rozwiązać ten problem należy utworzyć konwerter, który przedstawia wynik asynchronicznie tak (na podstawie this answer):

Tworzenie complition zadanie zgłaszającego:

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; 

} 

Tworzenie konwertera transmisji asynchronicznej realizacji 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; 
    } 

} 

Użyj go w 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> 

Aktualizacja 2 Wygląda Obraz load control asynchronicznie sama. Masz rację, pierwsze ładowanie zajmuje dużo czasu. Możesz użyć następującego kodu:

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) { } 

Ale jego działanie nie jest lepsze niż Twój wariant.

+0

Dziękuję bardzo.Niestety to wciąż blokuje mój interfejs użytkownika. Wygląda na to, że nie ma znaczenia, czy wykonam to asynchronicznie - problem wydaje się być wywołaniem ServicePointManager.FindServicePoint, które jest wykonywane raz na aplikację przy pierwszym pobraniu zdalnego obrazu. – user1130329

+0

W moim przypadku wszystkie prace bez zamrażania. Gdzie nazywasz ServicePointManager.FindServicePoint? Czy możesz podać kod? Zwróć też uwagę, że ustawiłem IsAsync = True. – Deffiss

+1

Okay Znalazłem "błąd" - ale to naprawdę dziwne: ma to związek z ustawieniami internetowymi w Internet Explorerze. Miałem "Automatyczne wykrywanie ustawień" w "Ustawienia" -> "Połączenia" -> "Ustawienia Lan" aktywowane. Kiedy go dezaktywuję, wszystko ładuje się idealnie, kiedy aktywuję, zamarza. Czy to wydaje się być problemem z systemem operacyjnym? – user1130329