ZanAlex
Topic Author
Posts: 66
Joined: 16 Jan 2015, 17:46

Specify ResourceDictionary and ResourceKey in Binding

09 Mar 2015, 18:40

Hi everyone,

In order to have a set of common resources for my application without having several instances of the same ResourceDictionary, I'd like to be able to specify what UI component requires what Dictionary in the code. The thing is I don't want to add them as MergedDictionaries, because I don't want to end up with conflicts due to multiple resources having the same key. To achieve that, I want each ResourceDictionary to be an entry in the Control Resources and then be able to specify which dictionary should be interrogated with what key.
<Border
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Width="50" Height="50">
	
     <Border.Resources>
        <ResourceDictionary>
            <ResourceDictionary x:Key="SampleDictionary" Source="basicDictionary.xaml"/>
        </ResourceDictionary>
    </Border.Resources>
  
    <Border.Background>
        <SolidColorBrush Color="{Binding Sample_Blue, Source={StaticResource SampleDictionary}}"/>
    </Border.Background>
  
</Border>
basicDictionary.xaml being:
<ResourceDictionary
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  
    <Color x:Key="Sample_Blue">#FF0000FF</Color>
  
</ResourceDictionary>
This code does not get me any error, but nothing is displayed. I've tried to use a converter, explicitly turning the argument into a ResourceKey for the ResourceDictionary:
<SolidColorBrush Color="{Binding Sample_Blue, Converter="ResArgConverter", ConverterParameter={StaticResource SampleDictionary}}"/>
NsBool TryConvert(Noesis::Core::BaseComponent* value,
                              const Noesis::Core::Type* targetType,
                              Noesis::Core::BaseComponent* parameter,
                              Noesis::Core::Ptr<Noesis::Core::BaseComponent>& result)
{
    Noesis::Core::BaseComponent* pResult = nullptr;
    if (nullptr != value)
    {
        Noesis::Gui::ResourceDictionary* pParam = NsStaticCast<Noesis::Gui::ResourceDictionary*>(parameter);
        const NsChar* strKey = value->ToString().c_str();
        Noesis::Core::Ptr<Noesis::Gui::ResourceKeyString> pResKeyStr =
          Noesis::Gui::ResourceKeyString::TryCreate(strKey);

        if (nullptr != pParam && 0 != pResKeyStr && pParam->Find(pResKeyStr.GetPtr(), pResult))
        {
            result.Reset(pResult);
            return true;
        }
    }
    result.Reset();
    return false;
}
But the value of strKey is wrong, therefore the key is null and I retrieve nothing from my Dictionary.

What am I doing wrong? Is there a better way to perform what I'm trying to do?
Thanks,
Alexandre.
Last edited by ZanAlex on 28 Sep 2015, 16:41, edited 2 times in total.
 
User avatar
sfernandez
Site Admin
Posts: 1912
Joined: 22 Dec 2011, 19:20

Re: Specify ResourceDictionary and ResourceKey in Binding

13 Mar 2015, 13:17

Hi,

This piece of xaml:
<SolidColorBrush Color="{Binding Sample_Blue, Source={StaticResource SampleDictionary}}"/>
won't resolve the binding because there is no property named 'Sample_Blue' in a ResourceDictionary. What you want to do is access the key 'Sample_Blue' of that dictionary. This can be done using the brackets in the binding Path (maybe this doc can be useful: https://msdn.microsoft.com/en-us/library/ms742451.aspx):
<SolidColorBrush Color="{Binding [Sample_Blue], Source={StaticResource SampleDictionary}}"/>
Anyway, I think this approach can be improved by using a ViewModel that exposes as properties the dictionaries you are going to use:
class ViewModel: public BaseComponent
{
  ...
  ResourceDictionary* CommonResources() const;
  ResourceDictionary* ThemeResources() const;
  ...
};
...

Ptr<ResourceDictionary> commonRes = LoadXaml<ResourceDictionary>("assets/Resources/CommonResources.xaml");
Ptr<ResourceDictionary> themeRes =  LoadXaml<ResourceDictionary>("assets/Resources/ThemeResources.xaml");
Ptr<ViewModel> vm = *new ViewModel(commonRes.GetPr(), themeRes.GetPtr());
root->SetDataContext(vm.GetPtr());
<Border
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Border.Background>
    <SolidColorBrush Color="{Binding CommonResources[Sample_Blue]}"/>
  </Border.Background>

  <StackPanel>
    <Button Style="{Binding ThemeResources[Red_Button]}" Content="Cancel"/>
    ...
  </StackPanel>
</Border>
 
ZanAlex
Topic Author
Posts: 66
Joined: 16 Jan 2015, 17:46

Re: Specify ResourceDictionary and ResourceKey in Binding

17 Mar 2015, 10:03

Thanks, your solution is indeed better!

Yet another question though :) My view model looks something like this:
class ViewModel : public BaseComponent
{
[...]
    NsProp("Icons", &ViewModel ::GetIconDictionary); // ResourceDictionary 
    NsProp("Item", &ViewModel ::GetItem, &ViewModel ::SetItem); //Other ViewModel
[...]
}

