ViewModelLocator in prism mvvm - mvvm

Here is my Scenario,I need to create a simple uwp app and I have got a single viewmodel and multiple views..I am using prism mvvm/unity .
MainPage.xaml
<prism:SessionStateAwarePage
x:Class="MvvmSample.Views.MainPage"
xmlns:prism="using:Prism.Windows.Mvvm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MvvmSample"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" FontSize="29.333" />
<Button Content="Navigate" Command="{Binding del}"/>
</Grid>
Viewmodels.MainpageViewModel
public class MainPageViewModel : ViewModelBase
{
public string Title { get; set; }
public INavigationService NavigateToPage;
public static List<string> names = new List<string>() { "Anzal", "Rashid", "Kamil", "Fahad" };
public ObservableCollection<string> Mynames { get; set; }
public MainPageViewModel(INavigationService navigationservice)
{
this.Title = "Run Time";
NavigateToPage = navigationservice;
for (int i = 0; i < names.Count; i++)
{
Mynames.Add(names[i]);
}
del = new DelegateCommand(
() =>
NavigateToPage.Navigate(App.Expeirences.Second.ToString(),null);
);
}
}
SecondPage.xaml
<prism:SessionStateAwarePage
x:Class="Design3.Views.SecondPage"
xmlns:prism="using:Prism.Windows.Mvvm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Design3"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListBox ItemsSource="{Binding names}"/>
</Grid>
App.xaml.cs
sealed partial class App : PrismUnityApplication
{
public App()
{
this.InitializeComponent();
}
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterInstance<INavigationService>(this.NavigationService);
return base.OnInitializeAsync(args);
}
protected override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
this.NavigationService.Navigate(Expeirences.Main.ToString(), null);
Window.Current.Activate();
return Task.FromResult(true);
}
public enum Expeirences
{
Main,
Second
}
}
Now the problem occurs..How can I bind my secondpage to mainpageviewmodel???How to use my ViewModelLocator??

You want to register MainpageViewModel for SecondPage with the ViewModelLocationProvider, thus overriding the convention:
ViewModelLocationProvider.Register<SecondPage, MainpageViewModel>();
...preferably before navigating to the second page :-)

Related

data is not getting in viewmodel on Icommand button click

