I'm in the process of translating a WPF window. I'm using WPF Localize Extension . So far I only have a Spanish translation for testing purposes in <Resource>.es.resx file . At design time translations work . So I guess I'm on the right track .
I have included menu items to translate the GUI dynamically at run time . Initially I tried this (naïve) command ...
public class CmdTranslateUI : ICommand
{
bool ICommand.CanExecute(object parameter)
{
// TODO: Query available translations
return true;
}
public event EventHandler CanExecuteChanged;
void ICommand.Execute(object parameter)
{
LocalizeDictionary.Instance.Culture = new CultureInfo(
(string) parameter);
}
}
... and menu items for each language are bound to it in XAML this way .
<MenuItem Header="Español" CommandParameter="es-ES">
<MenuItem.Command>
<l:CmdTranslateUI />
</MenuItem.Command>
</MenuItem>
The fact is that such approach is not working . Culture info remains set to "en-US" anyway . I read that on setting LocalizeDictionary.Instance.Culture its DictionaryEvent is triggered, so I thought this would update the GUI automatically . Obviously I'm wrong .
On the other hand , it seems current thread's culture won't influence library behavior either.
So I ask ...
Q:
What's the recommended approach to translate a window at run time with WPF Localize Extension ?
How could I list available translations ?
Thanks in advance .
It seems I introduced a typo by accident last time I compiled the library (or I had ghosts in my HDD / CPU) . Language switching is working now after setting LocalizeDictionary.Instance.SetCurrentThreadCulture .
Just for the record , this is what command class mentioned above should look like
public class CmdTranslateUI : ICommand
{
bool ICommand.CanExecute(object parameter)
{
CultureInfo ci = new CultureInfo((string)parameter);
foreach (CultureInfo c in ResxLocalizationProvider.Instance.AvailableCultures)
{
if (ci.Name == c.Name)
return true;
else if (ci.Parent.Name == c.Name)
return true;
}
return false;
}
public event EventHandler CanExecuteChanged;
void ICommand.Execute(object parameter)
{
LocalizeDictionary.Instance.SetCurrentThreadCulture = true;
LocalizeDictionary.Instance.Culture = new CultureInfo(
(string) parameter);
}
}
... at least that's a simple approach that will work as long as resource files l10n provider is active.
The LocalizeExtension's Culture property is independent of the threads UI culture. Sometimes this may be desired, because the threads culture does affect a lot of things. We are using the extension in our own project and settings the threads culture manually to match the LocalizeDictionary's culture.
Normally this should update the UI automatically. Make sure you are using the LocText markup extension in your XAML, e.g:
<TextBlock Text="{lex:LocText Bla:Bla:Bla}" />
Update:
To get the list of available translations you might try this:
Get all available cultures from a .resx file group
However I would recommend that you just provide a fix list of languages in code or if you are using an IoC container, register the available languages there.
This does not answer OP's question, but thought I'd post it here anyway for those wandering here from Google since the title of the question is relevant.
I was having a problem where my application would be partially translated when changing the language in runtime. At first thought it was a convention problem since I had separate resources for each page, but the real problem was that the last control in the first page failed to translate, so it broke the chain and as a result all the subpages didn't get translated.
I was translating the FallbackValue of a textblock, I was using this property to show a default text when the binding was null.
Anyway, to catch errors like this, you can subscribe to the following event:
LocalizeDictionary.Instance.MissingKeyEvent += (sender, args) =>
{
//Do something
};
Related
I'm trying to implement the Command Processor pattern using MVVM for WPF. The Command Processor pattern that I'm roughly following is the one described in the book Pattern-Oriented Software Architecture (Volume 1):
The Command Processor design pattern separates the request for a
service from its execution. A command processor component manages
requests as separate objects, schedules their execution, and provides
additional services such as the storing of request objects for later
undo.
My implementation requires that all possible UI actions that may be undoable be stored as commands. This is mostly compatible with WPF's Commands, except for TextBoxes. TextBoxes in WPF don't provide a Command (unlike Buttons and CheckBoxes).
I thought I solved this by adding a TextChangedCommand that executes when the TextChanged event is raised, like this:
<l:CommandTextBox
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
TextChangedCommand="{Binding SetNameCommand, Mode=OneWay}"
/>
But the problem is that the TextChangedCommand is executed not only when the user makes a change but whenever the data-bound property is changed, which happens when undoing the change. In an undo operation, I just want the property to be set to what it was previously, not run a command again.
It's like there needs to be a TextChangedByUserCommand, but before I try to implement this I'm wondering if I'm going about this the wrong way, or if someone has come up with a cleaner solution.
My first choice would be to track the operation which is undoing the change and react on the change.
private void ResetCommandTextbox()
{
//some stuff...
IsViewModelUndoing = true;
Name = oldValue;
IsViewModelUndoing = false;
}
public void TextChangedCommand(...)
{
if(IsViewModelUndoing)
{
return;
}
// your other stuff which your doing
}
I'm currently trying to figure out how to automate UI testing for my WPF application and I have troubles getting it to work.
The XAML of MyControl (which extends UserControl) contains the following CheckBox:
<CheckBox Name="IsFooCheckBox"
IsChecked="{Binding Path=IsFoo, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
The binding points to a custom data context that implements INotifyPropertyChanged and that contains the following property:
private bool _isFoo;
public bool IsFoo
{
get { return _isFoo; }
set
{
_isFoo = value;
OnPropertyChanged("IsFoo");
}
}
The binding is working in production (in the debugger is can see that _isFoo is updated whenever I toggle the checkbox).
I'd like to have a test now that toggles the checkbox and checks that the data context is updated (or to check logic that is implemented in the code-behind). The WPF UI Automation framework seems to be exactly what I am looking for, so I wrote the following NUnit test:
var myContext = ...
var sut = new MyControl
{
DataContext = myContext
};
var peer = new CheckBoxAutomationPeer(sut.IsFooCheckBox);
var pattern = peer.GetPattern(PatternInterface.Toggle) as IToggleProvider;
pattern.Toggle();
Assert.That(sut.IsProvidingProfileCheckBox.IsChecked.Value); // works
Assert.That(myContext.IsFoo); // fails
While the first Assert passes, the second one fails. I do not understand why this happens... it seems that the binding in the XAML file is ignored or that the update is not triggered. Does anybody have a suggestion how to fix my test? Is this even possible?
Issue originates here
public bool IsFoo
{
get { return _IsFoo; }
set
{
_isFoo = value;
OnPropertyChanged("IsFoo");
}
}
Once you have invoked
pattern.Toggle();
you implicitly invoke setter of IsFoo which raises PropertyChanged event and in turn forces to refresh UI elements which have binding associated with IsFoo - to cut a long story short, getter is invoked and instead of _isFoo it returns _IsFoo. You mistook variable.
Try to avoid calling OnPropertyChanged method with explicit property name. Instead of this use CallerMemberName attribute which retrieves property name.
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
Then you only need below invocation.
OnPropertyChanged();
I had similar problem. I was populating textbox with following code:
ValuePattern valuePattern = promptBox.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
valuePattern.SetValue(value);
And depending on the value in text box, another button state was supposed to change from Disabled to Enabled.
I've noticed that after automation code above executed, clicking on the window by hand triggered binding to evaluate.
So I just added
System.Windows.Forms.SendKeys.SendWait("{TAB}");
after SetValue and it started to work just fine.
It took me some time to figure this out, but in the end, it is kind of obvious and described in many places all over the internet. I guess everything is easy as soon as you know what to look for...
The problem of coded UI tests is that the bindings are not resolved automatically. The resolving has to be initiated by calling Window.ShowWindow.
I extended my test and added the following snippet:
Window window = new Window
{
Content = sut // the control to test
};
window.Show();
Adding this call instantaneously fixed the strange test behavior that I described in my post.
The direct follow-up problem is that this call requires an active UI thread. Because of this, it might be tricky to get the test to run on a build server in a continuous integration environment. However, this depends on the environment (esp. the build server) and is a different question.
I have a MvxViewController and in the ViewDidLoad i bind the button click to the viewmodel. When the button is clicked I open another view in which I will need to return a string back to my first view
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand);
set.Apply();
}
public ICommand MyButtonCommand
{
get
{
_myButtonCommand = _myButtonCommand ?? new MvxCommand(MyButtonCommandClick);
return _myButtonCommand;
}
}
private void MyButtonCommandClick()
{
ShowViewModel<ViewModelNumber2>();
}
After some logic is ran in my second view I want to return the string
private void SomeMethodInViewModelNumber2()
{
//Raise event that will get pickup up in MyView
//Or somehow get "SomeString"
if (OnMyResult != null)
OnMyResult ("SomeString");
}
The problem is that I don't want to send the string back using the messenger. I have my reasons but basically because ViewModelNumber2 can be opened from many different places and works slightly different and managing the different messages that would need to be sent back and where to subscribe to these messages would be a mess
Is there any way that I can do something like the below?
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand).OnMyResult((myString) => {Process(myString)});
set.Apply();
}
Or perhaps when I create ViewModelNumber2 I should pass a callBack into the constructor and use that to send the string back from ViewModelNumber2 to MyView1ViewModel
ShowViewModel<ViewModelNumber2>(OnMyResult);
What is the best way to do this?
In short: I don't know what "the best way to do this" is.
The area of ChildViewModel-ParentViewModel messages is complicated - especially because on platforms like Android using Activities and WindowsPhone using Pages you have no guarantee that the ParentViewModel will be in memory when the Child is shown. (Note: this isn't a problem on iOS as its "app suspension" model is simpler)
When I do need one ViewModel returning data to another, then:
Often I try to implement the data collection views as "popup dialogs" rather than as "whole pages" - this makes the parent-child ViewModel relationship more correct - and ensures the parent ViewModel will be in memory when the child closes.
Often I recommend people use a Messenger-based technique like Greg describes in: http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/
often I've done this messaging via background services rather than via ViewModel-ViewModel messaging (a bit like the way screens are updated in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-17-CollectABull-Part6)
Another solution I've used is to:
implement a IDropBoxService singleton - with an API like void Deposit(key, value) and bool TryCollect(key, out value)
allow the closing "child" ViewModels to leave "values" when they close
implement IVisible functionality in my "parent" ViewModel - like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-42-Lifecycles/Lifecycle.Core/ViewModels/FirstViewModel.cs#L10
use the IVisible method to check for messages
To implement anything perfectly, you really should add serialisation code to make sure this all works during "tombstoning" on all platforms... but often this is overkill - for a simple data collection dialog users often don't need "perfect" tombstoning support.
I have a WinRT app which is up and running nicely using the caliburn.micro INavigationService (FrameAdapter). The problem is all of the pages in my app are full pages and they repeat a lot of view xaml markup. What I would like to do would be to have more of a "MasterPages" type of architecture where I have a shell view which defines the main layout and contains a ContentControl and uses the Conductor pattern to switch out the contents of the ContentControl. I have had success with this sort of architecture in the past with WPF and Silverlight so I assume it is possible in WinRT.
The problem is there seems to be a disconnect between the navigation infrastructure (using the FrameAdapter) and the Conductor infrastructure using a ContentControl bound to the ActiveItem of the conductor (like the SimpleNavigation sample).
In the conductor scenario I use ActivateItem:
ActivateItem(new MyViewModel());
but with the INavigationService I use NavigateToViewModel:
navigationService.NavigteToViewModel<MyViewModel>();
and the two don't seem to be connected as far as I can tell.
One idea I had was to create a ConductorNavigationService which implements INavigationService and basically handles the creation and activation of the child screens. While it seems possible it doesn't seem very straight forward so I figured I would check to see if there is an already supported way to do this in caliburn.micro.
Ok my understanding might be a little strained as I've not used WinRT or the INavigationService but I assume Frame is part of RT and INavigationService provides viewmodel resolution and navigation for the frames.
My other assumption is that your frame is a bit like your conductor already, and when you call 'Navigate()' on the frame, it just replaces the content of the frame with the newly specified content. If this is the case then CM is doing view first resolution for viewmodels.
Since you want to go the conductor route, it sounds like you want to ditch the CM implementation of INavigationService and just roll your own to handle the INavigationService navigation methods (e.g. skip the Frames Navigate() method).
A quick look at the CM source reveals that all NavigationService is doing is handling the Navigate events on the frame and then doing VM resolution and setting up the view (something that conductor probably already does). All you would need to do is ensure that your INavigationService implementation just loads the specified view into the shell instead of navigating the frame
You could just need to steal the constructor code for NavigationService and change the implementation of Navigate(), then just call ActivateItem(x) on your shell where x is the instance of the VM. CM will take care of the rest (I think CM boostrapper will already setup your root 'Frame' too so you shouldn't need to worry about that).
e.g.
An implementation may look more like this (bear in mind this is just something I thrown together and may be barefaced lies!):
public class NewFrameAdapter : INavigationService
{
private readonly Frame frame;
private readonly IConductActiveItem shell;
private event NavigatingCancelEventHandler ExternalNavigatingHandler = delegate { };
public NewFrameAdapter(Frame frame)
{
this.frame = frame;
// Might want to tighten this up as it makes assumptions :)
this.shell = (frame as FrameworkElement).DataContext as IConductActiveItem;
}
public bool Navigate(Type pageType)
{
// Do guardclose and deactivate stuff here by looking at shell.ActiveItem
// e.g.
var guard = shell.ActiveItem as IGuardClose;
if (guard != null)
{
var shouldCancel = false;
guard.CanClose(result => { shouldCancel = !result; });
if (shouldCancel)
{
e.Cancel = true;
return;
}
}
// etc
// Obviously since the guard is probably async (assume it is, if not you are ok to continue!) you'd have to not call this code right
// here but I've just stuck it in here as an example
// edit: looking at the code above (the guard code) it looks like this is all sync so the below code should be fine
// You might get away with calling shell.ActivateItem(pageType) as I'm not sure
// if the viewmodel binder in RT would resolve this all for you, but if it doesnt...
// Init the view and then resolve the VM type
ViewLocator.InitializeComponent(pageType);
var viewModel = ViewModelLocator.LocateForView(pageType);
// Activate the VM in the shell)
shell.ActivateItem(viewModel);
}
It shouldn't be too difficult to roll this your own way. Does this help you at all?
Then your XAML would be pretty simple:
<Frame blah blah>
<SomeStaticContent />
<ContentControl x:Name="ActiveItem" /> <!-- The dynamic bit... -->
<SomeMoreStaticContent />
</Frame>
I'm thinking that this will probably be a hybrid of view-first and viewmodel-first since your root Frame will be using view-first, and your conductor will be using ActivateItem() which takes a viewmodel and then resolves the view when the binder kicks in, but if my assumptions are ok, it should work
I just created a user control.
This control also makes use of my static Entity Framework class to load two comboboxes.
All is well and runs without a problem. Design and runtime are working.
Then when I stop the application all the forms that contain my UserControl don't work any more in design time. I just see two errors:
Error1:
The specified named connection is either not found in the configuration, not intended to be used with the EntityClient provider, or not valid.
Error 2:
The variable ccArtikelVelden is either undeclared or was never assigned.
(ccArtikelVelde is my UserControl)
Runtime everything is still working
My static EF Repositoy class:
public class BSManagerData
{
private static BSManagerEntities _entities;
public static BSManagerEntities Entities
{
get
{
if (_entities == null)
_entities = new BSManagerEntities();
return _entities;
}
set
{
_entities = value;
}
}
}
Some logic happening in my UserControl to load the data in the comboboxes:
private void LaadCbx()
{
cbxCategorie.DataSource = (from c in BSManagerData.Entities.Categories
select c).ToList();
cbxCategorie.DisplayMember = "Naam";
cbxCategorie.ValueMember = "Id";
}
private void cbxCategorie_SelectedIndexChanged(object sender, EventArgs e)
{
cbxFabrikant.DataSource = from f in BSManagerData.Entities.Fabrikants
where f.Categorie.Id == ((Categorie)cbxCategorie.SelectedItem).Id
select f;
cbxFabrikant.DisplayMember = "Naam";
cbxFabrikant.ValueMember = "Id";
}
The only way to make my forms work again, design time, is to comment out the EF part in the UserControl (see above) and rebuild.
It's very strange, everything is in the same assembly, same namespace (for the sake of simplicity).
Anyone an idea?
Looks like you're somehow executing database code in design mode. To prevent this, hunt down the control and method causing this, and use:
if (DesignMode)
return
Also, it's a very bad idea to cache the database context statically. It will cause problems with multithreading, and also when you're doing inserts and deletes. The database context is meant to be used for a single "unit of work", is adding 2, and removing 3 other objects and calling SaveChanges once.
I faced the same problem,
In my case , I have added some database codes in user control loading event which were using some libraries, which weren't loaded till runtime.
Hence it is advisable, not to write any database code in user control load event.
Hope , this helps you !
this error show if you call the function "LaadCbx()" on constructor of userControl.
because the initialization on entity framework exist into this function.
the solution is to call this function "LaadCbx()" in the constructor of the parent form.