UWP Windows-10 Лучший способ загрузки изображений

У меня есть приложение C#, предназначенное для рабочего стола Windows UWP. Приложение отображает список изображений. Когда пользователь нажимает на любое изображение в представлении списка, приложение увеличивает (детальный вид) его и показывает изображение (вид полноэкранного режима, поскольку размер изображения больше).
Всякий раз, когда я нажимаю любое изображение в представлении списка, изображение в подробном представлении сначала показывает ранее загруженное изображение, а затем обновляет и загружает изображение из текущего выбора
Я хочу знать, как предотвратить загрузку предыдущего изображение до тех пор, пока не будет получено новое изображение?
В настоящее время всякий раз, когда пользователь щелкает элемент в представлении списка, я сначала загружаю источник изображения подробного представления с пустым изображением, а затем извлекаю требуемое изображение и загружаю его.
Мой код здесь:

 await CallOnUiThreadAsync(() =>
 {
    /*Load empty image first*/
    imgBitmap = new BitmapImage();
    var uri = new System.Uri("ms-appx:///Assets/emptyImage.png");
    imgBitmap.UriSource = uri;
    img.Source = imgBitmap;
  });

  if (("/Assets/emptyImage.png" != url) || (url != String.Empty))
  {
      await Task.Run(async () =>
      {
        /*image_fetch is our proprietary api. It returns a byte array*/
        byte[] imageBytes = image_fetch;
        MemoryStream stream = new MemoryStream(imageBytes, 0, imageBytes.Length, true);
        var randomAccessStream = new MemoryRandomAccessStream(stream);
        await CallOnUiThreadAsync(async () =>
        {
            imgBitmap = new BitmapImage();
            await imgBitmap.SetSourceAsync(randomAccessStream);
            img.Source = imgBitmap;
         });
       });
   }


Я уже запускаю image_fetch в фоновом потоке, тем не менее я вижу небольшую задержку с момента, когда пользователь щелкает элемент представления списка, до загрузки изображения в подробном представлении.
Я хотел бы уменьшить отставание. Есть ли лучший способ сделать это, кроме загрузки пустого изображения?


person Ganesh kudva    schedule 27.05.2016    source источник


Ответы (2)


Всякий раз, когда я нажимаю любое изображение в представлении списка, изображение в подробном представлении сначала показывает ранее загруженное изображение, а затем обновляет и загружает изображение из текущего выбора.

Я думаю, проблема в том, как реализована ваша структура Mater/Detail. Из вашего кода я вижу, что вы, вероятно, читаете изображения из файла и устанавливаете BitmapImage в качестве источника Image в ListView, здесь я создал образец, когда ширина окна превышает 720, Деталь будет отображаться справа от Мастера , а если ширина меньше 720, деталь будет увеличена (на весь экран).

Моя главная страница:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="AdaptiveStates">
            <VisualState x:Name="DefaultState">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="720" />
                </VisualState.StateTriggers>
            </VisualState>

            <VisualState x:Name="NarrowState">
                <VisualState.StateTriggers>
                    <AdaptiveTrigger MinWindowWidth="0" />
                </VisualState.StateTriggers>

                <VisualState.Setters>
                    <Setter Target="MasterColumn.Width" Value="*" />
                    <Setter Target="DetailColumn.Width" Value="0" />
                    <Setter Target="MasterListView.SelectionMode" Value="None" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="MasterColumn" Width="320" />
        <ColumnDefinition x:Name="DetailColumn" Width="*" />
    </Grid.ColumnDefinitions>

    <ListView x:Name="MasterListView" Grid.Column="0" ItemContainerTransitions="{x:Null}"
        IsItemClickEnabled="True" ItemClick="MasterListView_ItemClick" ItemsSource="{x:Bind itemcollection}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="local:TestItem">
                <Image Source="{x:Bind ImageItem}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <ContentPresenter x:Name="DetailContentPresenter" Grid.Column="1" BorderThickness="1,0,0,0"
        Padding="24,0" BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
        Content="{x:Bind MasterListView.SelectedItem, Mode=OneWay}">
        <ContentPresenter.ContentTemplate>
            <DataTemplate x:DataType="local:TestItem">
                <Image Source="{x:Bind ImageItem}" />
            </DataTemplate>
        </ContentPresenter.ContentTemplate>
    </ContentPresenter>
</Grid>

код позади:

private ObservableCollection<TestItem> itemcollection = new ObservableCollection<TestItem>();

