异步显示来自互联网的图像

时间:2017-09-19 12:36:13

标签: xaml uwp

在我的UWP应用程序中,我有ListView增量加载。现在我还需要在ListViewItem中添加一个图像。我试着直接给它URI。

其中ThumbnailImage只是我视图模型中的一个字符串。这个问题是因为增量加载,当我在前一个图像仍在加载时向下滚动一点时,应用程序就会挂起。然而,如果我让所有图像出现然后向下滚动,它就可以正常工作。

我还尝试过该工具包中的ImageEx控件,它也有同样的问题。

我做了一些搜索,我在XAML中找到的唯一的东西是doing IsAsync = True。但似乎UWP中没有IsAsync

我的ViewModel:

public class MyViewModel
{
    ...   
    public string ThumbnailImage { get; set; }
    ...
}

我的XAML ListView会逐渐填充

<ListView  Grid.Row="0"            
        SelectionMode="Single"
        IsItemClickEnabled="True"
        ItemClick="SearchResultListView_ItemClick" >

    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderBrush" Value="Gray"/>
            <Setter Property="BorderThickness" Value="0, 0, 0, 1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListViewItem">
                        <ListViewItemPresenter
                            ContentTransitions="{TemplateBinding ContentTransitions}"
                            SelectionCheckMarkVisualEnabled="True"
                            CheckBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                            CheckBoxBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                            DragBackground="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
                            DragForeground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
                            FocusBorderBrush="{ThemeResource SystemControlForegroundAltHighBrush}"
                            FocusSecondaryBorderBrush="{ThemeResource SystemControlForegroundBaseHighBrush}"
                            PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
                            PointerOverBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            SelectedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            SelectedForeground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            SelectedPointerOverBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            PressedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            SelectedPressedBackground="{ThemeResource SystemControlDisabledTransparentBrush}"
                            DisabledOpacity="{ThemeResource ListViewItemDisabledThemeOpacity}"
                            DragOpacity="{ThemeResource ListViewItemDragThemeOpacity}"
                            ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                            ContentMargin="{TemplateBinding Padding}"
                            CheckMode="Inline"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListView.ItemContainerStyle>

    <ListView.ItemTemplate>  
        <DataTemplate x:DataType="viewModel:MyViewModel">
            <UserControl >
                <Grid Style="{StaticResource SomeStyle}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="VisualStateGroup">
                            <VisualState x:Name="VisualStatePhone">
                                <VisualState.StateTriggers>
                                    <AdaptiveTrigger MinWindowWidth="0"/>
                                </VisualState.StateTriggers>
                                <VisualState.Setters>
                                    <Setter Target="ThumbnailImage.Width" Value="50"/>
                                    <Setter Target="ThumbnailImage.Height" Value="50"/>                                         
                                </VisualState.Setters>
                            </VisualState>

                            <VisualState x:Name="VisualStateTablet">
                                <VisualState.StateTriggers> 
                                    <AdaptiveTrigger MinWindowWidth="600" />
                                </VisualState.StateTriggers>
                                <VisualState.Setters>
                                    <Setter Target="ThumbnailImage.Width" Value="70"/>
                                    <Setter Target="ThumbnailImage.Height" Value="70"/>
                                </VisualState.Setters>
                            </VisualState>

                            <VisualState x:Name="VisualStateDesktop">
                                <VisualState.StateTriggers>
                                    <AdaptiveTrigger MinWindowWidth="1200" />
                                </VisualState.StateTriggers>
                                <VisualState.Setters>
                                    <Setter Target="ThumbnailImage.Width" Value="90"/>
                                    <Setter Target="ThumbnailImage.Height" Value="90"/>
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>

                    <Grid.RowDefinitions>
                        <RowDefinition MaxHeight="30"></RowDefinition>
                        <RowDefinition MaxHeight="30"></RowDefinition>
                        <RowDefinition MaxHeight="30"></RowDefinition>
                        <RowDefinition MaxHeight="30"></RowDefinition>
                        <RowDefinition MaxHeight="30"></RowDefinition>
                        <RowDefinition MaxHeight="10"></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>

                    <Image Source="{x:Bind ThumbnailImage}"                                              
                            Visibility="{x:Bind ThumbnailImage, Converter={StaticResource BoolToVisibilityImage}}"
                            Name="ThumbnailImage"/>

                    <TextBlock Text="{x:Bind Name}" .../>                             

                    <Grid  Grid.Row="1"   
                            Grid.Column="1"
                            Visibility="{x:Bind Source, Converter={StaticResource ConverterNameHere}}">

                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>

                        <TextBlock Text="{x:Bind lblSource, Mode=OneWay}" .../>

                        <TextBlock Text="{x:Bind Source}" .../>
                    </Grid>

                    <Grid Grid.Row="2"
                            Grid.Column="1"
                            Visibility="{x:Bind Author, Converter={StaticResource ConverterNameHere}}">

                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>

                        <TextBlock Text="{x:Bind lblAuthor, Mode=OneWay}" .../>
                        <TextBlock Text="{x:Bind Author}"  .../>
                    </Grid>

                    <TextBlock Text="{x:Bind EducationalLevel}" .../>

                    <StackPanel Orientation="Horizontal"
                                Grid.Row="4"
                                Grid.Column="1">

                        <ItemsControl ItemsSource="{x:Bind AccessRights}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Image Source="{Binding}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>

                        <ItemsControl ItemsSource="{x:Bind Languages}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                        </ItemsControl>

                        <ItemsControl ItemsSource="{x:Bind TypeImages}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <Image Source="{Binding}"/>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>                            

                    </StackPanel>

                </Grid>
            </UserControl>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

