Получение TaskCanceledException в методе обновления свойства - PullRequest
0 голосов
/ 28 марта 2019

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

namespace GammaFour.Views.Controls
{
    using System;
    using Windows.Storage;
    using Windows.Storage.Streams;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Media.Imaging;

    /// <summary>
    /// Provides an image from a store.
    /// </summary>
    public class BitmapSourceSelector : BitmapSource
    {
        /// <summary>
        /// The Category DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty CategoryProperty = DependencyProperty.Register(
            "Category",
            typeof(string),
            typeof(BitmapSourceSelector),
            new PropertyMetadata(default(string)));

        /// <summary>
        /// The Dictionary DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty DictionaryProperty = DependencyProperty.Register(
            "Dictionary",
            typeof(UriDictionary),
            typeof(BitmapSourceSelector),
            new PropertyMetadata(default(UriDictionary)));

        /// <summary>
        /// The Key DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
            "Key",
            typeof(string),
            typeof(BitmapSourceSelector),
            new PropertyMetadata(default(string), BitmapSourceSelector.OnKeyPropertyChanged));

        /// <summary>
        /// Gets or sets the category used to select the source for the icon.
        /// </summary>
        public string Category
        {
            get
            {
                return this.GetValue(BitmapSourceSelector.CategoryProperty) as string;
            }

            set
            {
                this.SetValue(BitmapSourceSelector.CategoryProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the dictionary of URIs.
        /// </summary>
        public UriDictionary Dictionary
        {
            get
            {
                return this.GetValue(BitmapSourceSelector.DictionaryProperty) as UriDictionary;
            }

            set
            {
                this.SetValue(BitmapSourceSelector.DictionaryProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the key used to select the source for the icon.
        /// </summary>
        public string Key
        {
            get
            {
                return this.GetValue(BitmapSourceSelector.KeyProperty) as string;
            }

            set
            {
                this.SetValue(BitmapSourceSelector.KeyProperty, value);
            }
        }

        /// <summary>
        /// Invoked when the effective property value of the Key property changes.
        /// </summary>
        /// <param name="dependencyObject">The DependencyObject on which the property has changed value.</param>
        /// <param name="dependencyPropertyChangedEventArgs">
        /// Event data that is issued by any event that tracks changes to the effective value of this property.
        /// </param>
        private static async void OnKeyPropertyChanged(
            DependencyObject dependencyObject,
            DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            // Extract the changed properties.
            string key = dependencyPropertyChangedEventArgs.NewValue as string;
            BitmapSourceSelector bitmapSourceSelector = dependencyObject as BitmapSourceSelector;

            // Select a source for the image based on the new key.
            if (!string.IsNullOrEmpty(key) && bitmapSourceSelector.Dictionary != null)
            {
                Uri uri = bitmapSourceSelector.Dictionary.GetUri(bitmapSourceSelector.Category, key);
                StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);
                IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
                await bitmapSourceSelector.SetSourceAsync(stream);
            }
        }
    }
}

При выполнении метода OnKeyPropertyChanged я получаю исключение:

Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
A task was canceled.

Исключение происходит во время этого вызова:

        await bitmapSourceSelector.SetSourceAsync(stream);

Если я заменим SetSourceAsync на SetSource , он будет работать нормально. Исключение происходит несколько случайно, что обычно означает, что ожидание не возвращается в потоке окна, но я не могу найти способ его форсировать. Итак, у меня есть два вопроса:

  1. Что не так с этим кодом? Почему не ждать (синхронно версия работает нормально, которая поддерживает гипотеза о том, что вызов не возвращается в нужном потоке).
  2. У кого-нибудь есть лучший способ связать ресурс Bitmap с ценность? Теоретически модель представления не должна иметь знание ресурсов View и Bitmap явно является аспектом зрения.

РЕДАКТИРОВАТЬ: Нико Чжу попросил вспомогательных классов. Вот они:

UriCategory:

/// <summary>
/// A category of URIs.
/// </summary>
public class UriCategory : Dictionary<string, UriSource>
{
}

UriDictionary:

/// <summary>
/// A dictionary of URIs organized by category.
/// </summary>
public class UriDictionary : Dictionary<string, UriCategory>
{
    /// <summary>
    /// Gets the URI associated with the category, key keys.
    /// </summary>
    /// <param name="category">The category of the URIs.</param>
    /// <param name="key">The key for the URI.</param>
    /// <returns>The URI entered into the dictionary with the category, key index.</returns>
    internal Uri GetUri(string category, string key)
    {
        // This URI is returned if there's no matching entry for the keys.
        Uri uri = default(Uri);

        // Use the two dictionary levels to find the URI.
        UriCategory resourceCategory;
        if (this.TryGetValue(category, out resourceCategory))
        {
            UriSource resourceSource;
            if (resourceCategory.TryGetValue(key, out resourceSource))
            {
                uri = resourceSource.Uri;
            }
        }

        // The URI belonging to the compound key.
        return uri;
    }
}

А вот использование в XAML:

<!-- Metadata Image Dictionary -->
<dbcontrols:UriDictionary x:Key="MetadataImages">
    <dbcontrols:UriCategory x:Key="Small">
        <dbcontrols:UriSource x:Key="Alert"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Small/Alert.png"/>
        <dbcontrols:UriSource x:Key="Blotter"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Small/Blotter.png"/>
        <dbcontrols:UriSource x:Key="Folder"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Small/Folder.png"/>
        <dbcontrols:UriSource x:Key="Portfolio"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Small/Portfolio.png"/>
    </dbcontrols:UriCategory>
    <dbcontrols:UriCategory x:Key="Medium">
        <dbcontrols:UriSource x:Key="Alert"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Medium/Alert.png"/>
        <dbcontrols:UriSource x:Key="Blotter"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Medium/Folder.png"/>
        <dbcontrols:UriSource x:Key="Folder"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Medium/Folder.png"/>
        <dbcontrols:UriSource x:Key="Portfolio"
                                Uri="ms-appx:///GammaFour.InvestmentManagement.Views/Assets/Medium/Portfolio.png"/>
    </dbcontrols:UriCategory>
</dbcontrols:UriDictionary>

А вот как использовать словарь изображений для отображения изображения на основе ключа. Такая конструкция позволяет View Model не требовать каких-либо знаний о View для отображения изображений с данными.

<Grid Height="160"
      Width="190">
        <Image.Source>
            <dbcontrols:BitmapSourceSelector Category="Medium"
                                             Dictionary="{StaticResource MetadataImages}"
                                             Key="{Binding ImageKey}"/>
        </Image.Source>
    </Image>
</Grid>

Опять же, если вы знаете лучший способ сделать это, удаляющий модель представления из представления, я был бы признателен за пример.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...