У меня есть следующий вспомогательный класс 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 , он будет работать нормально. Исключение происходит несколько случайно, что обычно означает, что ожидание не возвращается в потоке окна, но я не могу найти способ его форсировать. Итак, у меня есть два вопроса:
- Что не так с этим кодом? Почему не ждать (синхронно
версия работает нормально, которая поддерживает
гипотеза о том, что вызов не возвращается в нужном потоке).
- У кого-нибудь есть лучший способ связать ресурс 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>
Опять же, если вы знаете лучший способ сделать это, удаляющий модель представления из представления, я был бы признателен за пример.