MAUI Unable to use string resource file (resx) in XAML - maui

I'm using Visual Studio 17.4.4 on Windows.
Everything I've read says this should be working, but I get:
XamlC error XFC0000: Cannot resolve type "MyApp.Resources.Localization:AppStrings"
What am I missing?
Here's the xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="MyApp.Resources.Localization"
xmlns:views="clr-namespace:Jetty_Mobile.Views"
x:Name="thisPage"
x:Class="MyApp.MainPage">
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Label Text="{x:Static local:AppStrings.AppName}" />
</VerticalStackLayout>
</ContentPage>
Looking for AppStrings in code behind works:
public MainPage()
{
InitializeComponent();
var thisworks = MyApp.Resources.Localization.AppStrings.AppName;
}
And here is AppStrings.Designer.cs
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MyApp.Resources.Localization {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class AppStrings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal AppStrings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Jetty_Mobile.Resources.Localization.AppStrings", typeof(AppStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to My App.
/// </summary>
public static string AppName {
get {
return ResourceManager.GetString("AppName", resourceCulture);
}
}
}
}
I have searched all over the web and everything I see says this should work, but it doesn't.

It should be xmlns:local="clr-namespace:MyApp.Resources.Localization"
I have created a sample to test your code:
In the xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local ="clr-namespace:JsonData.Resources.Localization"
x:Class="JsonData.MainPage">
<Label Text="{x:Static local:AppStrings.AppName}"/>
The resource file:
And then, the label will show the right value. And when I deleted the clr-namespace in the xmlns:local. I met the same error as yours.

"{x:Static local:AppStrings.AppName}"
The error message suggests that local:AppStrings is the wrong syntax to use inside of a property string. AFAIK, those xmlns are for xml element names; e.g. immediately after <.
Try:
"{x:Static MyApp.Resources.Localization.AppStrings.AppName}"
(I'm not sure what syntax is, to shorten it.)

Related

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.

Xamarin Forms Controls values not visible

I have created a page that passes a value to a new page that will allow users to update the data. When the users selects the record to be updated the edit form opens but the data is not visible. If the value is changed and the edit button clicked it will update the value, but it is never visible. How can I show the data that is to be edited?
View Model
namespace QiApp.ViewModels
{
public class EditTodayCasesViewModel
{
private SxCaseDataService _sxCaseDataService = new SxCaseDataService();
public SxCase SelectedSxCase { get; set; }
public ICommand EditSxCaseCommand => new Command(async () =>
{
await _sxCaseDataService.PutSxCase(SelectedSxCase.SxCaseId, SelectedSxCase);
});
}
}
Edit Page xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:QiApp.ViewModels;assembly=QiApp.UWP"
x:Class="QiApp.Views.EditTodayCasePage">
<ContentPage.BindingContext>
<viewModels:EditTodayCasesViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Label Text="Surgery Case"/>
<Label Text="{Binding SelectedSxCase.SxCaseId}"/>
<Entry Text="{Binding SelectedSxCase.Record}"/>
<Switch IsToggled="{Binding SelectedSxCase.Complete}"/>
<Button Text="Edit Surgery Case"
Command="{Binding EditSxCaseCommand}"/>
</StackLayout>
</ContentPage>
Code behind
namespace QiApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EditTodayCasePage : ContentPage
{
public EditTodayCasePage(SxCase sxCase)
{
InitializeComponent();
var editTodayCasesViewModel = BindingContext as EditTodayCasesViewModel;
editTodayCasesViewModel.SelectedSxCase = sxCase;
}
}
}
Everything is alright except that your view gets bound to a view model which stays silent if properties are changed. Your view cannot get any information on when it should update itself and hence the UI as soon as the property SelectedSxCase gets changed.
Thankfully this can be done very easily by simply implementing the common interface INotifyPropertyChanged and extending your bound properties with a code line raising the event the interface provides.
Basically it goes like this ...
private SxCase _case;
public SxCase SelectedSxCase
{
get => _case;
set
{
_case = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedSxCase)));
}
}
... but there are several implementations to do that more elegant like using the CallerMemberName or even weaving the getter and setter automatically with Fody.

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>

MVVM Windows Phone 8 - adding a collection of pushpins to a map

Here is the XAML code:
<maps:Map x:Name="NearbyMap"
Center="{Binding MapCenter, Mode=TwoWay}"
ZoomLevel="{Binding ZoomLevel, Mode=TwoWay}"
>
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="StoresMapItemsControl" ItemsSource="{Binding Treks}">
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin x:Name="RouteDirectionsPushPin" GeoCoordinate="{Binding Location}" Visibility="Visible" Content="test"/>
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>
<maptk:UserLocationMarker x:Name="UserLocationMarker" Visibility="Visible" GeoCoordinate="{Binding MyLocation}"/>
</maptk:MapExtensions.Children>
</maps:Map>
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
xmlns:maptk="clr-namespace:Microsoft.Phone.Maps.Toolkit;assembly=Microsoft.Phone.Controls.Toolkit"
PushPinModel has an attribute Location which is a GeoCoordinate. Treks is an ObservableCollection<PushPinModel>. I run this code and only the UserLocationMarker is displayed, which is my current location.
I finally make it work by using dependency property. I added a new class:
public static class MapPushPinDependency
{
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.RegisterAttached(
"ItemsSource", typeof(IEnumerable), typeof(MapPushPinDependency),
new PropertyMetadata(OnPushPinPropertyChanged));
private static void OnPushPinPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
UIElement uie = (UIElement)d;
var pushpin = MapExtensions.GetChildren((Map)uie).OfType<MapItemsControl>().FirstOrDefault();
pushpin.ItemsSource = (IEnumerable)e.NewValue;
}
#region Getters and Setters
public static IEnumerable GetItemsSource(DependencyObject obj)
{
return (IEnumerable)obj.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(DependencyObject obj, IEnumerable value)
{
obj.SetValue(ItemsSourceProperty, value);
}
#endregion
}
And in the .xaml file I have added
xmlns:dp="clr-namespace:Treks.App.Util.DependencyProperties"
and now the .xaml file looks like this:
<maps:Map x:Name="NearbyMap"
Center="{Binding MapCenter, Mode=TwoWay}"
ZoomLevel="{Binding ZoomLevel, Mode=TwoWay}"
dp:MapPushPinDependency.ItemsSource="{Binding Path=Treks}"
>
<maptk:MapExtensions.Children>
<maptk:MapItemsControl Name="StoresMapItemsControl">
<maptk:MapItemsControl.ItemTemplate>
<DataTemplate>
<maptk:Pushpin x:Name="PushPins" GeoCoordinate="{Binding Location}" Visibility="Visible" Content="test"/>
</DataTemplate>
</maptk:MapItemsControl.ItemTemplate>
</maptk:MapItemsControl>
<maptk:UserLocationMarker x:Name="UserLocationMarker" Visibility="Visible" GeoCoordinate="{Binding MyLocation}"/>
</maptk:MapExtensions.Children>
</maps:Map>
Now all the pushpins are correctly rendered.
The MapItemsControl is currently not yet MVVM bindable ( what I am aware off ).
So best way is to set it's ItemsSource in the code behind of your view.
You can still use the collection defined in your ViewModel though!
Options are:
through mvvm messaging pass along the collection from the viewmodel to the code behind of the view
use the datacontext of the view to access the collection, something like this: this.StoresMapItemsControl.ItemsSource = ServiceLocator.Current.GetInstance<MainViewModel>().Locations;

In Javascript, where do I put XML comment on a var so it shows in intellisense?

I am using intellisense successfully in my javascript code for functions, but I don't know how to get it to work for a var or if I should be designing this class differently so I can document it effectively.
(function ($)
{
$.myNamespace.MyClass = {
m_varIWantToCommentOn: null,
/// <summary locid="m_varIWantToCommentOn">
/// *This doesn't work here* How should I comment on what this var is for?
/// </summary>
Init: function ()
{
/// <summary locid="Init">
/// Called when MyClass is initialized for the first time. this comment works fine.
/// </summary>
// ...use m_varIWantToCommentOn in some way...
}
}
})(jQuery);
I know this question is kind of old, but in case someone else has the same question...
I would use the <field> tag. It goes above the field it describes, unlike function documentation which goes on the inside.
(function ($) {
$.myNamespace.MyClass = {
/// <field> comments here </field>
m_varIWantToCommentOn: null,
Init: function () {
/// <summary locid="Init">
/// Called when MyClass is initialized for the first time. this comment works fine.
/// </summary>
// ...use m_varIWantToCommentOn in some way...
}
}
})(jQuery);
Typically, <var> tags are only used in var declarations, but they also go above the var they describe.
/// <var>comments here</var>
var someVar = null,
/// <var>This is a number</var>
anotherVar = 0;