The application I'm trying to get to work uses MVVM. The biggest part of this post is an explanation of what I tried and what I got working. The question is near the bottom of the post. The used Localizer
class is only used here as an example and can easily be replaced with another class.
I have a class library
with a Localizer
class. This purpose of this class is to change the language of the application on the fly, without having to restart the application. The `Localizer has to be instantiated before it can be used but once instantiated, should be usable in the entire application. (The class uses the application resources to localize the application.)
My first approach I could think of is making the Localizer
a public static class
with a public static void Initialize
method. This way I could initialize the Localizer
like this
Localizer.Initialize(/* Needed arguments here */);
on the application level and use it wherever I want in either my class library or application like this
string example = Localizer.GetString(/* A key in the resource dictionary */);
Considering the class library is written by me (only I have the source code) and used by other people who have no clue about the source code (they only know what the class library can do), I would have to explicitly state in some sort of "How to use this class library" that they need to call Localizer.Initialize
on the application level in order to use it everywhere in their application.
After doing some research a lot of people state that this is a bad practice and suggest investigating what Dependency Injection (DI) and Inversion of Control (IoC), so I did. I learned that DI is doing about the same as my first approach but remove the static stuff, use Localizer.Initialize
as the constructor and inject the instantiated class in my other classes.
So the second approach is dependency injection and this is where I'm stuck. I managed to let my application compile with a single MainWindowView
and MainWindowViewModel
with the following code:
protected override void OnStartup(StartupEventArgs e)
{
ILocalizer localizer = new Localizer(Current.Resources, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name, "Languages", "Language", "en");
var mainWindowViewModel = new MainWindowViewModel(localizer);
var mainWindowView = new MainWindowView { DataContext = mainWindowViewModel };
mainWindowView.Show();
base.OnStartup(e);
}
What the above code does is, is inject the localizer
into MainWindowViewModel
. This way no additional code is added to the MainWindowView
code behind and has the view a view model bound to.
In MainWindowViewModel
the constructor is like this (note that the message box is called somewhere else but was moved here to minimize the code):
ILocalizer _localizer;
public MainWindowViewModel( ILocalizer localizer)
{
_localizer = localizer;
MessageBox.Show(_localizer.GetString(/* A key in the resource dictionary */));
}
The above code is still compiling and running fine without exceptions. The problem occurs when I have either UserControls
in my class library
with a view and view model that also require the localizer
instance.
I think I have a solution for when I have a UserControl
in my application assembly but it feels like it is more 'complex' then when I would use a static class
. I usually just bind the view model of a UserControl
to the view in its code behind. This way I can simply add the UserControl
to my .xaml code like this <local:UserControl1 />
without a lot of extra hustle. This way the view model parent view model doesn't have to be concerned about child view models.
With DI I would do something like this in my parent (the child would be the same as in the previous block of code):
View
<n:UserControl1 DataContext="{Binding UC1ViewModel}" />
ViewModel
public UserControl1ViewModel UC1ViewModel { get; set; }
ILocalizer _localizer;
public MainWindowViewModel(ILocalizer localizer)
{
_localizer = localizer;
UC1ViewModel = new UserControl1ViewModel(localizer);
}
The above is still all working fine, no problems so far. The only thing that changed is that the DataContext
is set in the parents view and the content of the DataContext
is set in the view model of the parent.
The question
I also have several UserControls
in my class library
. These can be used by the users of the class library
but they can't alter them. Most of these UserControls
are some fixed pages
that display information about a person, car, etc. The intention is that, for example the label with the name of the person is "Name" in English, "Naam" in Dutch, etc. (which are all declared in the view and are working fine) but there is also text in the code behind that has to be localized and this is where I'm stuck.
Should I approach the problem the same way as I'm doing with a UserControl
in my applications assembly? This feels really counterproductive if let say 20+ of these UserControls
are used in a single parent view.
I also feel that I'm not implementing DI 100% correctly.