use*_*329 5 c# wpf image servicepointmanager
我想在我的WPF应用程序中显示用户Gravatar.这就是我绑定Image-Control的方式:
<Image Source="{Binding Path=Email, Converter={StaticResource GravatarConverter},IsAsync=True}">
Run Code Online (Sandbox Code Playgroud)
GravatarConverter返回给定电子邮件的URL.不幸的是,这在加载第一张图片时完全阻止了我的UI.请注意我使用"IsAsync = True".经过一些研究后,我发现在应用程序启动时在一个单独的线程中调用FindServicePoint时,我可以解决这个问题:
Task.Factory.StartNew( () => ServicePointManager.FindServicePoint( "http://www.gravatar.com", WebRequest.DefaultWebProxy ) );
Run Code Online (Sandbox Code Playgroud)
但是当我的应用程序已经下载图像时,当FindServicePoint没有完成时,这不起作用.有人可以解释为什么WPF-App完全需要这个FindServicePoint,为什么这会阻止UI以及如何避免阻塞?
谢谢
更新:事实证明,当我在Internet Explorers"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>
Run Code Online (Sandbox Code Playgroud)
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 ){}
}
}
}
Run Code Online (Sandbox Code Playgroud)
阻止 UI 发生是因为IsAsync=True仅以异步方式运行绑定进程。在您的情况下,您在转换过程中需要长时间运行的操作。为了解决这个问题,您应该创建像这样异步呈现结果的转换器(基于这个答案):
创建任务完成通知器:
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;
}
Run Code Online (Sandbox Code Playgroud)
创建实现 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;
}
}
Run Code Online (Sandbox Code Playgroud)
在 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>
Run Code Online (Sandbox Code Playgroud)
更新 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) { }
Run Code Online (Sandbox Code Playgroud)
但它的性能并不比你的变体更好。