class Item : public BaseComponent
{
[...]
    NsProp("IconID", &Item ::GetIconDictionary); // NsString
[...]
}
What I would like to do is interrogating Icons with the value of IconID. I guess I can add another property processing the value in code-behind and bind to it in Xaml. But I think using a converter would be more reusable. I've alreadu done something similar before (with your help if I remember correctly :) ), looking like that:
NsBool TryConvert(Noesis::Core::BaseComponent* value,
    const Noesis::Core::Type* targetType,
    Noesis::Core::BaseComponent* parameter,
    Noesis::Core::Ptr<Noesis::Core::BaseComponent>& result)
{
    Noesis::Core::BaseComponent* pResult = nullptr;
    if (nullptr != value)
    {
        Noesis::Gui::ResourceDictionary* pParam = NsDynamicCast<Noesis::Gui::ResourceDictionary*>(parameter);
                    
        const NsChar* strKey = Noesis::Core::Boxing::Unbox<NsString>(value).c_str();

        Noesis::Core::Ptr<Noesis::Gui::ResourceKeyString> pResKeyStr =
            Noesis::Gui::ResourceKeyString::TryCreate(strKey);

        if (nullptr != pParam && 0 != pResKeyStr && pParam->Find(pResKeyStr.GetPtr(), pResult))
        {
            result.Reset(pResult);
            return true;
        }
    }
    result.Reset();
    return false;
}
Used as such:
<TextBlock
    Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter=Localization}"/>
I can get the IconID key correctly, but I did not manage to pass the ResourceDictionary.

An idea how to perform this in a reusable way? Thanks!
 
User avatar
sfernandez
Site Admin
Posts: 1912
Joined: 22 Dec 2011, 19:20

Re: Specify ResourceDictionary and ResourceKey in Binding

18 Mar 2015, 16:13

What do you want to do when you write ConverterParameter=Localization in your binding?

Because in the converter code you are casting to ResourceDictionary and that is not correct, "Localization" would be parsed as a string.
 
ZanAlex
Topic Author
Posts: 66
Joined: 16 Jan 2015, 17:46

Re: Specify ResourceDictionary and ResourceKey in Binding

18 Mar 2015, 16:57

I want it to refer to the Localization field in the Data Context. I've tried {Binding Localization} instead, but it throws me a parsing error in the BuildTool because Binding.ConverterParameter is not a DependencyProperty of a DependencyObject. I should have mentionned it, sorry about that.

I managed to hack it by adding this:
<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
in the Resources of one of my hierarchy's control and using it as StaticResource. It works, but I want it to be created only once in my application and then be distributed to other controls.
 
User avatar
jsantos
Site Admin
Posts: 2904
Joined: 20 Jan 2012, 17:18
Contact:

Re: Specify ResourceDictionary and ResourceKey in Binding

24 Mar 2015, 13:23

As sergio said with this XAML
<TextBlock
Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter=Localization}"/>
you will be getting a string in the ConverterParameter field. You should be using a binding to the Dictionary instead:
<TextBlock
Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter={Binding Localization}}"/>
Please, tell me if this works for you.

By the way, regarding localization, I recommend you reading this topic where you fill find several interesting ideas.
 
ZanAlex
Topic Author
Posts: 66
Joined: 16 Jan 2015, 17:46

Re: Specify ResourceDictionary and ResourceKey in Binding

24 Mar 2015, 14:16

I've tried your solution, but the BuildTool throws me the following error:
  Parsing Binding (@73,5).
  A 'BindingExpression' cannot be set on the 'Binding.ConverterParameter' property of type 'BaseComponent'. A 'BindingExpression' can only be set on a DependencyProperty of a DependencyObject
@73,5 being:
<TextBlock Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter={Binding Localization}}"/>
 
User avatar
sfernandez
Site Admin
Posts: 1912
Joined: 22 Dec 2011, 19:20

Re: Specify ResourceDictionary and ResourceKey in Binding

27 Mar 2015, 00:52

I want it to refer to the Localization field in the Data Context. I've tried {Binding Localization} instead, but it throws me a parsing error in the BuildTool because Binding.ConverterParameter is not a DependencyProperty of a DependencyObject. I should have mentionned it, sorry about that.

I managed to hack it by adding this:
<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
in the Resources of one of my hierarchy's control and using it as StaticResource. It works, but I want it to be created only once in my application and then be distributed to other controls.
In NoesisGUI, ResourceDictionaries are shared along all the xamls referencing them, as long as you keep a reference to them. So you can load a dictionary in your application at start, and then reference that same dictionary from some diferent xamls, and they will share the same data (styles, brushes, geometries, ...).
 
ZanAlex
Topic Author
Posts: 66
Joined: 16 Jan 2015, 17:46

Re: Specify ResourceDictionary and ResourceKey in Binding

30 Mar 2015, 11:54

I think I did not understand correctly what you're saying.

My understanding is that I can just add
<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
to Control's Resources everywhere and that the framework will only keep one instance of the dictionary?

But then, why use a DataContext containing Dictionaries as fields?
 
User avatar
sfernandez
Site Admin
Posts: 1912
Joined: 22 Dec 2011, 19:20

Re: Specify ResourceDictionary and ResourceKey in Binding

02 Apr 2015, 13:52

Probably the scenario you try to solve is perfect for MultiBinding (which is not yet supported), where you want to generate a value from several values coming from the DataContext:
<TextBlock>
    <TextBlock.Text>
        <MultiBinding Converter="{StaticResource localizationConverter}">
            <Binding Path="Item.IconID" />
            <Binding Path="Localization" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>
Until we implement MultiBindings your only option is to specify the Localization dictionary in the xaml and use a StaticResource to pass it inside the Converter via the ConverterParameter. As I said, if you keep a copy of the dictionary in the DataContext, any time you refer to it from the xaml, it will share the same dictionary data.

Who is online

Users browsing this forum: Google [Bot] and 1 guest