MVVM Light Messenger firing twice - mvvm-light

I am using Messenger to broadcast some information from a page code behind (I can't use eventtocommand) and messenger action is fring twice
here's my code
In viewModel
public CedareImpozitViewModel()
{
if (IsInDesignMode)
{
}
else
{
LoadListaPers();
LoadListaEntitati();
Messenger.Default.Register<EntitateMessage>(this, AdaugaEntitateNoua);
}
UpdCedareCommand = new RelayCommand(() => UpdCedare(), () => this.CanUpdCedare());
}
From page code behind
Messenger.Default.Send(new EntitateMessage(cedare) { cedarenoua = cedare, entNoua = entity});
and EntitateMessage class
public class EntitateMessage : GenericMessage<CedareImpozit>
{
public EntitateMessage(CedareImpozit parm)
: base(parm)
{
entNoua = null;
cedarenoua = null;
}
public CedareImpozit cedarenoua { get; set; }
public Entitate entNoua { get; set; }
}
I am not navigating twice from my page.
Any ideas ?
Thank you

The experience shows that when such a thing happens, the cause is always that the registration occurred twice. Place a breakpoint on the Register method call to verify.
Cheers,
Laurent

Related

Realtime validation of a button, using fody

I a trying to disable and enable a button based on user input. I implemented fody property changed Nuget package, to help me reduce my code a bit.
and it works, when I start typing, the breakpoint in my LoginViewModel gets hit and display my values ViewModel getting hit every time I type
but I can't seem to trigger CanLogin() method
[AddINotifyPropertyChangedInterface]
public class LoginPageViewModel {
public ICommand OpenRegisterPopupCommand { get; set; }
public ICommand Register { get; set; }
public ICommand Login { get; set; }
public Users Users { get; set; }
public bool IsPopUpOpen { get; set; }
public LoginPageViewModel() {
Users = new Users();
Login = new Command(LoginAction, CanLogin);
Register = new Command(RegisterAction);
OpenRegisterPopupCommand = new Command(() => {
IsPopUpOpen = true;
});
}
private void LoginAction(object obj) {
throw new NotImplementedException();
}
private bool CanLogin(object arg) {
if (Users != null && !string.IsNullOrEmpty(Users.Email) && !string.IsNullOrEmpty(Users.Password)) {
return true;
}
return false;
}
You are using a Command with a CanExecute method. If the CanExecute method returns false, the command will not ve able to be executed. But this validation does not happen all the time, you have to trigger it.
You have to call Login.ChangeCanExecute() when you modify any of the related properties (like Users, Users.Email or Users.Password), this will fire the CanExecute validation of the command.
Command documentation.

How to get data from Web API to Xamarin forms Picker?

I'm developing a Xamarin APP and I want to load a Picker with data from a Web API that has Server Database. I tried to Google this but most of the articles don't show the content of source "Services class" that use Get async Method, Model class and ViewModel. I would be very grateful if someone could help me with an example.
This is my Controller in ASP.NET Web API
// GET: api/TipoUsers
public IQueryable<TipoUser> GetTipoUsers()
{
return db.TipoUsers;
}
Model class
public class TipoUsuario
{
public int IdTipoUsuario { get; set; }
public string Nome { get; set; }
}
ViewModel class
public class UsuarioViewModel
{
public ObservableCollection<TipoUsuario> tipos { get; set; }
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
tipos = new ObservableCollection<TipoUsuario>(task.Result);
}
}
Xaml Page
<Picker Title="Selecione o Tipo de Usuario"
ItemsSource="{Binding tipos}"
ItemDisplayBinding="{Binding Nome}"/>
Xaml.cs
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class UsuarioPage : ContentPage
{
public UsuarioPage()
{
InitializeComponent();
BindingContext = new UsuarioViewModel();
}
}
}
Service class
public class ApiService
{
public const string Url = "http://thisismysite:44342/";
public static async Task<List<TipoUsuario>> GetTipoUsers()
{
try
{
HttpClient client = new HttpClient();
string url = Url + "/api/TipoUsers";
string response = await client.GetStringAsync(url);
List<TipoUsuario> tipos = JsonConvert.DeserializeObject<List<TipoUsuario>>(response);
return tipos;
}
catch (Exception)
{
throw;
}
}
}
when I debug the app it just doesn't load the screen.
This can happen for a few reasons, I would check your async method isn’t throwing an exception that you aren’t able to see. Async methods return a Task object and if an exception is thrown inside it will be visible in the returned object in Task.Exception.
https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
Also property changed events aren’t called when you set an ObserableCollection to a new instance, you want to add and remove from the collection.
You want to change:
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
tipos = new ObservableCollection<TipoUsuario>(task.Result);
}
to something like:
public UsuarioViewModel() {
Task<List<TipoUsuario>> task = ApiService.ObterTipoUsuarios();
var temptipos = task.Result;
foreach(var tipo in temptipos)
{
tipos.Add(tipo);
}
}