public MainPage()
{
    this.InitializeComponent();
}

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    StorageFolder picLib = KnownFolders.PicturesLibrary;
    var picfiles = await picLib.GetFilesAsync();
    foreach (var pic in picfiles)
    {
        BitmapImage bmp = new BitmapImage();
        IRandomAccessStream stream = await pic.OpenReadAsync();
        bmp.SetSource(stream);
        itemcollection.Add(new TestItem { ImageItem = bmp });
    }
}

private void MasterListView_ItemClick(object sender, ItemClickEventArgs e)
{
    var clickedItem = (TestItem)e.ClickedItem;

    if (AdaptiveStates.CurrentState == NarrowState)
    {
        Frame.Navigate(typeof(DetailPage), clickedItem);
    }
}

Моя подробная страница:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Image Source="{x:Bind image}" />
</Grid>

код позади:

private BitmapImage image;

public DetailPage()
{
    this.InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    var testitem = e.Parameter as TestItem;
    image = testitem.ImageItem;

    var backStack = Frame.BackStack;
    var backStackCount = backStack.Count;

    if (backStackCount > 0)
    {
        var masterPageEntry = backStack[backStackCount - 1];
        backStack.RemoveAt(backStackCount - 1);

        // Doctor the navigation parameter for the master page so it
        // will show the correct item in the side-by-side view.
        var modifiedEntry = new PageStackEntry(
            masterPageEntry.SourcePageType,
            e.Parameter,
            masterPageEntry.NavigationTransitionInfo
            );
        backStack.Add(modifiedEntry);
    }

    // Register for hardware and software back request from the system
    SystemNavigationManager systemNavigationManager = SystemNavigationManager.GetForCurrentView();
    systemNavigationManager.BackRequested += DetailPage_BackRequested;
    systemNavigationManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    base.OnNavigatedFrom(e);

    SystemNavigationManager systemNavigationManager = SystemNavigationManager.GetForCurrentView();
    systemNavigationManager.BackRequested -= DetailPage_BackRequested;
    systemNavigationManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Collapsed;
}

private void OnBackRequested()
{
    Frame.GoBack();
}

private void DetailPage_BackRequested(object sender, BackRequestedEventArgs e)
{
    e.Handled = true;
    OnBackRequested();
}

И мой класс TestItem здесь довольно прост:

public class TestItem
{
    public BitmapImage ImageItem { get; set; }
}

Вы также можете обратиться к официальному образцу Master/detail. . Я написал свой образец по этому официальному образцу, ключевые моменты здесь:

  • Создание параллельной главной страницы/страницы сведений.

  • Навигация между основным списком и подробным представлением.

  • Изменение модели навигации при изменении размера приложения.

Я уже запускаю image_fetch в фоновом потоке, но все же вижу небольшую задержку с момента, когда пользователь щелкает элемент представления списка, до загрузки изображения в подробном представлении.

Получали ли вы источник изображения (например, из файла) каждый раз, когда щелкали элемент ListViewItem, поэтому можете ли вы установить источник изображения в рамку сведений? Здесь, используя DataBinding, вы можете просто получить все изображения, когда хотите добавить данные в свой файл ListView.

person Grace Feng    schedule 30.05.2016
comment
Привет @Grace Feng - MSFT, спасибо за ответ. Я загружаю только пустое изображение из файла /Assets/emptyImage.png. Фактическое изображение для выбора извлекается с помощью нашего пользовательского API image_fetch. Этот API получает изображения, расположенные на наших серверах, используя HTTP-вызовы. - person Ganesh kudva; 02.06.2016
comment
@ Ганешкудва, все в порядке. Это не имеет значения, где и как вы получаете изображение, я имею в виду, что вам просто нужно загрузить изображение, как только вы добавите источник изображения в коллекцию для ListView, если один элемент изображения должен отображаться в представлении сведений, нет необходимости извлекать снова. Не могли бы вы проверить мой образец еще раз? - person Grace Feng; 02.06.2016

Вы можете использовать FFImageLoading, он использует различные методы для оптимизации загрузки изображений (включая кеш загрузки/памяти):

XML <ff:FFImage VerticalAlignment="Stretch" HorizontalAlignment="Stretch" TransformPlaceholders="False" LoadingPlaceholder="loading.png" ErrorPlaceholder="error.png" Height="500" Width="500" Source="http://loremflickr.com/600/600/nature?filename=simple.jpg">

person Daniel Luberda    schedule 02.01.2017
comment
Какое пространство имен у ff:FFImage? Можете ли вы предоставить исходный код элемента управления? - person Briefkasten; 22.06.2018