Specify ResourceDictionary and ResourceKey in Binding
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.
basicDictionary.xaml being:
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:
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.
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.
Code: Select all
<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>
Code: Select all
<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>
Code: Select all
<SolidColorBrush Color="{Binding Sample_Blue, Converter="ResArgConverter", ConverterParameter={StaticResource SampleDictionary}}"/>
Code: Select all
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;
}
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.
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Specify ResourceDictionary and ResourceKey in Binding
Hi,
This piece of xaml:
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):
Anyway, I think this approach can be improved by using a ViewModel that exposes as properties the dictionaries you are going to use:
This piece of xaml:
Code: Select all
<SolidColorBrush Color="{Binding Sample_Blue, Source={StaticResource SampleDictionary}}"/>
Code: Select all
<SolidColorBrush Color="{Binding [Sample_Blue], Source={StaticResource SampleDictionary}}"/>
Code: Select all
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());
Code: Select all
<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>
Re: Specify ResourceDictionary and ResourceKey in Binding
Thanks, your solution is indeed better!
Yet another question though My view model looks something like this:
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:
Used as such:
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!
Yet another question though My view model looks something like this:
Code: Select all
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
[...]
}
Code: Select all
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;
}
Code: Select all
<TextBlock
Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter=Localization}"/>
An idea how to perform this in a reusable way? Thanks!
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Specify ResourceDictionary and ResourceKey in Binding
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.
Because in the converter code you are casting to ResourceDictionary and that is not correct, "Localization" would be parsed as a string.
Re: Specify ResourceDictionary and ResourceKey in Binding
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:
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.
I managed to hack it by adding this:
Code: Select all
<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
Re: Specify ResourceDictionary and ResourceKey in Binding
As sergio said with this XAML
By the way, regarding localization, I recommend you reading this topic where you fill find several interesting ideas.
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=Localization}"/>
Please, tell me if this works for you.<TextBlock
Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter={Binding Localization}}"/>
By the way, regarding localization, I recommend you reading this topic where you fill find several interesting ideas.
Re: Specify ResourceDictionary and ResourceKey in Binding
I've tried your solution, but the BuildTool throws me the following error:
@73,5 being:
Code: Select all
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
Code: Select all
<TextBlock Text="{Binding Item.IconID, Converter={StaticResource ResArgConverter}, ConverterParameter={Binding Localization}}"/>
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Specify ResourceDictionary and ResourceKey in Binding
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, ...).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: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.Code: Select all<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
Re: Specify ResourceDictionary and ResourceKey in Binding
I think I did not understand correctly what you're saying.
My understanding is that I can just add
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?
My understanding is that I can just add
Code: Select all
<ResourceDictionary x:Key="Localization" Source="localization.xaml"/>
But then, why use a DataContext containing Dictionaries as fields?
-
sfernandez
Site Admin
- Posts: 2984
- Joined:
Re: Specify ResourceDictionary and ResourceKey in Binding
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:
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.
Code: Select all
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource localizationConverter}">
<Binding Path="Item.IconID" />
<Binding Path="Localization" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Who is online
Users browsing this forum: Semrush [Bot] and 84 guests