Xamarin.Forms ListView with ReactiveUI does not update new items

I am developing a Xamarin.Forms app. It have SearchBar in the NavigationBar, ListView in the ContentPage and a Entry with AddButton in the bottom. When user click on the AddButton the text in the Entry adds to the realm mobile database. Which autorefresh the IEnumerable. The ListView that is bind to IEnumerable auto updates.
public class MainViewModel : ReactiveObject
{
public IEnumerable<Company> Companies { get; set; }
[Reactive]
public string Query { get; set; }
public string NewCompany { get; set; }
public ReactiveCommand<Unit, Unit> AddCompanyCommand { get; set; }
public ReactiveCommand<Unit, IEnumerable<Company>> SearchCommand { get; set; }
Realm _realm;
public MainViewModel()
{
_realm = Realm.GetInstance();
Companies = _realm.All<Company>();
AddCompanyCommand = ReactiveCommand.CreateFromTask(async () => await AddButtonClicked());
SearchCommand = ReactiveCommand.Create<Unit, IEnumerable<Company>>(
_ =>
SortCollection()
);
SearchCommand.ToProperty(this, nameof(Companies));
this.WhenAnyValue(x => x.Query).Throttle(TimeSpan.FromSeconds(1)).Select(_ => Unit.Default).InvokeCommand(this, x => x.SearchCommand);
}
async Task AddButtonClicked()
{
if (!string.IsNullOrWhiteSpace(NewCompany))
{
_realm.Write(() =>
{
_realm.Add(new Company { Name = NewCompany });
});
NewCompany = string.Empty;
}
}
IEnumerable<Company> SortCollection()
{
if (string.IsNullOrWhiteSpace(Query))
{
Companies = Companies.Where(x => x.Name != string.Empty);
}
else
{
Companies = Companies.Where(x => x.Name.IndexOf(Query, StringComparison.InvariantCultureIgnoreCase) >= 0);
}
return Companies;
}
}
Recently when I added Search Logic to the ViewModel the ListView is not Auto Updating. I either have to search or restart the app to display the new item in the ListView. When I comment out the following line the ListView starts auto updating.
SearchCommand.ToProperty(this, nameof(Companies));
But then it stops displaying Search Results. I want both Auto updating with new item and displaying search result functionalities in the ListView.
At first glance, your public IEnumerable<Company> Companies { get; set; } isn't notifying property change events.
The fact that you need to tell the runtime that the property has changed so you need to implement notify property changed interface
Look at this another stackoverflow thread.
Implementing INotifyPropertyChanged - does a better way exist?
Also there is is a plugin named fody which automatically injects propertychanged code into every setter of the properties in the project. Without needing you to do anything.
Install both of them in the project where you are writing your properties, it will automatically inject property changed code while compiling.
https://www.nuget.org/packages/Fody/
https://www.nuget.org/packages/PropertyChanged.Fody/

The optimal way to decouple business logic from controller

