为 WPF Image 类提供初始图像占位符

发布于 2024-11-28 19:52:55 字数 4138 浏览 2 评论 0原文

当我运行该项目时,出现运行时错误: 错误:必须设置属性“UriSource”或属性“StreamSource”。 因为 this.ImageUri 为 null ,我不知道为什么 this.ImageUri 为 null !帮助我

我一直在使用 WPF ListBox 使用图像作为我的列表框项目。源图像路径指向托管这些图像的服务器。在快速网络上,图像显示没有任何明显的延迟。然而,通过缓慢的链接,用户体验明显下降,我真的想在下载和解码图像时显示占位符图像。

令人惊讶的是,我在博客圈中没有找到此问题的解决方案,因此我编写了一个派生类来解决此问题。

下面的示例 XAML 来自我的项目容器样式。我用本地类实现 local:ImageLoader 替换了 Image。

<Window.Resources>
<DataTemplate DataType="{x:Type local:MyData}">
...
<StackPanel Grid.Column="0" Margin="5">
<Border BorderThickness="0">
<MyControl:ImageLoader Width="50" Height="50" ImageUri="{Binding Path=profile_image_url_https, FallbackValue=profile_image_url_https}" InitialImage="/MyProject;component/Images/nopic.png"  HorizontalAlignment="Left"></imgz:ImageLoader>
</Border>
</StackPanel>
...
</DataTemplate>
</Window.Resources>

<Grid>
<ListBox ItemsSource="{Binding Source = {StaticResource MyData}}"    />
</Grid>

初始图像处理的核心是 OnLoaded() 方法,其中我使用 BitmapImage 作为源并将 UriSource 设置为派生类的 ImageUri 依赖属性,这允许数据绑定。当下载完成或收到失败事件时,初始映像将更新为实际映像。该类还可以选择允许您指定“LoadFailedImage”。

public class ImageLoader : Image
{
    public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
        "ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(null, null));

    private BitmapImage loadedImage;

    public ImageLoader()
    {
        this.Loaded += this.OnLoaded;
    }

    public string LoadFailedImage
    {
        get;
        set;
    }

    public Uri ImageUri
    {
        get {return this.GetValue(ImageUriProperty) as Uri;}
        set {this.SetValue(ImageUriProperty, value);}
    }

    public string InitialImage
    {
        get;
        set;
    }

    private new ImageSource Source
    {
        get {return base.Source;}
        set {base.Source = value;}
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        // Loading the specified image            
        this.loadedImage = new BitmapImage();
        this.loadedImage.BeginInit();
        this.loadedImage.CacheOption = BitmapCacheOption.OnDemand;
        this.loadedImage.DownloadCompleted += this.OnDownloadCompleted;
        this.loadedImage.DownloadFailed += this.OnDownloadFailed;
        this.loadedImage.UriSource = this.ImageUri;
        this.loadedImage.EndInit();

        // The image may be cached, in which case we will not use the initial image
        if (!this.loadedImage.IsDownloading)
        {
            this.Source = this.loadedImage;
        }
        else
        {
            // Create InitialImage source if path is specified
            if (!string.IsNullOrWhiteSpace(this.InitialImage))
            {
                BitmapImage initialImage = new BitmapImage();

                // Load the initial bitmap from the local resource
                initialImage.BeginInit();
                initialImage.UriSource = new Uri(this.InitialImage, UriKind.Relative);
                initialImage.DecodePixelWidth = (int)this.Width;
                initialImage.EndInit();

                // Set the initial image as the image source
                this.Source = initialImage;                
            }
        }

        e.Handled = true;
    }

    private void OnDownloadFailed(object sender, ExceptionEventArgs e)
    {
        if (!string.IsNullOrWhiteSpace(this.LoadFailedImage))
        {
            BitmapImage failedImage = new BitmapImage();

            // Load the initial bitmap from the local resource
            failedImage.BeginInit();
            failedImage.UriSource = new Uri(this.LoadFailedImage, UriKind.Relative);
            failedImage.DecodePixelWidth = (int)this.Width;
            failedImage.EndInit();
            this.Source = failedImage;
        }
    }

    private void OnDownloadCompleted(object sender, EventArgs e)
    {
        this.Source = this.loadedImage;
    }
}

当我运行该项目时,发生运行时错误: 错误:必须设置属性“UriSource”或属性“StreamSource”。 因为 this.ImageUri 为 null ,我不知道为什么 this.ImageUri 为 null !帮助我

When i run the project a runtime error ocure:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me

I have been working with the WPF ListBox using images as my list box items. The sourced image path points to a server hosting those images. While on fast network, the images appeared without any noticeable delay. However it became apparent over a slow link that the user experience degraded and I really wanted to show a placeholder image while the image was downloaded and decoded.

Surprisingly, I didn't find a solution in the blogosphere for this issue so I coded up a derived class to address this.

The sample XAML below is from my item container style. I replaced Image with my local class implementation local:ImageLoader.

