Is there any built-in tool to support multilanguage applications in WinRT? For instance I have few buttons with text content "Add", "Remove", "Edit" in english and it should be "Dodaj", "Usun", "Edytuj" and so on in polish. Can I automatically set this text to user language? (and how to automatically detect user language?). I could use language model and bind buttons content to ViewModel property but isn't there exist better way to do it?
This is well supported, and MS have a very good sample here: http://code.msdn.microsoft.com/windowsapps/Application-resources-and-cd0c6eaa
Setting the text of "static" content using the x:uid doesn't work if the elements are databound. For example you have an observable collection in your view model containing username view models and you try and do the following (pseudo code!!):-
<List ItemSource={Binding Users}>
<List.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:uid="ByUser"/>
<TextBlock Text={Binding Username}/>
</StackPanel>
</DataTemplate>
</List.ItemTemplate>
</List>
The text that should set the first textbox (based on the uid ByUser) won't get set. I work around this by wrapping up the ResourceLoader in a Globalisation Service and passing this into my "UsersName" view model, I would then expose a property called ByUserText, and bind on that. Not ideal, hopefully this will get fixed.
The only other thing you will need to use a globalisation service for is things like message boxes etc.
This is the globalisation service I pass around:-
using Windows.ApplicationModel.Resources;
public class GlobalisationService : IGlobalisationService
{
private readonly ResourceLoader resourceLoader;
public GlobalisationService()
{
resourceLoader = new ResourceLoader();
}
public string GetString(string key)
{
try
{
return resourceLoader.GetString(key);
}
catch
{
return "NOT FOUND: " + key;
}
}
public string this[string key]
{
get
{
return GetString(key);
}
}
}
Related
In JSON response:
results = "<p>This is a <b>paragraph</b></p><p>New paragraph with symbols > tags</p>";
XAML:
<Textblock Text={Binding results}/>
result:
This is a **paragraph** New Word
New paragraph with symbols > tags
You can use RichTextBlock to more easily match HTML DOM with XAML output. Unfortunately, there is not built-in API that will transform HTML into equivalent XAML for the control.
You can parse the HTML into known tags using HtmlAgilityPack and add items into RichTextBlock.Inlines manually. There is an old Docs article on this process, but it still applies. One of the examples it shows:
private static Inline GenerateBlockForNode(HtmlNode node)
{
switch (node.Name)
{
case "div":
return GenerateSpan(node);
case "p":
case "P":
return GenerateInnerParagraph(node);
case "img":
case "IMG":
return GenerateImage(node);
...
The individual GenerateXXX methods then generate appropriate inlines:
private static Inline GenerateSpan(HtmlNode node)
{
Span s = new Span();
AddChildren(s, node);
return s;
}
The easiest solution would be to use the code in this GitHub repo, which implements a lot the tag conversion and maybe you will be able to just copy-paste the converter to your project and get running.
Is it possible?:
//...
local:MvxBind="Text Format('{0} {1}', Stock, #string/in_stock)"/>
//...
I want to construct a text value using my property from ViewModel and string resource from strings.xml, but the example above does not work.
AFAIK it is not directly possible to bind to an Android string.
Working with Xamarin and Mvx you should use resx files to support internationalization (i18n).
You can easily access the resx file from a binding using an indexer on your ViewModel:
public abstract class BaseViewModel : MvxViewModel
{
public string this[string key] => Strings.ResourceManager.GetString(key);
}
Then in your View you can use it like:
local:MvxBind="Text Format('{0} {1}', Stock, [InStock])"
There is another way to bind strings in resx files that is using the ResxLocalization plugin and even though it does not support Format yet you can workaround it (you can check this issue Feature request: Combine MvxLang with Format to keep track of this)
Basically, you create your Strings.resx file in your PCL/NetStandard/Shared project and register it:
Mvx.RegisterSingleton(new MvxResxTextProvider(Strings.ResourceManager));
Then in your base view model you need to implement this property so your views and viewmodels have access to i18n:
public IMvxLanguageBinder TextSource => new MvxLanguageBinder("", GetType().Name);
Finally in your view you can call it using:
local:MvxLang="Text InStock"
Pay attention that it is using MvxLang instead of MvxBind. BTW you can use both of them but if you use Text in MvxLang then don't use it in MvxBind because problems will arise.
Finally you can combine the plugin with the indexer to lower the coupling between the ViewModel and the resx files and workaround the support of Format in the binding like this (taken from the issue abovementioned):
public abstract class BaseViewModel : MvxViewModel
{
private IMvxTextProvider _textProvider;
public BaseViewModel(IMvxTextProvider textProvider)
{
_textProvider = textProvider;
}
public string this[string key] => _textProvider.GetText("", "", key);
}
and in your view (because of Format we cannot use MvxLang here):
local:MvxBind="Text Format('{0} {1}', Stock, [InStock])"
HIH
I have a command which interacts with an API. If the command doesn't return a desired result it sets a property of the ViewModel called Error.
I want to bind Error to a UIAlertController in my View and have it display when the error occurs.
Here's roughly what I have (although obviously the visibility converter isn't the way to go). I should add that I'm aware PresentViewController should be used to display the UIAlertController.
UIAlertController myAlert = UIAlertController.Create ("", Error.Text, UIAlertControllerStyle.Alert);
set.Bind(myAlert).For("Visibility").To((myViewModel vm) => vm.Error).WithConversion("Visibility");
Check out Observer design pattern.
The way I prefer to achieve that is simple:
Create class which inherits from MvxMessage - let say ShowAlertDialogMessage with properties like title, content and so on.
Create abstract MessageObserver where TMessage : MvxMessage class, ex.:
public interface IMessageObserver
{
void Subscribe(IMvxMessenger messenger);
void Unsubscribe();
}
public abstract class MessageObserver<TMessage> : IMessageObserver where TMessage : MvxMessage
{
public void Subscribe(IMvxMessenger messenger) {
messenger.SubscribeOnMainThread<TMessage>(OnMessageDelivered);
}
public abstract void OnMessageDelivered(TMessage message);
}
Create MessageObserverController
public class MessageObserverController {
public void SubscribeObserver(IMessageObserver msgObserver) {
msgObserver.Subscribe(messenger);
}
.. unsubscribe, dispose and so on goes here
}
Implement ShowAlertDialogMessageObserver class (inherit from MessageObserver<ShowAlertDialogMessage>() which shows UIAlertViewController with data from ShowAlertDialogMessage (title, content and so on). Pass root UIViewController as constructor if needed (you will register MessageObservers in your viewcontrollers anyway - so that's not a problem).
Use MessageObserverController in your ViewControllers (preferably create base view controller to simplify things).
VoilĂ - you get reusable UI logic, which you can raise by publishing message in your PCL ViewModel (without creating any platform-specific coupling!).
I have a GridView and the IsItemClickEnabled is set to true. In this case, the ItemClick event is fired, and it calls a delegate with a sender and an ItemClickEventArgs. I'm using MVVM (Caliburn.Micro), and my view models are all sitting in a portable class library (PCL). All command routing, etc., is done by Reactive-UI. The View's data context is set to the view model, as you might expect.
The problem is ItemClickEventArgs is not available in a PCL configuration as far as I can tell. So I am currently at a loss as how to sink that event in my view model. Heck - even without using a reactive ui command.
Here is my grid view defined:
<GridView x:Name="PaperList" Margin="0" Grid.Column="1" IsItemClickEnabled="True" ItemClick="whatgoeshere?"/>
My simplest idea is to replace "whatgoeshere?" with a method in the view's code-behind, for example:
public void ForkItUp(object sender, ItemClickEventArgs args)
{
// How do I get this information back to the view model?
}
And of course, that works, that method gets called. But once I'm there, how the heck do I get the information back into my View Model? As far as I know the view knows nothing about the view model.
The only thing I can think of is I need to create some property that contains the PCL object that was clicked on. This method would stuff the item into that property, and through some magic of data-binding that would be sent back to the view model. If so, I have no idea how to participate in the data-binding process to get that right! Ack! :-)
I don't know if this makes a difference yet, but I'm not using universal apps! And I'm using a nasty hybrid of Caliburn.Micro to hook up my views, and Reactive-ui and rx to run all my commands. I don't like making anything easy!
Here's how I would do it
ReactiveCommand ItemClicked { get; protected set; }
public MyCoolViewModel()
{
ItemClicked = new ReactiveCommand();
}
// In My View
gridView.ItemClicked += (o,e) => ViewModel.ItemClicked.Execute(e.Item);
As far as I know the view knows nothing about the view model.
This isn't how MVVM works - the View can know all kinds of stuff about the ViewModel, that's the whole idea! However, the ViewModel can know nothing about any Views.
I found two different approaches worked, based on the answer that Paul gave. I ended up using the second. In the first approach I added a public method to my MyViewModel:
public void ClickOnTile(PaperTileViewModel pt)
{
code to run;
}
Then, in my View I added the following:
public void ForkItUp(object sender, ItemClickEventArgs args)
{
var obj = DataContext as MyViewModel;
obj.ClickOnTile(args.ClickedItem as PaperTileViewModel);
}
And the XAML for my view for the grid view:
<GridView x:Name="PaperList" Margin="0" Grid.Column="1" IsItemClickEnabled="True" ItemClick="ForkItUp"/>
I'm a C++ guy by training, so the lack of type safety got to me a bit and lead to this second solution (which is the one that I ended up committing). So I ended up doing the following in the end. I first created an interface in my ViewModel's PCL:
public interface IHomePageViewCallback
{
void FinalizeVMWiring(HomePageViewModel homePageViewModel);
}
Then, in the View I implemented the interface:
public sealed partial class MyPageView : Page, IHomePageViewCallback
{
public MyPageView()
{
this.InitializeComponent();
}
public void FinalizeVMWiring(MyViewModel myVM)
{
PaperList.ItemClick += (s, args) => myVM.NavigateToPaperTile.Execute((args.ClickedItem) as PaperTileViewModel);
}
}
where NavigateToPaperTile is a ReactiveCommand similar to what Paul has in his answer. This funny call back is needed because as far as I can tell there is no easy way to get a typed version of the ViewModel in Caliburn.Micro.
For completeness here is the XAML:
<GridView x:Name="PaperList" Margin="0" Grid.Column="1" IsItemClickEnabled="True"/>
I have a seemingly simple use case. There is a ICsvReader component. Let's name it simply Reader here. We load a known set of CSV files and some of them have headers and some don't. Currently there are multiple readers: Reader_Skips1Row, Reader_Skips2Rows etc.
Is there a way to register only one component and have Windsor look at the component key, strip the "_Skips..." part and resolve the required component with relevant properties set?
I have tried subresolver and facility with no luck.
EDIT
Yes there is only one implementation but it is used as a dependency and configured to be resolved by name. The reader is configured in code
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 2 } )
.Named("CommaSeparetedCsvReader_Skips2Rows")
.Lifestyle.Transient
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 1 } )
.Named("CommaSeparetedCsvReader_Skips1Row")
.Lifestyle.Transient
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.Named("CommaSeparetedCsvReader")
.Lifestyle.Transient
These are used as dependency in a processor class. It is configured in XML, so that in can be manipulated at runtime
<component id="Processor
type="Processor">
<parameters>
<reader>CommaSeparetedCsvReader_Skips2Rows</reader>
</parameters>
</component>
Ideally I would like to register only the CommaSeparetedCsvReader component but when an attempt is made to resolve CommaSeparetedCsvReader_Skips2Rows it should strip the suffix, parse it and change the properties accordingly.
Is it possible to somehow modify the Resolve() behavior?
Thanks,
Tom
If you are resolving your components using the TypedFactoryFacility, creating a custom ITypedFactoryComponentSelectors might help you. I would need more detail on how you create the Readers to give you more info.
Kind regards,
Marwijn.
Edit =====================================
Let's add an example:
public interface IFoo
{
}
public class Foo1 : IFoo
{
}
public class Foo2 : IFoo
{
}
public interface IFooFactory
{
IFoo CreateFoo(string which);
}
public class FooFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return (string)arguments[0];
}
}
--- registration
container.AddFacility<TypedFactoryFacility>();
Component.For<IFoo>().Named("Foo1Name").ImplementedBy<Foo1>(),
Component.For<IFoo>().Named("Foo2Name").ImplementedBy<Foo2>(),
Component.For<IFooFactory>().AsFactory(f => f.SelectedWith(new FooFactoryComponentSelector())),
--- usage
var factory = _container.Resolve<IFooFactory>(); // in general this would just be a dependency in the constructor.
var foo = factory.CreateFoo("Foo2Name");
Just adapt the component selector to your needs. If necessary you can also pass additional arguments to CreateFoo, if the constructor requires arguments not provided by the container.
More info: http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx