Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use a ViewModel variable for a DynamicResource?

Tags:

wpf

xaml

This isn't MahApps.Metro-specific, but that happens to be what I'm using. I have a set of ViewModels that have a string property representing which icon to use from a resource XAML file.

public class CommandViewModel : ViewModel
{
    public CommandViewModel(string displayName, ICommand command, string icon)
    {
        if (command == null)
            throw new ArgumentNullException("command");

        DisplayName = displayName;
        Command = command;
        Icon = icon;
    }

    public ICommand Command { get; private set; }
    public string Icon { get; set; }
}

Icon would end up being something like "appbar_add" from MahApps.Metro.Resources. These are defined in an Icons.xaml file.

How do I write this up in my ItemTemplate such that the correct resource shows. I'm either getting errors on execution (not at edit/build) or I'm getting no icons at all.

The "no icon" XAML looks like this:

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Rectangle Width="20" Height="20">
                    <Rectangle.Fill>
                        <VisualBrush Visual="{DynamicResource {Binding Path=Icon}}" />
                    </Rectangle.Fill>
                </Rectangle>
                <TextBlock Margin="15,6">                            
                    <Hyperlink Command="{Binding Path=Command}">
                        <TextBlock Text="{Binding Path=DisplayName}" />
                    </Hyperlink>
                </TextBlock>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

My attempts that caused errors were using StaticResource, which I think is fundamentally incorrect.

How should I be referencing the Icon property as the name of the resource I want?

Edit

More code was requested, so here's an example of what does work:

<Rectangle Width="20" Height="20">
    <Rectangle.Fill>
        <VisualBrush Visual="{StaticResource appbar_add}" />
    </Rectangle.Fill>
</Rectangle>

What I need to do is let "appbar_add" be a value from a property on my ViewModel -- the Icon property above.

The resources are in a ResourceDictionary in a separate file (Icon.xaml) and look like the following:

<Canvas x:Key="appbar_add" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
    <Path Width="38" Height="38" Canvas.Left="19" Canvas.Top="19" Stretch="Fill" Fill="{DynamicResource BlackBrush}" Data="F1 M 35,19L 41,19L 41,35L 57,35L 57,41L 41,41L 41,57L 35,57L 35,41L 19,41L 19,35L 35,35L 35,19 Z "/>
</Canvas>
like image 528
Melissa Avery-Weir Avatar asked Sep 28 '22 09:09

Melissa Avery-Weir


1 Answers

You can use a converter to do the work. refer the below code. I have two style to make the + symbol in red color or black color in Icon.xaml.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Canvas x:Key="appbar_add_Black" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="38" Height="38" Canvas.Left="19" Canvas.Top="19" Stretch="Fill" Fill="Black" Data="F1 M 35,19L 41,19L 41,35L 57,35L 57,41L 41,41L 41,57L 35,57L 35,41L 19,41L 19,35L 35,35L 35,19 Z "/>
    </Canvas>
    <Canvas x:Key="appbar_add_Red" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
        <Path Width="38" Height="38" Canvas.Left="19" Canvas.Top="19" Stretch="Fill" Fill="Red" Data="F1 M 35,19L 41,19L 41,35L 57,35L 57,41L 41,41L 41,57L 35,57L 35,41L 19,41L 19,35L 35,35L 35,19 Z "/>
    </Canvas>
</ResourceDictionary>

Refer the View code.

<Window.Resources>
    <local:IconConverter x:Key="conv"/>
</Window.Resources>
<Grid>
    <Rectangle Width="20" Height="20">
        <Rectangle.Fill>
            <VisualBrush Visual="{Binding Icon,Converter={StaticResource conv}}" />
        </Rectangle.Fill>
    </Rectangle>
</Grid>

ViewModel

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new CommandViewModel("Red");
    }
}

public class CommandViewModel :INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
    public CommandViewModel(string icon)
    { 
        Icon = icon;
    }

    private string icon;

    public string Icon
    {
        get { return icon; }
        set { icon = value; }
    }    
}

class IconConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string str = (string)value;
        ResourceDictionary myResourceDictionary = new ResourceDictionary();
        myResourceDictionary.Source =
            new Uri("Icon.xaml",
                UriKind.Relative);
        if (str.Equals("Black"))
        {              
            return myResourceDictionary["appbar_add_Black"];
        }
        else
        {
            return myResourceDictionary["appbar_add_Red"];
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
like image 75
Ayyappan Subramanian Avatar answered Oct 05 '22 07:10

Ayyappan Subramanian