<Window.Resources>
<DataTemplate DataType="{x:Type local:MyData}">
...
<StackPanel Grid.Column="0" Margin="5">
<Border BorderThickness="0">
<MyControl:ImageLoader Width="50" Height="50" ImageUri="{Binding Path=profile_image_url_https, FallbackValue=profile_image_url_https}" InitialImage="/MyProject;component/Images/nopic.png"  HorizontalAlignment="Left"></imgz:ImageLoader>
</Border>
</StackPanel>
...
</DataTemplate>
</Window.Resources>

<Grid>
<ListBox ItemsSource="{Binding Source = {StaticResource MyData}}"    />
</Grid>

The heart of the handling for the initial image is in the OnLoaded() method, where I use a BitmapImage as the source and set the UriSource to the derived class' ImageUri dependency property, which allows for data binding. The initial image is updated to the actual image when the download completes or when a failure event is received. The class also optionally allows you to specify a "LoadFailedImage".

public class ImageLoader : Image
{
    public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
        "ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(null, null));

    private BitmapImage loadedImage;

    public ImageLoader()
    {
        this.Loaded += this.OnLoaded;
    }

    public string LoadFailedImage
    {
        get;
        set;
    }

    public Uri ImageUri
    {
        get {return this.GetValue(ImageUriProperty) as Uri;}
        set {this.SetValue(ImageUriProperty, value);}
    }

    public string InitialImage
    {
        get;
        set;
    }

    private new ImageSource Source
    {
        get {return base.Source;}
        set {base.Source = value;}
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        // Loading the specified image            
        this.loadedImage = new BitmapImage();
        this.loadedImage.BeginInit();
        this.loadedImage.CacheOption = BitmapCacheOption.OnDemand;
        this.loadedImage.DownloadCompleted += this.OnDownloadCompleted;
        this.loadedImage.DownloadFailed += this.OnDownloadFailed;
        this.loadedImage.UriSource = this.ImageUri;
        this.loadedImage.EndInit();

        // The image may be cached, in which case we will not use the initial image
        if (!this.loadedImage.IsDownloading)
        {
            this.Source = this.loadedImage;
        }
        else
        {
            // Create InitialImage source if path is specified
            if (!string.IsNullOrWhiteSpace(this.InitialImage))
            {
                BitmapImage initialImage = new BitmapImage();

                // Load the initial bitmap from the local resource
                initialImage.BeginInit();
                initialImage.UriSource = new Uri(this.InitialImage, UriKind.Relative);
                initialImage.DecodePixelWidth = (int)this.Width;
                initialImage.EndInit();

                // Set the initial image as the image source
                this.Source = initialImage;                
            }
        }

        e.Handled = true;
    }

    private void OnDownloadFailed(object sender, ExceptionEventArgs e)
    {
        if (!string.IsNullOrWhiteSpace(this.LoadFailedImage))
        {
            BitmapImage failedImage = new BitmapImage();

            // Load the initial bitmap from the local resource
            failedImage.BeginInit();
            failedImage.UriSource = new Uri(this.LoadFailedImage, UriKind.Relative);
            failedImage.DecodePixelWidth = (int)this.Width;
            failedImage.EndInit();
            this.Source = failedImage;
        }
    }

    private void OnDownloadCompleted(object sender, EventArgs e)
    {
        this.Source = this.loadedImage;
    }
}

When i run the project a runtime error ocured:
Error: Property 'UriSource' or property 'StreamSource' must be set.
because this.ImageUri is null , i don't know why this.ImageUri be null ! help me

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

靑春怀旧 2024-12-05 19:52:55

如果不是InitialImage="/MyProject;component/Images/nopic.png"中的分号拼写错误,

也许最好将您的 InitialImage 设置为 ImageUri 中的默认值

 public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
    "ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(new Uri("/MyProject/component/Images/nopic.png"), null));

更新:

您必须绑定到Image.Source,并且可以使用PriorityBinding来显示占位符。

<Image.Source>
    <PriorityBinding>
        <!--highest priority sources are first in the list-->
        <Binding Path="YourImageUri"
           IsAsync="True" />
        <Binding Path="InitialImageUri"
           IsAsync="True" />
    </PriorityBinding>
</Image.Source>

对于“LoadFailedImage”,将订阅 Image.ImageFailed 事件。

希望这有帮助。

If it isn't the semicolon typo in InitialImage="/MyProject;component/Images/nopic.png",

maybe it's better to set your InitialImage as Default in ImageUri

 public static readonly DependencyProperty ImageUriProperty = DependencyProperty.Register(
    "ImageUri", typeof(Uri), typeof(ImageLoader), new PropertyMetadata(new Uri("/MyProject/component/Images/nopic.png"), null));

UPDATE:

You have to bind to Image.Source and you could use PriorityBinding to show a placeholder.

<Image.Source>
    <PriorityBinding>
        <!--highest priority sources are first in the list-->
        <Binding Path="YourImageUri"
           IsAsync="True" />
        <Binding Path="InitialImageUri"
           IsAsync="True" />
    </PriorityBinding>
</Image.Source>

For a "LoadFailedImage" a would subsribe to Image.ImageFailed Event.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文