Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MVVM LocalizationBinding does not seem to work #6

Open
Awsmolak opened this issue Dec 2, 2016 · 7 comments
Open

MVVM LocalizationBinding does not seem to work #6

Awsmolak opened this issue Dec 2, 2016 · 7 comments

Comments

@Awsmolak
Copy link
Contributor

Awsmolak commented Dec 2, 2016

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);
};
@rhyous
Copy link
Owner

rhyous commented Dec 14, 2016

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:

  1. Either adding the code to every globalized ViewModel
  2. 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;
            }
        }
    }

@Awsmolak
Copy link
Contributor Author

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.

@rhyous
Copy link
Owner

rhyous commented Dec 14, 2016

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.

@ehsansajjad465
Copy link

ehsansajjad465 commented Feb 22, 2017

@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>

@rhyous
Copy link
Owner

rhyous commented Feb 22, 2017

@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.

@ehsansajjad465
Copy link

@rhyous Can you guide me with a simple example ?

@rhyous
Copy link
Owner

rhyous commented Feb 23, 2017

Of course! I checked it into my MVVM example code.

8aabf7a

I simply added it to the constructor of PersonViewModel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants