How to set different localized string in different visual states in WP7 using Blend? - mvvm

How do I set different localized strings in different visual states in WP7 using Blend without any code behind?
I can set different non-localized strings in different visual states (although it flickers). That works, but how about localized strings?
If I change the string using data binding in Blend, Blend just overrides the data binding in Base state and not the actual state where I'm recording.
EDIT:
This is how I localize my strings:
I have a resources file named AppPresources.resx. Then I would do this in code:
// setting localized button title
mainButton.Content = AppResources.MainButtonText;
Then I have a GlobalViewModelLocator from MVVM Light Toolkit with the following Property for Databinding.
private static AppResources _localizedStrings;
public AppResources LocalizedStrings
{
get
{
if (_localizedStrings == null)
{
_localizedStrings = new AppResources();
}
return _localizedStrings;
}
}
And in xaml file:
<Button x:Name="mainButton" Content="{Binding LocalizedStrings.MainButtonText, Mode=OneWay, Source={StaticResource Locator}}" ... />

What you need to do, is very close to what you're already doing. First, define a class named Resources.cs with following content
public class Resources
{
private static AppResources resources = new AppResources();
public AppResources LocalizedStrings
{
get
{
return resources;
}
}
}
This allows us to create a instance of your Resource File in XAML. To do this, open App.xaml and add following
<Application.Resources>
<local:Resources x:Key="Resources" />
</Application.Resources>
Now when you need to do bindings in your XAML, you do it like this:
<Button Content="{Binding LocalizedStrings.MainButtonText,
Source={StaticResource Resources}}" />
What you'll notice is that it doesn't work in Blend, yet. To make it work in Expression Blend,
add the following file: DesignTimeResources.xaml in the Properties Folder, and add following content
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourNameSpace">
<local:Resources x:Key="Resources" />
</ResourceDictionary>
Now, you press F6 in Visual Studio to recompile, and voila, your localized strings are available in Expression Blend!
A real-world example from one of my projects:
AppResources.cs
DesignTimeResources.xaml
App.xaml

Related

How to pass objects to PopupPage using MAUI.Toolkit or Mopup plugins?

I have a Maui app where I use MVVM pattern with MAUI Toolkik and also trying with Mopup plugin but I haven't found how to pass objects to Popup pages combined with MVVM. At the moment, I have a page, which I use to navigate to the PopupPage successfully and also I am able to connect the PopupPage with its viewmodel. What I am unable to do is to pass any kind of object to the PopupPage.
I have tried to set the PopupPage constructor with parameters but the methods to navigate to the PopupPage only recognize parameters setted on the code behind.
Here is my code:
Popup
<mopup:PopupPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NewScholarApp.Views.MessagePopup"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:mopup="clr-namespace:Mopups.Pages;assembly=Mopups"
xmlns:viewmodels="clr-namespace:NewScholarApp.ViewModels"
x:DataType="viewmodels:MessagePopupViewModel">
<mopup:PopupPage.BindingContext>
<viewmodels:MessagePopupViewModel/>
</mopup:PopupPage.BindingContext>
<VerticalStackLayout BackgroundColor="White" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="100" WidthRequest="100">
<Label
Text="{Binding Message}"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</mopup:PopupPage>
I use this to navigate from my page viewmodel
await _popupNavigation.PushAsync(new MessagePopup(string text = "tex"));
If I try to set a parameter, shows this error, even that in my PopupPage constructor I have setted a parameter
"MessagePopup does not contain a a constructor that contains 1 argument"
**MessagePopupViewModel **
public partial class MessagePopupViewModel : ObservableObject
{
#region AnP
[ObservableProperty]
private string message;
private readonly IApiService _apiService;
#endregion
public MessagePopupViewModel(string tex)
{
Message = tex;
}
}
you are navigating using this code
await _popupNavigation.PushAsync(new MessagePopup(string text = "tex"));
so MessagePopup MUST have a constructor that accepts a parameter
public MessagePopup(string somevalue)
if you also want to pass that value to your VM, then you can add
BindingContext = new MessagePopupViewModel(somevalue);
if you do this, then you should remove the BindingContext property from the XAML

Should ItemSource and BindingContext both be set when using MVVM (Xamarin.Froms ListView)?

