3

我正在搞乱一些 Windows Phone 开发,因为我是来自 Android 背景的新手。

我正在使用“theCatAPI”加载一张猫的随机图片并显示它,然后当单击图片或屏幕底部的按钮时,图像会刷新为新的。

到目前为止,我有以下内容:

XAML:

<Page
    x:Class="CatFactsPics.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CatFactsPics"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid x:Name="LayoutRoot">

        <Grid.ChildrenTransitions>
            <TransitionCollection>
                <EntranceThemeTransition/>
            </TransitionCollection>
        </Grid.ChildrenTransitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- TitlePanel -->
        <StackPanel Grid.Row="0" Margin="24,17,0,28">
            <TextBlock Text="My Application" Style="{ThemeResource TitleTextBlockStyle}" Typography.Capitals="SmallCaps"/>
            <TextBlock Text="page title" Margin="0,12,0,0" Style="{ThemeResource HeaderTextBlockStyle}"/>
        </StackPanel>

        <!--TODO: Content should be placed within the following grid-->
        <Grid Grid.Row="1" x:Name="ContentRoot">
            <Image HorizontalAlignment="Center" Stretch="UniformToFill" VerticalAlignment="Center" x:Name="KittyPic" Tapped="KittyPic_Tapped"/>
            <Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
        </Grid>
    </Grid>
</Page>

在 page.cs 中:

...
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            this.navigationHelper.OnNavigatedTo(e);

            Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
            KittyPic.Source = new BitmapImage(myUri);
        }
...
        private void newPic_Click(object sender, RoutedEventArgs e)
        {
            Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
            BitmapImage bmi = new BitmapImage();
            bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
            bmi.UriSource = myUri;
            KittyPic.Source = bmi;
        }

我有一些问题:

1)这是正确的做事方式吗?在 Android 中,我会尝试异步执行操作以避免停止 UI 线程。话虽如此,我似乎对现在的事情没有任何问题。我不熟悉 Windows 的做事方式,也没有找到任何资源对此提供任何解释或建议。

2) 在显示新图片时存在延迟,导致在新图片重新出现之前,图片视图变黑的时间很短(几秒)。有没有一种方法可以设置它,以便旧图片保留直到新图片物理准备好显示,或者显示占位符“加载”图像,直到新图片可以替换它。

任何其他关于如何做事的建议或技巧都会很棒,谢谢。

4

2 回答 2

5

1)使用您当前的代码,您不会阻塞 UI 线程,因为您正在URIUI 线程上设置,但图像的实际加载是在另一个线程上自动完成的。(对于手动下载图像、字符串等,您可能会使用async/await来避免锁定 UI 线程)。

2)图像变黑,因为您ImageSource在新图像加载之前更改了。正如你提到的,有几种方法可以解决这个问题。但对于大多数人来说,共同点是您将希望在 Image 控件上使用ImageOpenedandImageFailed事件,该事件会在图像加载完成(或发生错误,例如没有互联网连接)时触发。这是一个在加载时显示加载栏的示例,它只是隐藏/显示加载进度:

page.xaml文件中:

<Grid Grid.Row="1" x:Name="ContentRoot">
    <Image x:Name="KittyPic" Tapped="KittyPic_Tapped" ImageOpened="KittyPic_ImageOpened" ImageFailed="KittyPic_ImageFailed" />
    <StackPanel x:Name="LoadingPanel" VerticalAlignment="Center">
        <ProgressBar IsIndeterminate="True" IsEnabled="True" />
        <TextBlock Text="Loading image..." HorizontalAlignment="Center" />
    </StackPanel>
</Grid>

并在 page.xaml.cs 文件中

private void KittyPic_Tapped(object sender, TappedRoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Visible;
    Uri myUri = new Uri("http://thecatapi.com/api/images/get?format=src&type=jpg", UriKind.Absolute);
    BitmapImage bmi = new BitmapImage();
    bmi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
    bmi.UriSource = myUri;
    KittyPic.Source = bmi;
}

private void KittyPic_ImageOpened(object sender, RoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Collapsed;
}

private async void KittyPic_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
    LoadingPanel.Visibility = Visibility.Collapsed;
    await new MessageDialog("Failed to load the image").ShowAsync();
}

而不是TextBlockandProgressBar你当然可以显示你想要的任何东西,或者例如在两个图像之间交换以继续显示旧图像。

对于其他建议,我认为当您习惯了基础知识时,请查看非常有用且功能强大的数据绑定。还可以看看这篇关于MVVM 模式的文章。

于 2014-04-24T13:32:14.330 回答
2

1)这是正确的做事方式吗?

不,这不对。您绝对应该采用 MVVM 模式并将您的业务逻辑从您的视图中分离出来——这意味着您不应该在视图的代码隐藏中创建位图图像或请求/分配此类远程图像 URL。您应该在 ViewModel 中执行此类操作并将它们绑定到您的 View。

因此,在您的情况下,您的 ViewModel(实现 INotifyPropertyChanged)中将有一个 Uri(或您将分配远程 URL 的字符串)属性,然后在您的 View 中您将像这样绑定它:

<!--TODO: Content should be placed within the following grid-->
    <Grid Grid.Row="1" x:Name="ContentRoot">
         <BitmapImage UriSource="{Binding BackGroundImage}" CreateOptions="BackgroundCreation" />
        <Button HorizontalAlignment="Center" VerticalAlignment="Bottom" x:Name="newPic" Click="newPic_Click" >New Kitty</Button>
    </Grid>

每当您设置 BackGroundImage 属性时,您都会引发一个名为的事件; RaisePropertyChanged("BackGroundImage")-> 这是经典的 MVVM 方法。

这样您的视图就会知道 BackGroundImage 已更改并自动加载它。(但请注意,如果您只是为此 BackGroundImage 提供一个字符串 - 您将不得不使用一个转换器,一个字符串到 Uri 转换器,因为它只接受远程图像的 Uri)

2)“......有没有办法设置它,以便旧图片保留直到新图片物理准备好显示,或者显示占位符“加载”图像,直到新图片可以替换它。“

我建议显示“正在加载”图像。我遇到了与您在这里遇到的完全相同的问题,我的解决方法是插入加载图像并将其不透明度值与实际图像一起设置为 0.1。当您在远程 URL 之间切换时,当前一个图像消失时,会出现不透明的加载图像,而当加载下一个实际图像时,加载图像不会显示,因为新图像正在覆盖它。

于 2014-04-28T15:01:32.053 回答