Hi Alex,
Because the MultiBinding feature is not available, this scenario must be solved using some kind of workaround. As you are using the native SDK you have the option to create a complex converter that is able to directly bind properties from the DataContext, for example, the selected localization dictionary.
I created the following sample that shows how to do it:
LocalizationConverter
class LocalizationConverter : public DependencyObject, public IValueConverter,
public IUITreeNode // this interface allows the Binding walk up the UI tree to find the DataContext
{
public:
LocalizationConverter() : mOwner(0) { }
~LocalizationConverter() { }
ResourceDictionary* GetResources() const
{
return GetValue<Ptr<ResourceDictionary> >(ResourcesProperty).GetPtr();
}
void SetResources(ResourceDictionary* resources)
{
SetValue<Ptr<ResourceDictionary> >(ResourcesProperty, resources);
}
// From IValueConverter
//@{
Ptr<BaseComponent> Convert(BaseComponent* value,
const Type* targetType, BaseComponent* parameter)
{
Ptr<BaseComponent> result;
if (TryConvert(value, targetType, parameter, result))
return result;
NS_ERROR("LocalizationConverter: failed to convert value '%'", value->ToString());
}
NsBool TryConvert(BaseComponent* value,
const Type* targetType, BaseComponent* parameter,
Ptr<BaseComponent>& result)
{
ResourceDictionary* resources = GetResources();
if (resources != 0)
{
Ptr<ResourceKeyString> key = ResourceKeyString::Create(value->ToString().c_str());
BaseComponent* resource;
if (resources->Find(key.GetPtr(), resource))
{
result.Reset(resource);
return true;
}
}
result.Reset();
return false;
}
Ptr<BaseComponent> ConvertBack(BaseComponent* value,
const Type* targetType, BaseComponent* parameter)
{
NS_ERROR("ConvertBack not implemented");
}
NsBool TryConvertBack(BaseComponent* value,
const Type* targetType, BaseComponent* parameter,
Ptr<BaseComponent>& result)
{
NS_ERROR("ConvertBack not implemented");
}
//@}
// From IUITreeNode
//@{
IUITreeNode* GetNodeParent() const { return mOwner; }
void SetNodeParent(IUITreeNode* parent) { mOwner = parent; }
Core::BaseComponent* FindNodeResource(IResourceKey* key) const
{
if (mOwner)
return mOwner->FindNodeResource(key);
else
return DependencyProperty::GetUnsetValue();
}
Core::BaseComponent* FindNodeName(const NsChar* name) const
{
if (mOwner)
return mOwner->FindNodeName(name);
else
return 0;
}
ObjectWithNameScope FindNodeNameAndScope(const NsChar* name) const
{
if (mOwner)
return mOwner->FindNodeNameAndScope(name);
else
return ObjectWithNameScope();
}
//@}
public:
static const DependencyProperty* ResourcesProperty;
private:
IUITreeNode* mOwner;
NS_IMPLEMENT_INLINE_REFLECTION(LocalizationConverter, DependencyObject)
{
NsMeta<TypeId>("LocalizationConverter");
NsImpl<IValueConverter>();
NsImpl<IUITreeNode>();
Ptr<DependencyData> data = NsMeta<DependencyData>(TypeOf<SelfClass>());
data->RegisterProperty<Ptr<ResourceDictionary> >(ResourcesProperty, "Resources",
PropertyMetadata::Create(Ptr<ResourceDictionary>::Null()));
}
};
const DependencyProperty* LocalizationConverter::ResourcesProperty;
ViewModel
NS_DECLARE_SYMBOL(Id1)
NS_DECLARE_SYMBOL(Id2)
NS_DECLARE_SYMBOL(Localization)
typedef Delegate<void (BaseComponent* param)> CommandAction;
class DelegateCommand : public BaseCommand
{
public:
DelegateCommand(const CommandAction& action) : _action(action) { }
NsBool CanExecute(Core::BaseComponent* param) const { return true; }
void Execute(Core::BaseComponent* param) const { _action(param); }
private:
CommandAction _action;
};
class ViewModel : public BaseComponent, public INotifyPropertyChanged
{
public:
ViewModel()
{
// Filled here just for testing, dictionaries can be loaded from a .xaml
Ptr<ResourceKeyString> key;
Ptr<BaseComponent> val;
_localizationEnglish = *new ResourceDictionary();
_localizationSpanish = *new ResourceDictionary();
key = ResourceKeyString::Create("hi");
val = Boxing::Box<NsString>("Hello");
_localizationEnglish->Add(key.GetPtr(), val.GetPtr());
val = Boxing::Box<NsString>("Hola");
_localizationSpanish->Add(key.GetPtr(), val.GetPtr());
key = ResourceKeyString::Create("bye");
val = Boxing::Box<NsString>("Goodbye");
_localizationEnglish->Add(key.GetPtr(), val.GetPtr());
val = Boxing::Box<NsString>("Adios");
_localizationSpanish->Add(key.GetPtr(), val.GetPtr());
_selectedLocalization = _localizationEnglish.GetPtr();
_switchLanguage = *new DelegateCommand(MakeDelegate(this, &ViewModel::OnSwitchLanguage));
}
~ViewModel()
{
_destroyed(this);
}
const NsChar* GetId1() const { return "hi"; }
const NsChar* GetId2() const { return "bye"; }
ResourceDictionary* GetLocalization() const { return _selectedLocalization; }
DelegateCommand* GetSwitchLanguage() const { return _switchLanguage.GetPtr(); }
void OnSwitchLanguage(BaseComponent* param)
{
if (_selectedLocalization == _localizationEnglish.GetPtr())
{
_selectedLocalization = _localizationSpanish.GetPtr();
}
else
{
_selectedLocalization = _localizationEnglish.GetPtr();
}
_propertyChanged(this, PropertyChangedEventArgs(NSS(Localization)));
_propertyChanged(this, PropertyChangedEventArgs(NSS(Id1))); // force update bindings to Id1
_propertyChanged(this, PropertyChangedEventArgs(NSS(Id2))); // force update bindings to Id2
}
PropertyChangedEventHandler& PropertyChanged() { return _propertyChanged; }
DestroyedEventHandler& Destroyed() { return _destroyed; }
private:
ResourceDictionary* _selectedLocalization;
Ptr<ResourceDictionary> _localizationEnglish;
Ptr<ResourceDictionary> _localizationSpanish;
Ptr<DelegateCommand> _switchLanguage;
PropertyChangedEventHandler _propertyChanged;
DestroyedEventHandler _destroyed;
NS_IMPLEMENT_INLINE_REFLECTION(ViewModel, BaseComponent)
{
NsImpl<INotifyPropertyChanged>();
NsProp("Id1", &ViewModel::GetId1);
NsProp("Id2", &ViewModel::GetId2);
NsProp("Localization", &ViewModel::GetLocalization);
NsProp("SwitchLanguage", &ViewModel::GetSwitchLanguage);
}
};
Now we can use this classes in a xaml control:
TestControl.xaml
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TestControl">
<Grid.Resources>
<LocalizationConverter x:Key="localizationConverter" Resources="{Binding Localization}"/>
</Grid.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding Id1, Converter={StaticResource localizationConverter}}"/>
<TextBlock Text="{Binding Id2, Converter={StaticResource localizationConverter}}"/>
<Button Content="Switch Language" Command="{Binding SwitchLanguage}"/>
</StackPanel>
</Grid>
TestControl.cppstruct TestControl : public Grid
{
void OnPostInit()
{
Ptr<ViewModel> vm = *new ViewModel();
SetDataContext(vm.GetPtr());
}
NS_IMPLEMENT_INLINE_REFLECTION(TestControl, Grid)
{
NsMeta<TypeId>("TestControl");
}
};
And don't forget to register the components that are used in the xaml file, so parser can create them when building the xaml resource:
void NsRegisterComponents(...)
{
NS_REGISTER_COMPONENT(LocalizationConverter)
NS_REGISTER_COMPONENT(TestControl)
}