Model:
public class Question : INotifyPropertyChanged
{
private float? _answer;
public float? Answer
{
get => _answer;
set
{
_answer = value;
NotifyPropertyChanged();
}
}
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
View model:
public class QuestionViewModel
{
private ObservableCollection<Question> _questions;
public ObservableCollection<Question> Questions
{
get => _questions;
set
{
if (_questions != value)
{
_questions = value;
}
}
}
}
XAML:
<ListView x:Name="ListViewQuestions" SelectionMode="Single" HasUnevenRows="True" HeightRequest="250" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" Keyboard="Numeric" FontSize="Medium" VerticalOptions="End"
HorizontalOptions="FillAndExpand" Grid.Row="0" Grid.Column="1" >
<Entry.Behaviors>
<behaviors:EntryMaxValueBehavior MaxValue="{Binding MaxVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
<behaviors:EntryMinValueBehavior MinValue="{Binding MinVal}" BindingContext="{Binding BindingContext, Source={x:Reference EntryAnswer}}" />
</Entry.Behaviors>
</Entry>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In my page OnAppearing method, I set the ListViewQuestions like this:
var questions = await DataStore.GetQuestions(_inspection.Id);
var questionsViewModel = new QuestionViewModel { Questions = new ObservableCollection<Question>(questions) };
ListViewQuestions.ItemsSource = null;
ListViewQuestions.ItemsSource = questionsViewModel.Questions;
However, when values are entered into EntryAnswer, the setter in the Question model is not called, as I would expect. I thought that maybe this was because the BindingContext for the ListView needed to be set, so I set it like this:
ListViewQuestions.BindingContext = questionsViewModel;
However, the setter in the Question model is still not called. I also tried implementing INotifyPropertyChanged in the QuestionViewModel, but still no joy. I checked that the ObservableCollection in the View Model is set correctly, with actual data, and it is. Can anyone spot what might be going wrong here?
Edit 1: I also tried not setting the ItemSource, but only setting the ListViewQuestions.BindingContext to the view model, but then the ListView was not being populated with any data.
Here is how this works together.
The BindingContext is the object that will be the scope for whatever bindings that are in the page or it's children, unless you specify a different context for a certain child object, but let's not overcomplicate things for now.
This means, that when you have set the BindingContext, all Bindings will now start looking into the object referenced in the BindingContext. In your case, you set the BindingContext to an instance of QuestionViewModel.
You want your ListView, to get its items from the QuestionViewModel.Questions property. So, you set a binding like this:
<ListView x:Name="ListViewQuestions" ItemsSource="{Binding Questions}" ...>.
Questions needs to be a public property in the BindingContext, in our case QuestionViewModel. You got this right already.
Now, whenever you assign something to Questions this should also propagate to your ListView because of the binding.
Inside your ListView you are using a ViewCell, now note, that the scope does change here. Each cell represents an instance of an object inside the ItemsSource. In our case, each cell will hold a Question. You are using this:
<Entry x:Name="EntryAnswer" Text="{Binding Answer,Mode=TwoWay}" ...>
This means Answer needs to be a public property inside Question. You got this right already.
When you implement it like this, basically the only thing you do is fill your view model and assign that to the BindingContext of your page. If you are using an MVVM framework, this might happen automatically.
At some point, you might run into some trouble that the UI doesn't update and you will have to implement the INotifyPropertyChanged interface. Have a close look at what object doesn't update on screen and implement the interface on that object along with the needed plumbing, but from what I can see in this code, this isn't needed right now. And besides, you have implemented it the right way in your Question right now.
I hope this makes sense! It's a bit hard to wrap your head around the first time, but once you get the swing of it, it is pretty easy!
In your Answer Setter try:
set
{
float? temp = null;
if(float.TryParse(value, out temp)
{
_answer = temp;
NotifyPropertyChanged("Answer");
}
}
It seems like for this to work though your setter would have to be called, and you indicate that it is not, so I think it must be the min, max binding where this is kicking out the error. For now perhaps get rid of that and see if the setter will get called.
In WPF using a converter is typical and I think will work with the Xamarin as well. See this for a good example of how to implement IValueConverter.

UWP Data-Binding not working with ViewModel

Fairly new to UWP and MVVM I came across a problem which might seem obvious to many of you.
In my project I have 3 folders named Views, ViewModels and Models which include some files as seen in the image bellow:
Can't upload image yet (reputation):
http://i.imgur.com/42f5KeT.png
The problem:
I am trying to implement MVVM. I have searched hours for articles and videos but it seems I am always missing something. I have some bindings in the LoginPage.xaml which I then modify in a class inside Models/LoginPageModel.cs. I have an INotifyPropertyChanged class in my LoginPageViewModel.cs where every time a property changes in my LoginPageModel.cs I want the INotifyPropertyChanged class to trigger which will then change the property in the LoginPage.xaml View. Below I have the content of those files.
This is a sample of my LoginPageModel.cs code:
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace App_Name.Models
{
class LoginPageModel
{
private NotifyChanges notify;
public async void LogIn()
{
if (something is true)
notify.LoginUIVisibility = Visibility.Visible;
}
}
}
This is my LoginPageViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml;
namespace App_Name.ViewModels
{
public class NotifyChanges : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Visibility loginUIVisibility = Visibility.Collapsed;
public Visibility LoginUIVisibility
{
get
{
return loginUIVisibility;
}
set
{
if (value != loginUIVisibility)
{
loginUIVisibility = value;
NotifyPropertyChanged("LoginUIVisibility");
}
}
}
}
}
Here is an example of LoginPage.xaml:
<Page
x:Class="App_Name.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App_Name"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:App_Name.ViewModels"
mc:Ignorable="d">
<Page.DataContext>
<vm:NotifyChanges/>
</Page.DataContext>
<StackPanel Visibility="{Binding LoginUIVisibility}">
Here is my LoginPage.xaml.cs:
namespace App_Name
{
public sealed partial class LoginPage : Page
{
private LoginPageModel login;
public LoginPage()
{
InitializeComponent();
login.LogIn();
}
}
}
I don't know why this is not working. Bindings used not to work, but now at runtime it gives me an unhandled exception and I think it has to do with not assigning any value to the private NotifyChanges notify and private LoginPageModel login objects, but I don't know what. Thanks everyone for your time in advance!
Please if you need clarifications for my question just write a comment. Thank you!
I am trying to implement MVVM.
And you're not getting it right yet. Forget about the Bindings for a moment, let's focus on the architecture.
Going down the acronym, you need
a Model. It supports your business logic and usually is defined by your backend (database). It should not depend on (be aware of) the Views or ViewModels. A lightweight UWP app could do without a Model layer.
a View. This is the XAML part that we like to keep as simple as possible, a.o. reasons because it's hardest to test.
a ViewModel. It's purpose is to serve the View. It should contain properties and commands the View can directly bind to. It does as much conversion and aggregation as possible to keep the View light. It usually relies on (0 or more) Models or Services.
Given this, it is not the case that you should always have 1 Model for 1 ViewModel. A ViewModel could use multiple Models, or none.
It is clear that your LoginPageModel.Login() is in the wrong place. Login() should be a method (Command) on your ViewModel.
Your story should go like this:
I want a LoginView
So I need to support it with a LoginViewModel, implementing INPC
The ViewModel probably needs to use a LoginService or a UserModel. But it would only need a Model instance after a successful login. A LoginModel doesn't sound right.
Have a look at Template10 to get started with View, ViewModel and a thread-safe BindableBase.
You could also look a the picture over here for a full (over the top maybe) layout of MVVM.
And here is the call for change in the main class:
NotifyChanges notifyChanges = new NotifyChanges();
notifyChanges.LoginUIVisibility = Visibility.Visible;
You have instantiated notifyChanges in the XAML file by adding <vm:NotifyChanges/>. And add a binding to StackPanel by using <StackPanel Visibility="{Binding LoginUIVisibility}">. But you created a new notifyChanges, and you did not bind the new notifyChanges to StackPanel. So it won't work. You could initialize viewModel just like following code.
MainPage
private LoginViewModel viewModel;
public MainPage()
{
this.InitializeComponent();
viewModel = this.DataContext as LoginViewModel;
}
private void showDetail_Click(object sender, RoutedEventArgs e)
{
viewModel.LoginUIVisibility = Visibility.Visible;
}
MainPage.xaml
<Page.DataContext>
<vm:LoginViewModel />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="loginButon" Content="Login" Visibility="{Binding LoginUIVisibility}" />
<Button x:Name="showDetail" Content="Show" Click="showDetail_Click" />
</StackPanel>

Titanium Alloy ListView XML Uncaught TypeError: Object #<UI> has no method 'createTemplates'

I am new to Titanium, so excuse my lack of understanding.
Even though I am using sdk version 3.2 (have sdk-version: 3.2.0.v20130801162445 in my tiapp.xml) when I try and have a view that uses the xml above I get this error:
[ERROR][V8Exception( 615)] Exception occurred at alloy/controllers/feed.js:22: Uncaught TypeError: Object # has no method 'createTemplates'
I cut down all my code so that the feed.js file is just:
function loadMoreBtnClicked(_event) {
alert('not implemented yet');
}
function createListView(_data) {
// this is pretty straight forward, assigning the values to the specific
// properties in the template we defined above
var items = [];
for (var i in _data) {
// add items to an array
items.push({
template : "template1", // set the template
textLabel : {
text : _data[i].name // assign the values from the data
},
pic : {
image : _data[i].pic_square // assign the values from the data
}
});
}
// add the array, items, to the section defined in the feed.xml file
$.section.setItems(items);
}
alert('feed loaded');
The XML is in feed.xml and looks like this:
<Alloy>
<Window class="container" formFactor="handheld">
<ListView id="list" defaultItemTemplate="template1">
<Templates>
<ItemTemplate name="buttonItem" height="Ti.UI.SIZE">
<!-- will use this in the next blog post -->
<Button id="loadMoreBtn" onClick="loadMoreBtnClicked">Load More</Button>
</ItemTemplate>
<!-- main template for displaying the list items -->
<ItemTemplate id="template1" name="template1" class="template1">
<ImageView id="pic" bindId="pic" class="imageThumb"/>
<View id="textContainer">
<Label id="textLabel" bindId="textLabel" class="title"/>
</View>
</ItemTemplate>
</Templates>
<!-- we only have one section and the items are contstucted using template1 -->
<ListSection id="section" >
<ListItem template="template1" />
</ListSection>
</ListView>
</Window>
</Alloy>
I still get the error (just using the XML with no actual controller code other than the alert running). If I pull the ListView XML out of the feed.xml file the alert fires, when I put the ListView XML back in I get the Error above.
I am trying to use code from this example:
https://gist.github.com/aaronksaunders/5896390
but cant really tell what I am missing?
Thanks!
-James
found out what the issue was, my problem had to do with not having the updated version of alloy that is needed to support the ListView Templates in XML. I needed to run this at the command line in Windows: "npm install -g alloy#1.2.0-alpha" (without quotes). After that I was able to use ListView templates in XML as shown above.

MVVM View event Viewmodel command binding

I'm looking for a good (read: simple) example on how to implement event aggregators with Prism. I've never used Prism and I'm also quite new to MVVM itself.
I have a WPF canvas as a View and I want to handle the MouseUp event on the canvas in the Viewmodel. Now the powers that be at our organization wants me to use Prism, and apparently Prism recommends using event aggregators, which is why I need a sample to get me started.
all you need for this is the EventToCommand behavior from MVVMLight or from System.Windows.Interactivity (Blend SDK). i would recommend you to take the MVVMLight version because it has some usefull specials:)
<Canvas>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp" >
<i:InvokeCommandAction Command="{Binding YourMouseUpViewModelCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
EventAggregator from Prism i mostly used for decoupled Viewmodel to Viewmodel communication.
I didn't know PRISM's EventAggregator allowed for event->command binding.
Another option for you in this case is using a "behavior". Here's a decent overview Behaviors: http://wpftutorial.net/Behaviors.html. You can ignore the Blend part of the tutorial; the important part is that you have at least the Blend 3 SDK installed. Here's how I did this:
public class ButtonDoubleClickCommandBehavior : Behavior<Button>
{
public ICommand DoubleClickCommand
{
get { return (ICommand)GetValue(DoubleClickCommandProperty); }
set { SetValue(DoubleClickCommandProperty, value); }
}
public static readonly DependencyProperty DoubleClickCommandProperty =
DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(ButtonDoubleClickCommandBehavior));
protected override void OnAttached()
{
this.AssociatedObject.MouseDoubleClick += AssociatedObject_MouseDoubleClick;
}
protected override void OnDetaching()
{
if (this.AssociatedObject != null)
{
this.AssociatedObject.MouseDoubleClick -= AssociatedObject_MouseDoubleClick;
}
}
void AssociatedObject_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (DoubleClickCommand != null && DoubleClickCommand.CanExecute(null))
{
DoubleClickCommand.Execute(null);
}
}
}
You could add another dependency property to the behavior to bind a command parameter so you can execute the command with that parameter; I just used null in my example.
And my XAML:
<Button Content="{Binding Path=Description}" VerticalAlignment="Center" HorizontalAlignment="Stretch" Template="{StaticResource TextBlockButtonTemplate}" Style="{StaticResource ZynCommandButton}" Tag="DescriptionButton">
<e:Interaction.Behaviors>
<ZViewModels:ButtonDoubleClickCommandBehavior DoubleClickCommand="{Binding Path=ItemDescriptionCommand}"/>
</e:Interaction.Behaviors>
</Button>
A more generic way using behaviors is proposed at AttachedCommandBehavior V2 aka ACB and it even supports multiple event-to-command bindings,
Here is a very basic example of use:
<Border local:CommandBehavior.Event="MouseDown"
local:CommandBehavior.Command="{Binding DoSomething}"
local:CommandBehavior.CommandParameter="From the DarkSalmon Border"
/>