I have a form for capturing data from user.
<StackLayout Spacing="10" Margin="20,10,20,0">
<Label Text="Job Name"></Label>
<Entry BackgroundColor="White" x:Name="JobName" Text="{Binding SelectedJob.Name,Mode=TwoWay}"/>
<Label Text="Goal"></Label>
<Entry BackgroundColor="White" x:Name="Goal" Text="{Binding SelectedJob.Goal,Mode=TwoWay}"/>
<Grid ColumnSpacing="8" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save" Command="{Binding SubmitCommand}" BackgroundColor="#078fc1" Grid.Column="0" HorizontalOptions="FillAndExpand"/>
<Button Text="Reset" BackgroundColor="#078fc1" Grid.Column="1" HorizontalOptions="FillAndExpand"/>
</Grid>
</StackLayout>
on button click i want to bind data to SelectedJob object.
this is my ViewModel
public class JobViewModel: INotifyPropertyChanged
{
public JobViewModel()
{
SubmitCommand = new Command(OnSubmitAsync);
}
private JobDTO selectedob { get; set; }
public JobDTO SelectedJob
{
get { return selectedob; }
set
{
selectedob = value;
OnPropertyChanged(nameof(SelectedJob));
}
}
public ICommand SubmitCommand { protected set; get; }
public async void OnSubmitAsync()
{
await.jobservice.postjob()
}
}
and this is my model
public class JobDTO:INotifyPropertyChanged
{
private string name { get; set; }
public string Name
{ get { return name;}
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
private string goal;
public string Goal
{
get { return goal; }
set
{
goal = value;
OnPropertyChanged(nameof(Goal));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
on button click i am getting SelectedJob as null,eventhough i made my class as JobDTO:INotifyPropertyChanged.can any one help me what i am doing here wrong.
I changed the code of ViewModel and model.
There is GIF of demo based on your code.
ViewModel of JobViewModel
If you want to used get/set method, one attribute corresponds to one get/set method, I also add OnPropertyChangedmethod.
public class JobViewModel : INotifyPropertyChanged
{
public JobViewModel()
{
SubmitCommand = new Command(OnSubmitAsync);
}
private JobDTO selectedob=new JobDTO();
public JobDTO SelectedJob
{
get { return selectedob; }
set
{
selectedob = value;
OnPropertyChanged("SelectedJob");
}
}
public ICommand SubmitCommand { protected set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public async void OnSubmitAsync()
{
// i donnot know what is your aim of this part, i just pop up a alert that contains Name and Goal from Entry.
await Application.Current.MainPage.DisplayAlert("Alert", "Name: "+SelectedJob.Name+"\n"+ "Goal: "+ SelectedJob.Goal , "Ok");
}
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Model of JobDTO, The changed place is same with ViewModel
public class JobDTO: INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
private string goal;
public string Goal
{
get { return goal; }
set
{
goal = value;
OnPropertyChanged("Goal");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DateBoundingDemo"
x:Class="DateBoundingDemo.MainPage">
<StackLayout Spacing="10" Margin="20,10,20,0">
<Label Text="Job Name"></Label>
<Entry BackgroundColor="White" x:Name="JobName" Text="{Binding SelectedJob.Name,Mode=TwoWay}"/>
<Label Text="Goal"></Label>
<Entry BackgroundColor="White" x:Name="Goal" Text="{Binding SelectedJob.Goal,Mode=TwoWay}"/>
<Grid ColumnSpacing="8" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save" Command="{Binding SubmitCommand}" BackgroundColor="#078fc1" Grid.Column="0" HorizontalOptions="FillAndExpand"/>
<Button Text="Reset" BackgroundColor="#078fc1" Grid.Column="1" HorizontalOptions="FillAndExpand"/>
</Grid>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext =new JobViewModel();
}
}

Unable to show Xamarin Forms MVVM binding result in listview

I am trying to implement MVVM approach in my xamarin forms application. During the implementations, I have hit a road block. I am unable to populate the list view with the data that i recieve from the server. I am unable to identify the binding issue.
Please let me know where is my mistake? What am I missing?
View Code
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.Views.SubtaskPage"
Title="Select Subtask"
xmlns:viewModels="clr-namespace:Test.ViewModels; assembly=Test">
<ContentPage.BindingContext>
<viewModels:SubtaskPageViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tbiAddSubtask" Text="Add Subtask" Clicked="tbiAddSubtask_Clicked"/>
</ContentPage.ToolbarItems>
<StackLayout Orientation="Vertical" Padding="10">
<ListView x:Name="lstSubtasks" ItemSelected="lstSubtasks_ItemSelected" IsPullToRefreshEnabled="True" RefreshCommand="{Binding RefreshCommand}" IsRefreshing="{Binding IsBusy}" ItemsSource="{Binding SubtaskList}}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem x:Name="menuAddTimeSpent" Clicked="menuItem_Clicked" CommandParameter="{Binding Ticket}" Text="Menu" />
</ViewCell.ContextActions>
<StackLayout Padding="20,0,0,0" HorizontalOptions="StartAndExpand" Orientation="Horizontal">
<Label Text="{Binding Subject}" VerticalTextAlignment="Center" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Response Class Code
public class SubtasksResponse
{
public int Status { get; set; }
public string Message { get; set; }
public List<Ticket> Subtasks { get; set; }
}
View Model Code
public class SubtaskPageViewModel : INotifyPropertyChanged
{
private SubtasksResponse _subtaskList;
public SubtasksResponse SubtaskList
{
get { return _subtaskList; }
set
{
_subtaskList = value;
OnPropertyChanged(nameof(SubtaskList));
}
}
private Command _refreshCommand;
public Command RefreshCommand
{
get
{
return _refreshCommand;
}
}
bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
OnPropertyChanged(nameof(IsBusy));
}
}
public SubtaskPageViewModel()
{
_refreshCommand = new Command(RefreshList);
}
async void RefreshList()
{
SubtaskList = await PopulateSubtaskList();
}
async Task<SubtasksResponse> PopulateSubtaskList()
{
RestService rs = new RestService();
IsBusy = true;
IsBusy = false;
var subtaskList = new SubtasksResponse();
subtaskList = await rs.GetSubtasksAsync(Convert.ToInt32(Application.Current.Properties["UserId"]));
return subtaskList;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
For starters we see you are binding the ListView to ItemsSource="{Binding SubtaskList} - when we then look at the ViewModel it seems that SubtaskList is of type SubtasksResponse, that type only has 3 properties.
But the item template inside your ListView is not using any of those 3 properties... it's using Ticket and Subject.
Are this properties of the class Subtasks? If so you need to bind the ListView directly to the List property for it to pick up the items in that collection.

Prism Forms INavigationAware OnNavigatingTo not updating ObservableCollection

I am using Prism which helps me in binding my view to the view model. Using INavigationAware, I am hoping to update the Observable collection in my Listview from OnNavigatingTo(). On debugging this method is accessible, however it does not seem to update the ObservableCollection that is bound to the view.
Below is the ViewModel, that is inherited from Prism's BindableBase and INavigationAware:
public class QuoteDetailPageViewModel : BindableBase, INavigationAware
{
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private ObservableCollection<Message> _messages;
private ObservableCollection<Message> Messages
{
get { return _messages; }
set { SetProperty(ref _messages, value); }
}
private Author _selectedAuthor;
private Author SelectedAuthor
{
get { return _selectedAuthor; }
set { SetProperty(ref _selectedAuthor, value); }
}
public QuoteDetailPageViewModel(INavigationService navigationService)
{
Title = "Text Messages";
}
public void OnNavigatingTo(NavigationParameters parameters)
{
var id = -1;
if (parameters != null && parameters.ContainsKey("id"))
{
int.TryParse(parameters["id"].ToString(), out id);
}
if (id > 0)
{
Title = "Contact Message";
}
var msgs = new List<Message>()
{
new Message() {Text = "An investment in knowledge pays the best
interest."},
new Message() {Text = "Early to bed and early to rise makes a
man healthy, wealthy, and wise."},
new Message()
{
Text = "It's fine to celebrate success but it is more
important to heed the lessons of failure."
},
};
Messages = new ObservableCollection<Message>(msgs);
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
}
}
public class Message
{
public string Text { get; set; }
}
And Below is the xaml code:
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="PrismAppTutorial.Views.QuoteDetailPage"
Title="{Binding Title}">
<StackLayout Margin="0,20,0,0">
<ListView x:Name="lvAuthorQuotes"
ItemsSource="{Binding Messages}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Padding="20,10"
Orientation="Vertical"
HorizontalOptions="FillAndExpand"
VerticalOptions="StartAndExpand">
<Label Text="{Binding Text}"
HorizontalOptions="StartAndExpand"
VerticalOptions="StartAndExpand"
LineBreakMode="WordWrap" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Anyone knows how to fix this?
Turns out I had set the Messages as private and not public. This needs to be public so as to be applied in the view.

Prism MVVM Navigate Command

I've viewed webinar "MVVM Made Simple with Prism" (Brian Lagunas).
I followed his examples to create a main window with a menu with some
items. When operator select an item I've to open a view. Application
compiled and start but when I select an item nothing happen.
I've put some breakpoints to control that view to be opened was
istantiated, to control the correct uri and the correct key used in
extension RegisterTypeForNavigation. All appear correct. I've noticed that
when I select an item from the menu when I close the MainWindow application
don't stop automatically. I copy some strings of code to give you more
details...
Main windows xml (MenuPrincipale.xml)
<Window x:Class="PBEdPallets.Views.MenuPrincipale"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PBEdPallets.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Title="Menu Principale:" Height="600" Width="850">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" x:Name="MenuItems" Height="19" Margin="5,5,5,0" VerticalAlignment="Top">
<MenuItem Header="Anagrafiche">
<MenuItem Header="Materiali" Command="{Binding NavigateCommand}" CommandParameter="Editor_AnaMateriali"/>
<MenuItem Header="Sottocataste" Command="{Binding NavigateCommand}" CommandParameter="Editor_AnaSottocataste"/>
<MenuItem Header="Tipi livelli pile" Command="{Binding NavigateCommand}" CommandParameter="Test"/>
</MenuItem>
</Menu>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
** Bootstrapper.cs **
using Prism.Unity;
using Microsoft.Practices.Unity;
using PBEdPallets.Views;
using System.Windows;
namespace PBEdPallets
{
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
//Importo la finestra di avvio.
return Container.Resolve<MenuPrincipale>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
//Container.RegisterType(typeof(object), typeof(Editor_AnaMateriali), "Editor_AnaMateriali");
//Container.RegisterType(typeof(object), typeof(Editor_AnaSottocataste), "Editor_AnaSottocataste");
Container.RegisterTypeForNavigation<Editor_AnaMateriali>("Editor_AnaMateriali");
Container.RegisterTypeForNavigation<Editor_AnaSottocataste>("Editor_AnaSottocataste");
Container.RegisterTypeForNavigation<Test>("Test");
}
}
public static class UnityExtensions
{
public static void RegisterTypeForNavigation<T>(this IUnityContainer container, string name)
{
container.RegisterType(typeof(object), typeof(T), name);
}
}
}
** MenuPrinciapaleViewModel **
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
namespace PBEdPallets.ViewModels
{
public class MenuPrincipaleViewModel: BindableBase
{
private readonly IRegionManager _regionManager;
public DelegateCommand<string> NavigateCommand { get; set; }
#region Costruttore
public MenuPrincipaleViewModel(IRegionManager regionalManager)
{
_regionManager = regionalManager;
//Definisco l'istanza di NavigateCommand.
NavigateCommand = new DelegateCommand<string>(Navigate);
}
#endregion Costruttore
#region Private Methods
private void Navigate(string uri)
{
try
{
//ContentRegion รจ il nome assegnato nel tag ContentControl
_regionManager.RequestNavigate("ContentRegion", new Uri(uri, UriKind.Relative));
}
catch (Exception)
{
throw;
}
}
#endregion Private Methods
}
}

Programmatically populate a stackpanel with an observablecollection of user controls using MVVM

I have an observablecollection of type frameworkelement that I would like to display in a stackpanel or something similar. Every item in the observablecollection is a usercontrol that I have created. I'm pretty new to WPF and I don't have any idea how to do this. An example would be much appreciated
I'm borrowing rhe1980's answer a bit here, but the point is that the code in the codebehind will actually be in a viewmodel.
View:
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=MyCollection}"/>
</StackPanel>
</Grid>
CodeBehind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (!string.IsNullOrEmpty(propertyName))
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
this.OnObjectChanged();
}
private ObservableCollection<FrameworkElement> _myCollection;
public ObservableCollection<FrameworkElement> MyCollection
{
get
{
return _myCollection;
}
set
{
_myCollection = value;
OnPropertyChanged("MyCollection");
}
}
}
Use a ItemsControl for bind the ObservableCollection in the StackPanel:
View(xaml):
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<Grid>
<StackPanel>
<ItemsControl ItemsSource="{Binding ElementName=mainWindow,Path=ObservableCollection}"/>
</StackPanel>
</Grid>
Codebehind(xaml.cs):
public partial class MainWindow : Window
{
public ObservableCollection<FrameworkElement> ObservableCollection { get; set; }
public MainWindow()
{
InitializeObservableCollection();
InitializeComponent();
}
private void InitializeObservableCollection()
{
ObservableCollection = new ObservableCollection<FrameworkElement>();
for (var ii = 0; ii < 10; ii++)
{
ObservableCollection.Add(new Button {Content = ii.ToString()});
}
}
}