这是我的增量加载类:

public class ItemsToShow : ObservableCollection<MyViewModel>, ISupportIncrementalLoading
{
    private SearchResponse ResponseObject { get; set; } = new SearchResponse();  
    MyViewModel viewModel = null;


    public bool HasMoreItems
    {
        get
        {
            if ((string.IsNullOrEmpty(SearchResultDataStore.NextPageToken) && !SearchResultDataStore.IsFirstRequest) || SearchResultDataStore.StopIncrementalLoading)
                return false;

            if(SearchResultDataStore.IsFirstRequest)
            {
                using (var db = new DbContext())
                {
                    var json = db.UpdateResponse.First(r => r.LanguageId == DataStore.Language).JsonResponse;
                    Metadata = Newtonsoft.Json.JsonConvert.DeserializeObject<UpdateApiResponse>(json).response.metadata.reply;
                }

                var returnObject = SearchResultDataStore.SearchResponse;
                ResponseObject = returnObject.response;

                //This will show a grid with some message for 3 seconds on a xaml page named SearchResultPage.
                Toast.ShowToast(ResponseObject.message, SearchResultPage.Current.ToastGrid);
            }
            else
            {
                SearchApiResponse returnObject = null;
                try
                {
                    returnObject = new SearchApiCall().CallSearchApiAsync(param1, param2, param3).Result;
                }
                catch
                {  
                    return false;
                }
                ResponseObject = returnObject.response;
            }

            try
            {
                return ResponseObject.documents.Count > 0;                                        
            }
            catch { return false; }
        }
    }

    public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
    {
        CoreDispatcher coreDispatcher = Window.Current.Dispatcher;

        return Task.Run<LoadMoreItemsResult>(async () =>
        {           
            await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                foreach (var item in ResponseObject.documents)
                {             
                    this.Add(PrepareViewModel(item));
                }
            });  

            await Task.Delay(350);                
            return new LoadMoreItemsResult() { Count = count };

        }).AsAsyncOperation<LoadMoreItemsResult>();
    }

    public MyViewModel PrepareViewModel(Document document)
    {
        viewModel = new MyViewModel();

        viewModel.property1 = document.value1;
        viewModel.property2 = document.value2;

        ...
        ...

        if(SearchResultDataStore.ShowImage)
        {            
            //thumbnailUrl is a string.
            viewModel.ThumbnailImage = document.thumbnailUrl;
        }
        else
        {
            viewModel.ThumbnailImage = "somegarbage.png";
        }

        ...
        ...

        return viewModel;
    }

}

2 个答案:

答案 0 :(得分:0)

默认情况下,当您使用x:Bind作为来源时,绑定不会自动更新。 要验证问题的根本原因,您可以在值转换器中设置断点,并查看调用的时间。

我建议使用x:Bind ThumbnailImage, Mode=OneWay

您可能必须在后面的代码中实现INotifyPropertyChanged。

答案 1 :(得分:0)

采取几个步骤:

  1. x:Phase属性添加到Image控件。设置比其他值最大(例如,所有是&#34; 0&#34;,设置&#34; 1&#34;等等)

  2. Mode=OneWay添加到Image,因为您应该知道何时更改属性。

  3. 使用依赖项属性ImageImagePath控件添加控件扩展。什么时候改变财产 - &gt;开始异步加载来自网络的图像(例如HttpClient)。请求完成后 - &gt;获取内容并将其放入BitmapSource并将其放入Source的{​​{1}}属性。

  4. 确保原始图片的大小不超过Image控件的大小,因为如果它是真的 - &gt;用户界面将会挂起。