I don't know if it is a good pratice, but I would like to use subdirectories into the directories "Views" and "ViewModels"
I have this code right now
namespace Project.Universal.ViewModels.Sales
{
public class SalesListViewModel:ViewModelBase
{
// Properties
private string _test;
private ICollection<Sale> _sales;
public string Test
{
get => _test;
set => SetProperty(ref _test, value);
}
public ICollection<Sale> Sales {
get => _sales;
private set
{
}
}
// Services
private readonly IStandardService<Sale> _salesService;
// Commands
public DelegateCommand Update { get;}
// Constructor
public SalesListViewModel(IStandardService<Sale> salesService)
{
Debug.WriteLine(">>>> Initializing SalesListViewModel, trying to set _salesService.");
_salesService = salesService;
Debug.WriteLine(">>>> Delegating command to update SalesListViewModel.");
Update = new DelegateCommand(async () =>
{
Sales = await _salesService.GetAsync();
Test = "Test";
});
}
}
}
And this is my view page
<mvvm:SessionStateAwarePage
x:Class="Project.Universal.Views.Sales.SalesListPage"
mvvm:ViewModelLocator.AutoWireViewModel="True"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Project.Universal.Views.Sales"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:mvvm="using:Prism.Windows.Mvvm"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Margin="{StaticResource MediumLeftTopRightBottomMargin}">
<TextBlock>this is the Sales page</TextBlock>
<TextBlock Text="{Binding Test}"></TextBlock>
</Grid>
</mvvm:SessionStateAwarePage>
But Prism do not inject the view model correctly. Unline this, the Prism Framework is injecting ShellViewModel, like error bellow says:
Error: BindingExpression path error: 'Test' property not found on 'Project.Universal.ViewModels.ShellViewModel'. BindingExpression: Path='Test' DataItem='Project.Universal.ViewModels.ShellViewModel'; target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')
For organization, I want to use this pattern for folders:
Views/Context/ViewPage.xaml for views
ViewModels/Context/ViewModel.cs for viewModels
I'm using a template from Windows Template Studio with Prism 6.3 framework.
Sorry for my english.
After hours I've resolute my problem:
The Windows Template Studio have changed the ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver() method to mantain compatibility to others frameworks. I've just changed this method like I want and that worked. For others that will have same problem, here's the code.
Class App.xaml.cs
protected override async Task OnInitializeAsync(IActivatedEventArgs args)
{
await ThemeSelectorService.InitializeAsync().ConfigureAwait(false);
ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) =>
{
var viewName = viewType.FullName; // get full type name
var viewModelName = viewName
.Substring(0, viewName.Length - 4)
.Replace(".Views.", ".ViewModels.");
var viewModelAssembly = viewType.GetTypeInfo().Assembly.FullName;
//var viewModel
var viewModelTypeName = string.Format(CultureInfo.InvariantCulture, "{0}ViewModel, Project.Universal", viewModelName);
return Type.GetType(viewModelTypeName);
});
await base.OnInitializeAsync(args);
}
Related
I needed to implement a wait indicator for a page in my Maui app.
Searching gave me this, but no step by step instructions.
So how do I do this?
Overview:
The control to display the animation is called ActivityIndicator.
ActivityIndicator is a visual element, should be part of your page.
So, add an ActivityIndicator to your xaml.
The state of the indicator is part of logic - should live in your view model.
So, add a bindable property to your view model, and bind ActivityIndicator.IsRunning to this property.
Sample (I haven't tested, just for illustration)
Page (xaml):
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customcontrols="clr-namespace:Waiter.Maui.CustomControls"
x:Class="..." >
<ContentPage.Content>
<ActivityIndicator IsRunning="{Binding IsBusy}" />
<Button Text="Go" Command="{Binding GoCommand}" />
</ContentPage.Content>
</ContentPage>
ViewModel:
namespace MyNamespace
{
public class MyViewModel : BaseViewModel
{
public MyViewModel()
{
GoCommand = new Command(execute: OnGo, canExecute: true);
}
public Command GoCommand { get; }
private void OnGo()
{
MainThread.InvokeOnMainThreadAsync(async () =>
{
IsBusy = true;
Thread.Sleep(5000);
IsBusy = false;
return result;
});
}
}
}
BaseViewModel class (so that it can be re-used, from existing community content):
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Waiter.Maui.Pages
{
public class BaseViewModel : INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
I want to point out few things.
First of all, this "IsBusy" that I see getting recommended all around, is working strategy. I can only recommend using CommunityToolkit.MVVM, and letting it do your job, and handle all notification code instead of you.
However, using such boolean variable, is no different than using Lock, Mutex, Semaphore, etc. A programmer has to be very careful how and when it is changed, otherwise all kinds of bugs may occur.
In reality, most problems can be solved with commanding itself.
Specifically CanExecute property is more than enough.
I recommend this:
https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/commanding?view=net-maui-7.0
Before becoming slave to manual changing bool variables.
I have implemented Tabbedpage using ViewModel but my ViewModel constructor call 4 times because I create 4 tabs, I also used prism for ViewModel binding.
Below is a design file
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage 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"
xmlns:material="clr-namespace:XF.Material.Forms.UI;assembly=XF.Material"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:ffTransformations="clr-namespace:FFImageLoading.Transformations;assembly=FFImageLoading.Transformations"
prism:ViewModelLocator.AutowireViewModel="True"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
xmlns:extended="clr-namespace:Xamarin.Forms.Extended;assembly=Xamarin.Forms.Extended.InfiniteScrolling"
xmlns:customcontrols="clr-namespace:QuranicQuizzes.CustomControls"
xmlns:local="clr-namespace:QuranicQuizzes.Views" NavigationPage.HasNavigationBar="True"
x:Class="QuranicQuizzes.Views.DashboardPage">
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="Dashboard" TextColor="White" HorizontalTextAlignment="Center" HorizontalOptions="CenterAndExpand" VerticalTextAlignment="Center" FontFamily="{StaticResource QuranFontBold}" FontSize="Medium" />
<StackLayout Orientation="Horizontal">
<material:MaterialMenuButton x:Name="Menus" ButtonType="Text" Image="list" TintColor="White" BackgroundColor="Transparent" CornerRadius="24" Choices="{Binding Actions}" MenuSelected="MaterialMenuButton_MenuSelected" />
</StackLayout>
</StackLayout>
</NavigationPage.TitleView>
<local:HomeTabPage/>
<local:QuizzesTabPage/>
<local:LiveGameTabPage/>
<local:AssignmentTabPage/>
</TabbedPage>
Below is my code
public partial class DashboardPage : TabbedPage
{
private DashboardPageViewModel vm;
public DashboardPage()
{
try
{
InitializeComponent();
vm = BindingContext as DashboardPageViewModel;
}
catch (Exception ex)
{
}
}
}
Below is my ViewModel
public class DashboardPageViewModel : ViewModelBase
{
INavigationService _navigationService;
IClientAPI _clientAPI;
Dashboards dashboard;
public DashboardPageViewModel(INavigationService navigationService, IClientAPI clientAPI) : base(navigationService)
{
_navigationService = navigationService;
_clientAPI = clientAPI;
if (CrossConnectivity.Current.IsConnected)
{
var StartDate = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd");
var Enddate = DateTime.Now.ToString("yyyy-MM-dd");
if (dashboard == null)
{
dashboard = new Dashboards();
getDashboardData(StartDate, Enddate);
}
}
}
}
I see what you're trying to do. You want to initialise your vm instance so that you can access you vm from your view.
Instead of doing this:
vm = BindingContext as DashboardPageViewModel;
what we can do is change the type of the existing BindingContext property by doing this:
public partial class DashboardPage
{
new DashboardPageViewModel BindingContext
{
get => (DashboardPageViewModel) base.BindingContext;
set => base.BindingContext = value;
}
public DashboardPage()
{
InitializeComponent();
}
}
now you can just access BindingContext.DoSomething because its type is now DashboardPageViewModel.
Now that's sorted out, your viewmodel should not be being called 4 times! Something is wrong here. Here is a checklist of things to do that may be causing the constructor being called 4 times as not a lot more info was provided.
Try removing <NavigationPage.TitleView> segment.
Make sure you are navigating to DashboardPage.
Make sure that each individual TabbedPage has it's own viewmodel.
Try removing prism:ViewModelLocator.AutowireViewModel="True"and manually adding the viewmodel to the TabbedPage.
Finally constructors should be able to run very fast and should only be used for assigning variables or instantiation or very quick operations. What you could maybe do is separate the code in your VM:
public class DashboardPageViewModel : ViewModelBase
{
IClientAPI _clientAPI;
Dashboards dashboard;
public DashboardPageViewModel(INavigationService navigationService, IClientAPI clientAPI) : base(navigationService)
{
_clientAPI = clientAPI;
}
public void Init()
{
if (CrossConnectivity.Current.IsConnected)
{
var StartDate = DateTime.Now.AddDays(-7).ToString("yyyy-MM-dd");
var Enddate = DateTime.Now.ToString("yyyy-MM-dd");
if (dashboard == null)
{
dashboard = new Dashboards();
getDashboardData(StartDate, Enddate);
}
}
}
}
and then in your view you could add this method:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if(BindingContext == null)
{
return;
}
BindingContext.Init();
}
I hope this really helps you.
NB: All this code was written on the fly and never compiled, there may be some errors.
I am trying to learn MVVVM, by doing a weather application, using a Udemy course as a reference, the Bing Maps Api, and the OpenWeather API.
I am trying to bind the city with the text of the AutoSuggestBox of my xaml
I have done the WeatherVM and bind it to the view, as a page resource
I also did a quick Method that gets me the City and the Country code, where I am (Using the Bing Maps api)
I Called the method from the MainPage.cs, only to see if it works, and it work fine I get the City and Country Code as expected
WeatherVM
public OpenWeather OpenWeather { get; set; }
private Task<string> _city;
public Task<string> city {
get { return _city; }
set {
_city = value;
GetLocationData();
}
}
public WeatherVM() {
OpenWeather = new OpenWeather();
}
private async void GetLocationData() {
var cityData = await MapLocator.GetCityData();
}
}
}
MainPage.xaml
x:Class="MVVM_Example.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVM_Example"
xmlns:vm="using:MVVM_Example.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<vm:WeatherVM x:Key="vm" />
</Page.Resources>
<Grid DataContext="{StaticResource vm}">
<AutoSuggestBox Margin="40" QueryIcon="Find"
PlaceholderText="Search"
Text="{Binding Source={StaticResource vm}, Path=city, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Page>
I expect "Orlando" to appear in my AutoSuggestBox
When you use Binding, you need to set the DataContext of the page to be an instance of your binding source class instead of set the page.Resource. For more details, you can refer to this document.
<Page.DataContext>
<local:WeatherVM x:Name="viewModelInDataContext"/>
</Page.DataContext>
<Grid>
<AutoSuggestBox Margin="40" QueryIcon="Find"
PlaceholderText="Search"
Text="{Binding Path=city, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
In code-behind, suppose you need to assign a value to the city.
this.viewModelInDataContext.city = "Orlando";
Update:
The ViewModel you showed has some issues. First, you need to assign a value to the cityData property, if not, the text of AutoSuggestBox which bound with the cityData will always display empty. Second, you need to implement INotifyChanged in WeatherVM. When your cityData changes, the text of the AutoSuggestBox will change. Third, do not set the property type to asynchronous like Task, this will affect the display, it's better to set string cityData.
public class WeatherVM : INotifyPropertyChanged
{
public WeatherVM(){
GetData();
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _cityData;
public string cityData
{
get
{
return _cityData;
}
set
{
_cityData = value;
OnPropertyChanged();
}
}
private async void GetData()
{
cityData = await MapLocator.GetCityData();
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
So far I can pass the value to the other view but the problem is I don't know how to do this using MVVM. I tried the documentations and tutorial still no luck. How can I achieve this?
The flow of my project:
- The user will login, when the user provides the correct it will return a JSON array that contains the ContactID of the user.
- This ContactID now be pass to the other view. It will be used to synchronize the server to the local database and vice versa
My Questions are:
1. How can I pass the data to other view with MVVM?
2. How can I check if the data is passed correctly?
The Output of the HTTPWebRequest:
[{"ContactID":"1"}]
My Code:
LoginPageViewModel.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Input;
using TBSMobileApplication.Data;
using TBSMobileApplication.View;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace TBSMobileApplication.ViewModel
{
public class LoginPageViewModel : INotifyPropertyChanged
{
void OnPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
public string username;
public string password;
public string Username
{
get { return username; }
set
{
username = value;
OnPropertyChanged(nameof(Username));
}
}
public string Password
{
get { return password; }
set
{
password = value;
OnPropertyChanged(nameof(Password));
}
}
public class LoggedInUser
{
public int ContactID { get; set; }
}
public ICommand LoginCommand { get; set; }
public LoginPageViewModel()
{
LoginCommand = new Command(OnLogin);
}
public void OnLogin()
{
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
{
MessagingCenter.Send(this, "Login Alert", Username);
}
else
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
var link = "http://192.168.1.25:7777/TBS/test.php?User=" + Username + "&Password=" + Password;
var request = HttpWebRequest.Create(string.Format(#link));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
{
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
}
else
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if (content.Equals("[]") || string.IsNullOrWhiteSpace(content) || string.IsNullOrEmpty(content))
{
MessagingCenter.Send(this, "Http", Username);
}
else
{
var result = JsonConvert.DeserializeObject<List<LoggedInUser>>(content);
var contactId = result[0].ContactID;
Application.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage(contactId), true);
}
}
}
}
}
else
{
MessagingCenter.Send(this, "Not Connected", Username);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
DatabaseSyncPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TBSMobileApplication.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DatabaseSyncPage : ContentPage
{
public DatabaseSyncPage (int contanctId)
{
InitializeComponent ();
}
}
}
If you are new to MVVM i would highly recommend using an MVVM helper framework such as Prism, MVVMCross or MVVMLight (there are even more).
I myself use Prism, I believe all of the frameworks are functionally very similar and it comes down more to preference here. I will show you how I pass data between views in my Prism based applications. Before we get started it would be worth to download the prism visual studio extensions and use the template pack to generate a prism project. I use the DryIoc container.
Imagine the scenario where we have ViewA (with ViewAViewModel) and ViewB (with ViewBViewModel). In View A we have an Entry and a Button, when the button is pressed the text from the entry in ViewA is passed to ViewB where it is displayed in a label.
You would first setup your prism project, creating a XAML fronted view for View A & B and then creating 2 class files and creating the relevant View Models (I'll show you how).
Firstly creating the following files:
ViewA (Xaml content page)
ViewB (Xaml content page)
ViewAViewModel (empty class)
ViewBViewModel (empty class)
In your app.cs register the views and view models:
//You must register these views with prism otherwise your app will crash!
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>();
}
Now format your view models by adding the following:
public class ViewAViewModel : ViewModelBase
{
INavigationService _navigationService;
public ViewAViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "ViewA";
_navigationService = navigationService;
}
}
Repeat the above step for ViewBViewModel also (changing the relevant names).
Now in the views xaml lets add some stuff! Add the following to ViewA.xaml (inside <ContentPage.Content></ContentPage.Content>:
<StackLayout>
<Entry Placeholder="Type Here..." Text="{Binding ViewAText}"/>
<Button Text="Navigate" Command="{Binding OnNavigateCommand}"/>
</StackLayout>
and in ViewB.xaml:
`<Label Text="{Binding TextFromViewA}"/>`
Now I've already added the binding for you, so lets make the properties!
In View Model A add:
private string _viewAText;
public string ViewAText
{
get { return _viewAText; }
set { SetProperty(ref _viewAText, value); }
}
public DelegateCommand OnNavigateCommand { get; set; }
private void OnNavigate()
{
//Do Something
}
Now we have a bindable property and a command for our button press, add the following to the constructor:
public ViewAViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "ViewA";
_navigationService = navigationService;
_viewAText = string.Empty;
OnNavigateCommand = new DelegateCommand(OnNavigate);
}
Now View A can bind text from the entry control and has an event handler for the command!
Lets hop into View B and wire that up!
Add the property:
private string _textFromViewA;
public string TextFromViewA
{
get { return _textFromViewA; }
set { SetProperty(ref _textFromViewA, value); }
}
and in the constructor:
public ViewBViewModel(INavigationService navigationService) : base(navigationService)
{
Title = "ViewB";
TextFromViewA = string.Empty;
}
Now the label we added in ViewB is hooked up to the view model. Lets now pass the text from the entry in A to B!
Back in View A add the following to the OnNavigate method:
private void OnNavigate()
{
NavigationParameters navParams = new NavigationParameters();
navParams.Add("PassedValue", _viewAText);
_navigationService.NavigateAsync("ViewB", navParams);
}
The navigation service is incredibly powerful and allows you to pass a dictionary between views (NavigationParameters). In this code we have created some NavigationParameter, added the value of the text in our entry to them and then asked the navigationService (which handles all navigation from viewmodels in Prism) to navigate to ViewB, passing the parameters to it.
In View B we can listen for these parameters using some built in methods provided by Prism. If you type override in ViewBViewModel you will see the methods:
OnNavigatingTo
OnNavigatedTo
OnNavigatedFrom
In this case we want to use OnNavigatingTo (which is fired during the transition between the views). Pull that method in and the following:
public override void OnNavigatingTo(NavigationParameters parameters)
{
base.OnNavigatingTo(parameters);
if (parameters.ContainsKey("PassedValue"))
{
_textFromViewA = (string)parameters["PassedValue"];
RaisePropertyChanged("TextFromViewA");
}
}
Here we check if the parameters contain the value we added (by searching for the dictionary key) and then retrieve the value (casting it to a string since the dictionary is ). We then set the property the label is bound to = to the passed value and then use a prism method, RaisePropertyChanged() to raise a property changed event so that the label's binded value updates!
Below is a gif of the results!
This might be alot to take in. I would advise you start using an MVVM framework asap, they are really easy to use and I would consider them essential to making testable, decoupled MVVM xamarin apps!
For more on how prism works, I'd suggest to go read the docs and watch Brian Lagunas' appearance on the Xamarin Show!
Good Luck!
i had implemented the same and hope this helps you.
i have create a loginViewModel
public class LoginVerificationVM : BaseViewModel // INotifyPropertyChanged
{
private INavigation _navigation;
private string usermobileno;
public string UserMobileNo
{ get { return usermobileno; }set { usermobileno = value;
OnPropertyChanged("UserMobileNo"); }
}
public LoginVerificationVM(INavigation navigation, string mobileno)
{
UserMobileNo = mobileno;
_navigation = navigation;
}
public Command Login
{
get
{
return new Command(async () =>
{
bool status = await WebApi.CheckNetWorkStatus();
if (status == false)
{
MessageClass.messagesWindow("Check Ur Connectivity");
this.Isvisible = false;
return;
}
Isvisible = true;
UserAuth ud = new UserAuth();
ud.username = UserMobileNo; // UserMobileNo;
ud.password = Password; // Password
ud.grant_type = "password"; //GrantType
Isvisible = true;
// IsBusy = false;
await Task.Delay(100);
var json = Task.Run(() => WebApi.GetUserAuth(ud)).Result;
// IsBusy = false;
if (json.ErrorMessage == "true")
{
Application.Current.MainPage = new MasterPages.MasterPage(json.access_token); //or use _navigation.PushAsync(new ForgotPasswordOTP(UserMobileNo));
}
else
{
MessageClass.messagesWindow(json.ErrorMessage);
}
Isvisible = false;
});
}
}
}
Xaml Code
<Entry x:Name="PasswordEntry" Grid.Row="2" IsPassword="True" Placeholder="******" HorizontalTextAlignment="Center" FontAttributes="Bold" TextColor="Black" WidthRequest="150" HeightRequest="35" FontSize="13" Text="{Binding Password, Mode=TwoWay}" >
<Button x:Name="Login" Grid.Row="3" HorizontalOptions="Center" BorderRadius="8" Text="Login" WidthRequest="100" BackgroundColor="#f7941d" TextColor="White" Command="{Binding Login}" IsEnabled="{Binding Active,Mode=TwoWay}">
here is implementation to get data on navigated page view model
public ForgotPasswordOTP(string Ph)
{
InitializeComponent();
BindingContext = new ForgotPasswordOTPViewModel(this.Navigation,Ph);
}
and the last thing you need to do is bind your view with your viewmodel
** BindingContext = new LoginVerificationVM(this.Navigation);**
And the answer for the last question is you need to deserialize json in c#
which can be done in following way
var userData = JsonConvert.DeserializeObject<YourObject>(result);
I have a datagrid which is bound to a collection of items using Caliburn Micro. I would like the grid to update as soon as a user makes an edit on each row. I would think this would be simple (like ASP.NET simple) but I haven't found anything that seems to work.
Here is my ViewModel
public class JournalViewModel : Caliburn.PresentationFramework.PropertyChangedBase
{
private CrystalRptDataEntities ctx = new CrystalRptDataEntities();
private BindableCollection<EmployeeInfo> employees;
public JournalViewModel()
{
Load();
}
public void Load()
{
employees = new BindableCollection<EmployeeInfo>(ctx.EmployeeInfoes);
AllEmployees = employees;
}
public BindableCollection<EmployeeInfo> AllEmployees
{
get { return employees; }
set
{
employees = value;
NotifyOfPropertyChange(() => AllEmployees);
}
}
//....
}
Here is my view
<DataGrid x:Name="AllEmployees" AutoGenerateColumns="True" />
I found the solution to my own question - it took 3 things.
1) I had to add this method to my JournalViewModel class
public void SaveChanges()
{
ctx.SaveChanges();
}
2) Then I had to add these 2 references to my xaml file
xmlns:i="clr-namespace:System.Windows.Interactivity;
assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org"
3) Then I had to attach an Event to my Datagrid like this:
<DataGrid x:Name="AllEmployees"
AutoGenerateColumns="True"
cal:Message.Attach="[Event CellEditEnding]=[Action SaveChanges()]">
That way every time I finished editing a cell, the ctx gets saved.