Я заполняю ListBox, который содержит картинку и имя, с помощью класса. Как присвоить картинке Canvas из файла ресурсов и будет ли изменятся цвет этой картинке если изменять в Canvas'e заливку?
List<MenuListBoxItem> menuItems = new List<MenuListBoxItem>();
ResourceDictionary iconDictionary = new ResourceDictionary();
iconDictionary.Source = new Uri("pack://application:,,,/Res/Vector/IconDictionary.xaml");
menuItems.Add(new MenuListBoxItem("Search", (ImageSource)iconDictionary["appbar_radar"]));
using System.Windows.Media;
using System.Windows.Media.Imaging;
public class MenuListBoxItem
{
public ImageSource Image { get; set; }
public string Content { get; set; }
public MenuListBoxItem(string name, ImageSource image)
{
this.Image = image;
this.Content = name;
}
public MenuListBoxItem(string name, Uri image)
{
this.Image = new BitmapImage(image);
this.Content = name;
}
public MenuListBoxItem(string name, BitmapImage image)
{
this.Image = image;
this.Content = name;
}
}
EDIT 1: В общем я пытаюсь получить такой вид меню:
Чтобы список брался из класса и цвет иконок мог меняться. Код одной из иконок:
<Canvas x:Key="appbar_radar" x:Name="appbar_radar" Width="44" Height="44" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="42.4159" Height="44.3333" Canvas.Left="16.1674" Canvas.Top="15.8334" Stretch="Fill" Fill="{DynamicResource ContrastBrush}" Data="..."/>
</Canvas>
Смотрите.
Один из центральных принципов WPF — разделение задач. Вы должны стараться не указывать UI-конструкции в описании ваших данных. Имеет смысл описывать в данных не то, какой UI у вашего элемента, а лишь необходимый минимум.
Кроме того, класть в ResourceDictionary
куски UI неправильно. XAML — не просто «разметка», элементы создаются на самом деле, поэтому если вы кладёте визуальный фрагмент в него и используете в нескольких местах, вы получите ошибку. В качестве повторно используемых фрагментов уместно использовать различные Template
'ы. Поскольку у вас повторяемая структура, уместно использовать ItemTemplate
, а в параметр выносить часть, являющуюся чистыми данными, то есть Geometry
.
В итоге получаем следующее:
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test"
x:Class="Test.MainWindow"
mc:Ignorable="d"
Title="Тест" Height="350" Width="525">
<Window.Resources>
<StreamGeometry x:Key="SettingsIcon">M0,0 L0,1 L1,1 L1,0 z</StreamGeometry>
<StreamGeometry x:Key="HelpIcon">M0,0 L1,1 M1,0 L0,1</StreamGeometry>
<SolidColorBrush x:Key="MenuColor">Green</SolidColorBrush>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ItemDescriptor}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Path Data="{Binding Icon}" Grid.Column="0" Stretch="Uniform"
Height="25" Width="25" Stroke="{DynamicResource MenuColor}"/>
<TextBlock Text="{Binding Content}" VerticalAlignment="Center"
Grid.Column="1" Foreground="{DynamicResource MenuColor}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
Понятно, что тут происходит? Мы изображаем последовательность одинаковых элементов, по одному на элемент коллекции, у каждого из них Icon
и Content
берётся из привязки.
Теперь code-behind:
Вспомогательный класс:
class ItemDescriptor
{
public Geometry Icon { get; }
public string Content { get; }
public ItemDescriptor(Geometry icon, string content)
{
Icon = icon;
Content = content;
}
}
И окно:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new[]
{
new ItemDescriptor((Geometry)Resources["SettingsIcon"], "Settings"),
new ItemDescriptor((Geometry)Resources["HelpIcon"], "Help"),
new ItemDescriptor((Geometry)Resources["HelpIcon"], "Udp Multicast")
};
}
}
Нужный цвет фона и отступы добавить по вкусу.
Добавка для эстетов. Если вы хотите, чтобы класс ItemDescriptor
не зависел от UI и не содержал Geometry
(например, вы захотите передавать его из VM), нужна небольшая переделка. Во-первых, мы заменяем в ItemDescriptor
Icon
на IconName
:
class ItemDescriptor
{
public string IconName { get; }
public string Content { get; }
public ItemDescriptor(string iconName, string content)
{
IconName = iconName;
Content = content;
}
}
Соответственно, конструктор будет выглядеть проще:
DataContext = new[]
{
new ItemDescriptor("SettingsIcon", "Settings"),
new ItemDescriptor("HelpIcon", "Help"),
new ItemDescriptor("HelpIcon", "Udp Multicast")
};
Теперь XAML: в нём мы должны установить DynamicResource
, а ключ взять из привязки! Из коробки {DynamicResource ResourceKey={Binding Key}}
не работает, нам нужен вспомогательный класс.
Я пользуюсь таким трюком:
public class IndirectDynamicResource : FrameworkElement
{
static IndirectDynamicResource()
{
WidthProperty.OverrideMetadata(typeof(IndirectDynamicResource),
new FrameworkPropertyMetadata(0.0));
HeightProperty.OverrideMetadata(typeof(IndirectDynamicResource),
new FrameworkPropertyMetadata(0.0));
FocusableProperty.OverrideMetadata(typeof(IndirectDynamicResource),
new FrameworkPropertyMetadata(false));
}
#region object dp ResourceKey, change callback ValueUpdateCallback
public object ResourceKey
{
get { return GetValue(ResourceKeyProperty); }
set { SetValue(ResourceKeyProperty, value); }
}
public static readonly DependencyProperty ResourceKeyProperty =
DependencyProperty.Register(
"ResourceKey", typeof(object), typeof(IndirectDynamicResource),
new PropertyMetadata(ValueUpdateCallback));
#endregion
static protected void ValueUpdateCallback(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = (IndirectDynamicResource)d;
self.SetResourceReference(IndirectDynamicResource.ValueProperty,
self.ResourceKey ?? string.Empty);
}
#region object dp Value
public object Value
{
get { return GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(object), typeof(IndirectDynamicResource));
#endregion
}
Имея это в арсенале, код в XAML меняем так:
<local:IndirectDynamicResource ResourceKey="{Binding IconName}" x:Name="D"/>
<Path Data="{Binding Value, ElementName=D}" Grid.Column="0" Stretch="Uniform"
Height="25" Width="25" Stroke="{DynamicResource MenuColor}"/>
(Другие решения этой же проблемы см. тут.)
Всё!
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Добавил в настройки проекта параметр с типом TypeSpan, но почему-то я не вижу этот параметр через PropertiesSettings
Написал код для вставки запятой перед словамиКак перезаписать текст в файле на текст с расставленными запятыми?
ЗдравствуйтеПередо мной стоит задача - мне необходимо сделать простенькую игру: когда начинаешь игру, через некоторое время необходимо нажать...
Не распознается свойство HasValueВ чем проблема? Не могу понять :/ В System