I need to display a MessageDialog to the user if certain fields in the view are blank when they click a button to navigate. I can handle the input field validation from the view model just fine, I'm just not sure how to invoke a messagedialgo.showasync method from the view model and have it display on the view. Any suggestions?
I saw the sample and lib source of Caliburn.Micro. IWindowManager interface is only for WPF and Silverlight. For WinRT the sample contains this helper class.
using System;
using Windows.UI.Popups;
namespace Caliburn.Micro.WinRT.Sample.Results
{
public class MessageDialogResult : ResultBase
{
private readonly string _content;
private readonly string _title;
public MessageDialogResult(string content, string title)
{
_content = content;
_title = title;
}
public async override void Execute(ActionExecutionContext context)
{
var dialog = new MessageDialog(_content, _title);
await dialog.ShowAsync();
OnCompleted();
}
}
}
It can be called from viewmodel like this
new MessageDialogResult("content", "title");
See the code at CodePlex
CoroutineViewModel.cs
MessageDialogResult.cs
Related
This question is a follow up to this problem here: How to make retrofit API call using ViewModel and LiveData
The mistakes 1 and 2 highlighted in that post's response have been fixed. For mistake 3, I haven't yet moved the API call to a repository, but I will once the code start working properly.
So I'm trying to make an API call using Retrofit, using MVVM with LiveData and ViewModel. The API call (which currently is in the ViewModel), is working properly, but the changes is not being picked up by the Observer in the Activity.
I've setup my ViewModel observer as follow:
public class PopularGamesActivity extends AppCompatActivity {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popular_games);
PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
#Override
public void onChanged(#Nullable List<Game> gameList) {
String firstName = gameList.get(0).getName();
Timber.d(firstName);
}
And my ViewModel code is as follow:
public class PopularGamesViewModel extends AndroidViewModel {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
public PopularGamesViewModel(#NonNull Application application) {
super(application);
}
public LiveData<List<Game>> getGameList() {
final MutableLiveData<List<Game>> gameList = new MutableLiveData<>();
// Create the retrofit builder
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(igdbBaseUrl)
.addConverterFactory(GsonConverterFactory.create());
// Build retrofit
Retrofit retrofit = builder.build();
// Create the retrofit client
RetrofitClient client = retrofit.create(RetrofitClient.class);
Call<List<Game>> call = client.getGame(FIELDS,
ORDER,
LIMIT);
call.enqueue(new Callback<List<Game>>() {
#Override
public void onResponse(Call<List<Game>> call, Response<List<Game>> response) {
Timber.d("api call sucesss");
if (response.body() != null) {
Timber.d("First game: " + response.body().get(0).getName());
gameList.setValue(response.body());
}
}
#Override
public void onFailure(Call<List<Game>> call, Throwable t) {
Timber.d("api call failed");
}
});
return gameList;
}
}
When I run the code, the onResponse in the ViewModel class will output the correct response from the API call, so the call is working properly. But the onChanged() in the PopularGamesActivity class will never get called. Can someone shed some light on what I'm doing wrong? Thank you!
Ok, so this turned out to be a weird android studio bug. I was initially running the code on my real nexus 4 device, and the onChange never gets called. However, after running it on an emulated device, it started working immediately. And now, it's working on my real device too.
I don't know the actual reason behind it, but if anyone in the future run into a problem where onChange won't get called, try switching device/emulators.
Cheers.
I debug the code on a device and have the same problem.
M'n solution was simply activate the device screen.
The screensaver was the problem..
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 method called creatcomponents() where I am creating few text fields and buttons in a composite. Now I want to write listeners to the button which calls a method and in this method I have get the values of the text fields. The problem I am facing is I am unable to access textfields from the method called in the listeners. Can someone help me on how to acheive this?
One way is to save the controls are fields in your class:
public class MyClass
{
private Text text1;
private Text text2;
public void createComponents(Composite parent)
{
Composite composite = new Composite(parent, SWT.None);
text1 = new Text(composite, SWT.SINGLE);
text2 = new Text(composite, SWT.SINGLE);
text1.addModifyListener(new ModifyListener()
{
#Override
public void modifyText(ModifyEvent event)
{
// Access field
String text = text1.getText();
}
});
}
}
Also note that many of the event classes passed to listeners have a widget field which refers to the current control which you can also use:
text1.addModifyListener(new ModifyListener()
{
#Override
public void modifyText(ModifyEvent event)
{
Text control = (Text)event.widget;
String text = control.getText();
}
});
I am currently programming using the MVVM pattern.
My view Model Looks like so
class DoorsViewModel
{
ObservableCollection<Door> doorCollection;
};
the door class looks like the following
class Door
{
string name;
bool isOpen;
};
my view is linked to the viewmodel, and simply contains a longlistselector with a picture and the name of the door. I want the picture to be dynamic and change depending on the state of the door( whether it's open or closed ). How would i implement it so that the picture updates dynamically depending on the state of the door? should this be done in the viewmodel? or should it be done within the view?
This logic should be in the ViewModel. All logic that is related to the view or how things are displayed should be in the ViewModel. No logic should be in the view (.xaml.cs).
You typically use the INotifyPropertyChanged interface to notify the View that something has changed. In this case you want a door image to be changed when the state of the door changes. In this case I would try something like this.
class Door: INotifyPropertyChanged
{
private string _name;
private bool _isOpen;
public Uri DoorImage
{
get
{
if (_isOpen) return new Uri("uri_to_open.png");
return new Uri("uri_to_closed.png");
}
}
public bool IsOpen
{
get { return _isOpen; }
set
{
_isOpen = value;
RaisePropertyChanged("IsOpen");
// important, notifies the UI to update the door image
RaisePropertyChanged("DoorImage");
}
}
private void RaisePropertyChanged(string propertyName)
{
var tmp = PropertyChanged;
if (tmp != null) tmp(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
};
Note: I have encapsulated the fields into properties.
If your image is embedded in your assebmly, please check out this link to learn how to write an uri for your image.
The Navigation framework in Windows Phone 7 is a cut down version of what is in Silverlight. You can only navigate to a Uri and not pass in a view. Since the NavigationService is tied to the View, how do people get this to fit into MVVM. For example:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container, IView view)
{
this.container = container;
this.view = view;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView { get { return this.view; } }
public void GoToNextPage()
{
// What do I put here.
}
}
public class View : PhoneApplicationPage, IView
{
...
public void SetModel(IViewModel model) { ... }
}
I am using the Unity IOC container. I have to resolve my view model first and then use the View property to get hold of the view and then show it. However using the NavigationService, I have to pass in a view Uri. There is no way for me to create the view model first. Is there a way to get around this.
Instead of passing the view through the constructor. You could construct the view first via the NavigationService and pass it into the view-model. Like so:
public class ViewModel : IViewModel
{
private IUnityContainer container;
private IView view;
public ViewModel(IUnityContainer container)
{
this.container = container;
}
public ICommand GoToNextPageCommand { get { ... } }
public IView
{
get { return this.view; }
set { this.view = value; this.view.SetModel(this); }
}
public void GoToNextPage()
{
// What do I put here.
}
}
PhoneApplicationFrame frame = Application.Current.RootVisual;
bool success = frame.Navigate(new Uri("View Uri"));
if (success)
{
// I'm not sure if the frame's Content property will give you the current view.
IView view = (IView)frame.Content;
IViewModel viewModel = this.unityContainer.Resolve<IViewModel>();
viewModel.View = view;
}
If you are using Mvvm Light you could try:
Windows Phone 7 — Navigation between pages using MVVM Light Messaging
(See similar post: Silverlight Navigation using Mvvm-light(oobe)+MEF?)
My opinion is that the view-model should be created and registered at application startup. By placing it inside the root DataContext all pages will automatically get a reference to it without any code-behind or IoC tricks.
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
RootFrame.DataContext = m_ViewModel;
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
m_ViewModel = new PrimaryViewModel(RootFrame) ;
m_ViewModel.Activated(PhoneApplicationService.Current.State);
RootFrame.DataContext = m_ViewModel;
}
If you are using MVVM architecture,then you can pass navigationPage after registering using Messenger. Create a model class (say NavigateToPageMessage) with a string(say PageName) variable. You want to pass string from homepage.xaml to newpage.xaml,then in Homepage viewmodel just send the message like this under the command you binded (say HomeNavigationCommand)
private void HomeNavigationCommandHandler()
{
Messenger.Default.Send(new NavigateToPageMessage {PageName = "newpage"});
}
In the newpage Viewmodel,you should register the messenger like this,
Messenger.Default.Register<NavigateToPageMessage>(this, (action) => ReceiveMessage(action));
private object ReceiveMessage(NavigateToPageMessage action)
{
var page = string.Format("/Views/{0}.xaml", action.PageName);
NavigationService.Navigate(new System.Uri(page,System.UriKind.Relative));
return null;
}
//Assuming your views are in View Folder