The rule is that controllers shouldn't have business logic, instead they should delegate it to the services. But when we do that, we can't handle all possible cases and return appropriate HTTP response.
Let's look at an example. Let's say that we are building some kind of a social network, and we need to create an endpoint for rating (liking or disliking) a post.
First let's take a look at an example where we delegate the logic to the service, this is our controller action:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do you see a problem in this? What if there is no post with the given id, how would we return a not found response? What if user has no permissions to rate a post, how would we return a forbidden response?
PostsService.Rate can only return a new PostRating, but what about other cases? Well, we could throw an exception, we would need to create a lot of custom exception, so that we can map them to the appropriate HTTP responses. I don't like to use exceptions for this, I think there is a better way to do handle these cases instead of exceptions. Because I think that cases when post doesn't exist and when user has no permissions aren't exceptional at all, they're just normal cases just like rating a post successfully.
What I propose, is handling that logic in a controller instead. Because in my opinion, that should be a controllers responsibility anyway, to check all of the permissions before commiting an action. So this is how I would do it:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var post = _postsRepository.GetByIdWithRatings(postId);
if (post == null)
return NotFound();
if (!_permissionService.CanRate(user, post))
return Forbidden();
PostRating newPostRating = new PostRating
{
Post = post,
Author = user,
Type = ratingType
};
_postRatingsRepository.Save(newPostRating);
return Created(newPostRating);
}
This is the way it should be done in my opinion but I bet that someone would say that this is too much logic for the controller, or that you shouldn't use a repository in it.
If you don't like using a repository in controller than where instead would you put a method that gets or saves posts? In service? So there would be PostsService.GetByIdWithRatings and PostsService.Save that would do nothing else but just call PostsRepository.GetByIdWithRatings and PostsRepository.Save. This so unnecessary and only causes boilerplate code.
Update:
Maybe someone will say to check the permissions using PostsService and then call PostsService.Rate. This is bad because it involves more unnecessary trips to database. For an example, it would probably be something like this:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
if(_postsService.Exists(postId))
return NotFound();
if(!_postsService.CanUserRate(user, postId))
return Forbidden();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do I even need to explain any further why this is bad?
There's a number of ways to handle this, but the closest thing to a "best practice" method is probably using a result class. For example, if your service method creates a rating and then returns that rating it created, you instead return an object that encapsulates the rating along with other relevant information, such as success status, error messages, if any etc.
public class RateResult
{
public bool Succeeded { get; internal set; }
public PostRating PostRating { get; internal set; }
public string[] Errors { get; internal set; }
}
Then, your controller code would become something like:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var result = _postsService.Rate(postId, ratingType, user);
if (result.Succeeded)
{
return Created(result.PostRating);
}
else
{
// handle errors
}
}
What I did (just now) is created new class ApiResult
public class ApiResult
{
public int StatusCode { get; private set; } = 200;
public string RouteName { get; private set; }
public object RouteValues { get; private set; }
public object Content { get; private set; }
public void Ok(object content = null)
{
this.StatusCode = 200;
this.Content = content;
}
public void Created(string routeName, object routeValues, object content)
{
this.StatusCode = 201;
this.RouteName = routeName;
this.RouteValues = routeValues;
this.Content = content;
}
public void BadRequest(object content = null)
{
this.StatusCode = 400;
this.Content = content;
}
public void NotFound(object content = null)
{
this.StatusCode = 404;
this.Content = content;
}
public void InternalServerError(object content = null)
{
this.StatusCode = 500;
this.Content = content;
}
}
And a controller base class with a single method TranslateApiResult
public abstract class CommonControllerBase : ControllerBase
{
protected IActionResult TranslateApiResult(ApiResult result)
{
if (result.StatusCode == 201)
{
return CreatedAtAction(result.RouteName, result.RouteValues, result.Content);
}
else
{
return StatusCode(result.StatusCode, result.Content);
}
}
}
And now in controller I do:
[ApiController]
[Route("[controller]/[action]")]
public class MyController : CommonControllerBase
{
private readonly IMyApiServcie _service;
public MyController (
IMyApiServcie service)
{
_service = service;
}
[HttpGet]
public async Task<IActionResult> GetData()
{
return TranslateApiResult(await _service.GetData());
}
}
In your services you inject repositories and other dependencies:
public class MyApiServcie : IMyApiServcie
{
public async Task<ApiResult> GetData()
{
var result = new ApiResult();
// do something here
result.Ok("success");
return result;
}
}
Now, the reason for Api prefix before the Service is that this service is not meant to be the final service containing all logic.
At this point I would split the business logic into different domains so the services (or facades) would end up without Api prefix in them just to differentiate between i.e. CarService. Preferably these services will not know of anything related to API responses, statuses etc. It's up to you how implement it, though.

MVC Mailer pass a model to the mail view

I'm using the MVC Mailer - I have this in my controller:
//
// POST: /ExhibitorInterest/Create
[HttpPost]
public ActionResult Create(ExhibitorInterest exhibitorinterest)
{
if (ModelState.IsValid)
{
db.ExhibitorInterests.Add(exhibitorinterest);
db.SaveChanges();
UserMailer.ExhibitorInterest().Send();
return RedirectToAction("Thankyou");
}
return View(exhibitorinterest);
}
But I want to pass the model exhibitorinterest to the mailer view:
The UserMailer.cs has:
public virtual MvcMailMessage ExhibitorInterest()
{
//ViewBag.Data = someObject;
return Populate(x =>
{
x.Subject = "ExhibitorInterest";
x.ViewName = "ExhibitorInterest";
x.To.Add("me#myemail.co.uk");
});
}
Any ideas how I get exhibitorinterest into the UserMailer.cs - so I can add it to the mail view please?
Thank you,
Mark
Think I figured it out - change the signature of IUSerMailer.cs to:
public interface IUserMailer
{
MvcMailMessage ExhibitorInterest(ExhibitorInterest exhibitorinterest);
And UserMail.cs to:
public virtual MvcMailMessage ExhibitorInterest(ExhibitorInterest exhibitorinterest)
{
ViewBag.Data = exhibitorinterest.xxxxxxx;
Hope it helps someone else.
Thanks, Mark

Resources