Reza
Reza

Reputation: 5634

How do I bind the name of a StaticResource to a ViewModel property?

I have a scenario where my Xamarin Label displays an Icon from a ttf file. As it stands, the Label looks as follows.

<Label
Text="{StaticResource Account}"/>

Where Account is a string in my Styles.xaml file that defines the Hex value.

<x:String x:Key="Account">&#xe70e;</x:String>

The above works just fine. However, I would like to bind the Icon text name via a property in my ViewModel. The following does not work, but I am thinking of something like this:

 Text="{StaticResource Binding=IconName}"

How would I accomplish this?

UPDATE: adding some more context to this question.

In my Styles.xaml I am defining a dictionary that corresponds to their hex values

<x:String x:Key="Back">&#xe709;</x:String>
<x:String x:Key="Share">&#xe704;</x:String>
<x:String x:Key="Next">&#xe71a;</x:String>
<x:String x:Key="Account">&#xe70e;</x:String>
<x:String x:Key="Bell">&#xe700;</x:String>
<x:String x:Key="Mail">&#xe70e;</x:String>
<x:String x:Key="Help">&#xe72c;</x:String>

This styles file is a resource to my xaml file.

        <ContentPage.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Styles.xaml" />
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </ContentPage.Resources>

In my XAML file, I am iterating through to display different icons.

            <StackLayout
                x:Name="ItemsList"
                Grid.Row="1"
                BindableLayout.ItemsSource="{Binding MenuItems}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>
                            <Label  Text="{StaticResource Account}" FontFamily="{StaticResource FontIcons}"
                                FontSize="Large"></Label>
                        </StackLayout>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>

And in my List, I am specifying the Icon name.

        MenuItems = new List<AppMenuItem>()
        {
            new AppMenuItem()
            {
                Title = "View A",
                IconSource = "Account",
            },
            new AppMenuItem()
            {
                Title = "View B",
                IconSource = "Mail",
            }

        };

UPDATE 2: I am trying to build a value converter, but I can't seem to access the merged dictionaries. I am using:

Application.Current.Resources.TryGetValue(value.ToString(), out var retValue);

retValue is always returning NULL.

Upvotes: 2

Views: 2799

Answers (4)

Raj
Raj

Reputation: 795

You can use data trigger,

<Label Text="">
     <Label.Triggers>
        <DataTrigger TargetType="Label"
                  Binding="{Binding IconName}"
                  Value="Account">
                  <Setter Property="Text" Value="{StaticResource Account}"/>
         </DataTrigger>
     </Label.Triggers>
</Label>

You can write multiple DataTriggers for each condition you want to match and set StaticResource

Upvotes: 1

Ben
Ben

Reputation: 2985

Define class for icon glyphs:

namespace MyNamespace
{
    public static class Icons
    {
        public const string Glyph = "\ue000";
        ...
    }
}

Resource dictionary /Resources/Glyphs.xaml:

<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:i="clr-namespace:MyNamespace">

    <OnPlatform x:Key="Glyph" x:TypeArguments="x:String" Default="{x:Static i:Icons.Glyph}" />

</ResourceDictionary>

Reference resource dictionary from page:

<ResourceDictionary>
  <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/Resources/Glyphs.xaml" />
  </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

ViewModel example:

using MyNamespace;
...
public class FontViewModel
{
    public string GlyphFromClass { get; set; }

    public string GlyphFromMergedResource { get; set; }

    //pass page in constructor for referencing the page local resource dictionary
    public FontViewModel(Page page)
    {
        GlyphFromClass = Icons.Glyph;

        //ResourceDictionary applicationResourceDictionary = Application.Current.Resources;

        ResourceDictionary localResourceDictionary = page.Resources;

        string str = string.Empty;
        if (localResourceDictionary.TryGetValue("Glyph", out object obj) && obj is OnPlatform<string>)
        {
            str = (OnPlatform<string>)obj;
        }

        GlyphFromMergedResource = str;
    }
}

If needed, implement INotifyPropertyChanged for viewmodel class and use OnPropertyChanged in properties.

Viewmodel usage example in page xaml code behind:

InitializeComponent();

BindingContext = new FontViewModel(this);

Xaml with binding:

<Label Text="{Binding GlyphFromClass}" FontFamily="{StaticResource FontFamily}" />
<Label Text="{Binding GlyphFromMergedResource}" FontFamily="{StaticResource FontFamily}" />

Xaml with static resources:

<Label Text="{x:Static i:Icons.Glyph}" FontFamily="{StaticResource FontFamily}" />
<Label Text="{StaticResource Glyph}" FontFamily="{StaticResource FontFamily}" />

Upvotes: 0

Mridul Malviya
Mridul Malviya

Reputation: 96

    -----:::App.xaml:::------
 <?xml version="1.0" encoding="utf-8" ?>
    <Application xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="PracticeXamarinForms.App">
        <Application.Resources>
            <ResourceDictionary>
                <x:String x:Key="Back">&#xe709;</x:String>
                <x:String x:Key="Share">&#xe704;</x:String>
                <x:String x:Key="Next">&#xe71a;</x:String>
                <x:String x:Key="Account">&#xe70e;</x:String>
                <x:String x:Key="Bell">&#xe700;</x:String>
                <x:String x:Key="Mail">&#xe70e;</x:String>
                <x:String x:Key="Help">&#xe72c;</x:String>
            </ResourceDictionary>
        </Application.Resources>
    </Application>

    -----:::Views:::------
    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="PracticeXamarinForms.Views.DemoPage"
                 xmlns:vm="clr-namespace:PracticeXamarinForms.ViewModels">
        <ContentPage.BindingContext>
            <vm:DemoViewModel/>
        </ContentPage.BindingContext>
        <ContentPage.Content>
            <StackLayout>
                <Label 
                    VerticalOptions="CenterAndExpand" 
                    HorizontalOptions="CenterAndExpand"
                       Text="{Binding IconName}"/>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>

    -----:::ViewModels:::------
    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace PracticeXamarinForms.ViewModels
    {
        public class DemoViewModel
        {
            #region properties
            public string IconName { get; set; }
            #endregion

            public DemoViewModel()
            {
                IconName = (string)App.Current.Resources["Mail"];
            }
        }
    }

Upvotes: 0

Angella Yu
Angella Yu

Reputation: 72

you can bind to a style of the label.

<Style TargetType="Label">
    <Setter Property="Text" Value="{Binding Account}"/>
</Style>

Upvotes: 0

Related Questions