2013-10-15 9 views
5

Desidero visualizzare un utente Gravatar nella mia applicazione WPF. Questo è come mi legano l'immagine-Control:Imagecontrol di WPF blocca l'interfaccia utente

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

Dove GravatarConverter restituisce l'URL per il data-mail. Sfortunatamente questo sta bloccando completamente la mia interfaccia utente durante il caricamento della prima immagine. Si noti che sto usando "IsAsync = True". Dopo alcune ricerche ho scoperto che posso incidere risolvere questo problema quando si chiama FindServicePoint in un thread separato sulle applicazioni all'avvio:

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

Ma questo non funziona quando FindServicePoint non è finito mentre la mia applicazione è già scaricando un Immagine. Qualcuno può spiegare perché un'app WPF ha bisogno di questo FindServicePoint, perché questo blocca l'interfaccia utente e come evitare il blocco?

Grazie

Aggiornamento: Come si è visto il mio problema è scomparso dopo aver deselezionato "rilevare automatico impostazioni" in Internet Explorers "Opzioni Internet" -> "Connessioni" -> "Impostazioni LAN".

Ho usato questa semplice applicazione WPF per riprodurre il problema semplicemente inserendo un URL per un'immagine nella casella di testo e facendo clic sul pulsante. Con "Impostazioni di rilevamento automatico" attivata l'applicazione si blocca per alcuni secondi la prima volta che viene caricata un'immagine. Con questa opzione disabilitato il caricamento immediato.

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

risposta

2

Blocco happans UI perché isAsync = true viene eseguito in maniera asincrona unico processo vincolante. Nel tuo caso hai una lunga esecuzione durante il processo di conversione. Per risolvere questo problema si dovrebbe creare convertitore che presenta il risultato in modo asincrono come questo (sulla base degli this answer):

Crea attività complition notificante:

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; 

} 

Creare convertitore asincrono attuazione 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; 
    } 

} 

Utilizzare in 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> 

Update 2 Sembra come Immagine immagini di carico di controllo in modo asincrono per sé. Hai ragione, il primo carico richiede molto tempo. Puoi usare un codice come questo:

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

Ma la sua prestazione non è migliore della tua variante.

+0

Grazie mille.Sfortunatamente questo sta ancora congelando la mia interfaccia utente. Sembra che non importi se eseguo questo in modo asincrono - il problema sembra essere questa chiamata ServicePointManager.FindServicePoint che viene eseguita una volta per app la prima volta che viene scaricata un'immagine remota. – user1130329

+0

Nel mio caso tutto funziona senza congelamento. Dove chiami ServicePointManager.FindServicePoint? Puoi fornire un codice? Paga anche la digitazione che ho impostato IsAsync = True. – Deffiss

+1

Ok ho trovato il "bug" - ma questo è davvero strano: ha a che fare con le impostazioni Internet in Internet Explorer. Ho avuto "Impostazioni di rilevamento automatico" in "Impostazioni" -> "Connessioni" -> "Impostazioni Lan" attivate. Quando lo disattivo tutto diventa perfetto, quando lo attivo si blocca. Quindi questo sembra essere un problema del sistema operativo? – user1130329

Problemi correlati