Столкнулся с интересной задачей. Есть некоторая коллекция данных, которую надо отобразить в ItemsControl. У элементов данных есть два свойства: первое просто текст, а второе имя ресурса с картинкой которую необходимо показать. Т.е. в зависимости от данных в модели должен подгружаться тот или иной ресурс. Поискав решение быстро наткнулся на применение конвертора который ищет ресурс по имени следующим способом:
Но это не работает, если ресурсы подгружены не в приложение, а например, в окно или контрол. Под катом интересный метод, который позволяет получить доступ к ресурсу где бы он не находился, главное, чтобы был доступен контролу.
В рамках примера, я буду использовать простой класс с двумя свойствами:
Как я уже сказал, таким образом надо поступать, если в конверторе нельзя просто извлечь ресурсы из Application. Так, кстати, можно и другие свойства задавать, необязательно привязку к ресурсам.
return Application.Current.FindResource(resourceName) as BitmapImage;
Но это не работает, если ресурсы подгружены не в приложение, а например, в окно или контрол. Под катом интересный метод, который позволяет получить доступ к ресурсу где бы он не находился, главное, чтобы был доступен контролу.
В рамках примера, я буду использовать простой класс с двумя свойствами:
public class ModelВот разметка формы:
{
public string Title { get; set; }
public string ResourceName { get; set; }
}
<Window x:Class="WpfApplication4.MainWindow"А вот заполнение данными списка при загрузке:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BitmapImage x:Key="plus" UriSource="plus.png" />
<BitmapImage x:Key="delete" UriSource="delete.png" />
<local:NameToStyleConverter x:Key="NameToStyleConverter" />
</Window.Resources>
<Grid>
<ItemsControl x:Name="icDemo">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Style="{Binding ResourceName,Converter={StaticResource NameToStyleConverter}}" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
public partial class MainWindow : WindowКак можно видеть из разметки, у контрола Image свойство Source не присваивается, а присваивается... Стиль! Ну а дальше все просто, получая имя ресурсы, мы конвертором создаем динамически стиль, в котором свойству Source присваиваем DynamicResource. Вот так:
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
List<Model> items = new List<Model>();
items.Add(new Model() { Title = "Один", ResourceName = "plus" });
items.Add(new Model() { Title = "Два", ResourceName = "plus" });
items.Add(new Model() { Title = "Три", ResourceName = "delete" });
items.Add(new Model() { Title = "Четыре", ResourceName = "plus" });
items.Add(new Model() { Title = "Пять", ResourceName = "delete" });
icDemo.ItemsSource = items;
}
}
public class NameToStyleConverter : IValueConverterНу и вот так это выглядит:
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Style result = null;
var name = value?.ToString();
if (!string.IsNullOrWhiteSpace(name))
{
result = new Style(typeof(Image));
var dynamicResource = new DynamicResourceExtension(name);
var setter = new Setter()
{
Property = Image.SourceProperty,
Value = dynamicResource
};
result.Setters.Add(setter);
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Как я уже сказал, таким образом надо поступать, если в конверторе нельзя просто извлечь ресурсы из Application. Так, кстати, можно и другие свойства задавать, необязательно привязку к ресурсам.