WPFSharp.Globalizer
WPFSharp.Globalizer copied to clipboard
MVVM LocalizationBinding does not seem to work
Running MVVMExample, labels that are bound to the view models using LocalizationBinding do not switch languages. Notice "Last Name" and "Age" do not update when language is switched, while elements directly connected to ResourceKeys do.
I did some poking around in the source to see if I could fix the issue, but didn't find anything obvious.
For anyone else that runs into this problem:
Simply attach to the ResourceDisctionaryChangedEvent
in your ViewModel and fire NotifyPropertyChanged
to update properties.
More or less this:
var glApp = CommonUI.App.Current as GlobalizedApplication;
glApp.GlobalizationManager.ResourceDictionaryChangedEvent += (source, args) =>
{
NotifyPropertyChanged(string.Empty);
};
Awsmolak,
That will work just fine. Here is more info if you are interested.
I used to have code similar to yours that in the MVVM example, probably sometime before I actually committed it. I don't think I ever solved it how I would prefer.
Solving it in the ViewModel requires:
- Either adding the code to every globalized ViewModel
- Adding the code to a base class and requiring all ViewModels to implement that base class.
While both those options are valid solutions, you will find push back from many in the MVVM community, Many don't feel that a ViewModel should require a base class or require code be added.
What I was trying to do was add the event in the LocalizationBinding object. However, I never solved it that way. In WPF Binding there are three objects; 1. The WPF control, the ViewModel object, the Binding. I could update binding from WPF Control. I could update binding from the ViewModel. I never figured out how to update a Binding from the Binding itself. I think the problem was because BindingBase sealed the ProvideValue method.
LocalizationBinding just inherits from Binding which inherits from BindingBase which inherits from MarkupExtension. It may be, perhaps, that I need to inherit directly from MarkupExtension and roll my own LocalizationBinding.
So here is where I was stuck at:
public class LocalizationBinding : Binding
{
public LocalizationBinding()
{
}
public LocalizationBinding(string path)
: base(path)
{
Converter = new LocalizationConverter();
GlobalizedApplication.Instance.GlobalizationManager.ResourceDictionaryChangedEvent += OnResourceDictionaryChangedEvent;
}
private void OnResourceDictionaryChangedEvent(object source, ResourceDictionaryChangedEventArgs e)
{
//How to update Binding from the binding object.
}
new public object FallbackValue
{
get { return base.FallbackValue; }
set
{
base.FallbackValue = value;
ConverterParameter = value;
}
}
}
That makes sense.
I found one more related issue yesterday, not sure if it should be filed separately or not: For more complex MVVM applications I like to create design time instances of my ViewModels with mock data. This is much more robust than using FallbackValue, especially for complex types.
In these cases the design data is declared in XAML at the UserControl or Window scope:
d:DataContext="{Binding ViewModel, Source={d:DesignInstance Type=designData:MainWindowDesignData, IsDesignTimeCreatable=True}}"
However, during design time this results in the designer throwing a NullReferenceException at: LocalizationConverter.Convert(Object value, Type targetType, Object parameter, CultureInfo culture)
The designer does resume working if all LocalizationBindings set Converter="{x:Null}"
. This of course, breaks the localization binding.
I wasn't able to dig any deeper into this because of lack of time and I have still not be able to successfully get the debugger to step into code during design time.
I've got design-time debugging working before. I'll see if I can get it working again sometime and debug it.
Obviosly GlobalizedApplication.Instance is not design-time safe.
@Awsmolak and @rhyous I am binding my combo box with a dictionary of int and string, string is used obviously for Text displaying, but i am unable to figure out how to apply globalization on the values of combo box so that when user switches language on that windows the combo box items should get updates with the language user specified, here is my view model code :
VehicleTypes = new Dictionary<int, string>() { { 0, "VehicleType" }//GlobalizedApplication.Current.FindResource("VehicleType").ToString()} };
I have key in resource files for the languages xaml with key VehicleType and following is my binding in xaml, how to update the following to enable the language based combo box values, following is what i tried but of no use uptil now:
<ComboBox Style="{StaticResource ComboBoxFlatStyle}" ItemsSource="{Binding VehicleTypes}" SelectedValue="{Binding SelectedVehicleType}" SelectedValuePath="{Globalizer:LocalizationBinding Value}" DisplayMemberPath="{Globalizer:LocalizationBinding Value, }" IsSynchronizedWithCurrentItem="True" Height="30" Width="250" Grid.Row="1" > </ComboBox>
@ehsansajjad465 Does your view model subscribe to the GlobalizedApplication.ResourceDictionaryChangedEvent? Then whenever the resrouce changes, your ViewModel should call notifypropertychanged for all localized properties. See the first post at the top of this.
@rhyous Can you guide me with a simple example ?
Of course! I checked it into my MVVM example code.
https://github.com/rhyous/WPFSharp.Globalizer/commit/8aabf7a78c29aa624a9c962d20b716a80606bcc1
I simply added it to the constructor of PersonViewModel.