MvvmCross - Passing a string with IMvxNavigationService - c#

I'm currently working on a Xamarin.iOS project that uses a web-api to gather data. However, I'm running into some problems trying to pass the user input from a textfield to the Tableview that gets the result from the api.
To do this I've followed the example on the MvvmCross documentation.
The problem is that the input from the Textfield never reaches the 'Filter' property in my TableviewController's viewmodel. I think I'm not passing the string object correctly to my IMvxNavigationService when called.
To clarify, in my UserinputViewController I'm binding the textfield's text like so:
[MvxFromStoryboard(StoryboardName = "Main")]
public partial class SearchEventView : MvxViewController
{
public SearchEventView (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel> set = new MvxFluentBindingDescriptionSet<SearchEventView, SearchEventViewModel>(this);
set.Bind(btnSearch).To(vm => vm.SearchEventCommand);
set.Bind(txtSearchFilter).For(s => s.Text).To(vm => vm.SearchFilter);
set.Apply();
}
}
The Viewmodel linked to this ViewController looks like this:
public class SearchEventViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
private string _searchFilter;
public string SearchFilter
{
get { return _searchFilter; }
set { _searchFilter = value; RaisePropertyChanged(() => SearchFilter); }
}
public SearchEventViewModel(IMvxNavigationService mvxNavigationService)
{
this._navigationService = mvxNavigationService;
}
public IMvxCommand SearchEventCommand {
get {
return new MvxCommand<string>(SearchEvent);
}
}
private async void SearchEvent(string filter)
{
await _navigationService.Navigate<EventListViewModel, string>(filter);
}
}
And finally, TableviewController's viewmodel looks like this:
public class EventListViewModel : MvxViewModel<string>
{
private readonly ITicketMasterService _ticketMasterService;
private readonly IMvxNavigationService _navigationService;
private List<Event> _events;
public List<Event> Events
{
get { return _events; }
set { _events = value; RaisePropertyChanged(() => Events); }
}
private string _filter;
public string Filter
{
get { return _filter; }
set { _filter = value; RaisePropertyChanged(() => Filter); }
}
public EventListViewModel(ITicketMasterService ticketMasterService, IMvxNavigationService mvxNavigationService)
{
this._ticketMasterService = ticketMasterService;
this._navigationService = mvxNavigationService;
}
public IMvxCommand EventDetailCommand {
get {
return new MvxCommand<Event>(EventDetail);
}
}
private void EventDetail(Event detailEvent)
{
_navigationService.Navigate<EventDetailViewModel, Event>(detailEvent);
}
public override void Prepare(string parameter)
{
this.Filter = parameter;
}
public override async Task Initialize()
{
await base.Initialize();
//Do heavy work and data loading here
this.Events = await _ticketMasterService.GetEvents(Filter);
}
}
Whenever trying to run, the string object 'parameter' in my TableviewController's Prepare function remains 'null' and I have no idea how to fix it. Any help is greatly appreciated!

I believe the issue is with your command setup
new MvxCommand<string>(SearchEvent);
As this command is being bound to a standard UIButton. It will not pass through a parameter value of your filter but null instead. So the string parameter generic can be removed. Additionally, as you want to execute an asynchronous method I would suggest rather using MvxAsyncCommand
new MvxAsyncCommand(SearchEvent);
Then in terms of SearchEvent method you can remove the parameter. The value of filter is bound to your SearchFilter property. It is this property's value that you want to send as the navigation parameter.
private async Task SearchEvent()
{
await _navigationService.Navigate<EventListViewModel, string>(SearchFilter);
}

Related

Pass ItemClicked in MvxRecyclerView object to new View

I'm newbie in Xamarin.
I have MvxRecyclerView to show list of Cars. Clicking in a car let user to display full specification of chosen car. I have problem with displaying full specification of chosen car in new activity (and at the same time pass object between viewmodels)
My MvxRecyclerView .xml looks like:
<mvvmcross.droid.support.v7.recyclerview.MvxRecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/background_light"
local:MvxItemTemplate="#layout/listitem_car"
local:MvxBind="ItemsSource Cars; ItemClick NavigateCommand"
/>
And my CarsViewModel with "empty" navigation to CarItemViewModel:
public class CarsViewModel : MvxViewModel
{
public CarsViewModel(IMvxNavigationService navigationService)
{
Cars = new MvxObservableCollection<Car>();
_navigationService = navigationService;
NavigateCommand = new MvxAsyncCommand(() => _navigationService.Navigate<CarItemViewModel>());
}
private MvxObservableCollection<Car> _cars;
public MvxObservableCollection<Car> Cars
{
get => _cars;
set
{
_cars = value;
RaisePropertyChanged(() => Cars);
}
}
public override async Task Initialize()
{
await base.Initialize();
CarService carService = new CarService();
await Task.Run(async () =>
{
Cars = await carService.GetCars();
});
}
private readonly IMvxNavigationService _navigationService;
public IMvxAsyncCommand NavigateCommand { get; private set; }
Do you know how is it possible to move data of chosen mvxrecyclerview item to new view using MVVMCross? Unfortunately, I don't understand what MVVMCross' documentation says about this (it looks so poor in my opinion).
I would appreciate for any help.
EDIT 1:
I've changed a bit description of my problem for more transparent.
I think the document is clear about passing parameter, here:
When you navigation, you should pass a MyObject of CarItemViewModel:
await _navigationService.Navigate<CarItemViewModel, MyObject>(new MyObject());
And then you can get the MyObject in the CarItemViewModel and use it:
public class CarItemViewModel: MvxViewModel<MyObject>
{
private MyObject _myObject;
public override void Prepare()
{
// first callback. Initialize parameter-agnostic stuff here
}
public override void Prepare(MyObject parameter)
{
// receive and store the parameter here
_myObject = parameter;
}
public override async Task Initialize()
{
await base.Initialize();
// do the heavy work here
}
}

Using custom navigation service, how to pass a parameter to the next view model?

I am coming up to speed on Xamarin. I am using "Mastering Xamarin.Forms: App architecture techniques for building multi-platform, native mobile apps with Xamarin.Forms 4, 3rd Edition" as a guide. This had me create a custom navigation service.
Here is the implementation (I skipped the interface for brevity)
namespace wfw_dispenser.Services
{
public class XamarinFormsNavService : INavService
{
readonly IDictionary<Type, Type> _map = new Dictionary<Type, Type>();
public event PropertyChangedEventHandler CanGoBackChanged;
public INavigation XamarinFormsNav { get; set; }
public bool CanGoBack => XamarinFormsNav.NavigationStack?.Any() == true;
public async Task GoBack()
{
if (CanGoBack)
{
await XamarinFormsNav.PopAsync(true);
OnCanGoBackChanged();
}
}
public async Task NavigateTo<TVM>()
where TVM : BaseViewModel
{
await NavigateToView(typeof(TVM));
if (XamarinFormsNav.NavigationStack.Last().BindingContext is BaseViewModel)
{
((BaseViewModel)XamarinFormsNav.NavigationStack.Last().BindingContext).Init();
}
}
public async Task NavigateTo<TVM, TParameter>(TParameter parameter)
where TVM : BaseViewModel
{
await NavigateToView(typeof(TVM));
if (XamarinFormsNav.NavigationStack.Last().BindingContext is BaseViewModel<TParameter>)
{
((BaseViewModel<TParameter>)XamarinFormsNav.NavigationStack.Last().BindingContext).Init(parameter);
}
}
public void RemoveLastView()
{
if (XamarinFormsNav.NavigationStack.Count< 2)
{
return;
}
var lastView = XamarinFormsNav.NavigationStack[XamarinFormsNav.NavigationStack.Count - 2];
XamarinFormsNav.RemovePage(lastView);
}
public void ClearBackStack()
{
if (XamarinFormsNav.NavigationStack.Count < 2)
{
return;
}
for (var i = 0; i < XamarinFormsNav.NavigationStack.Count - 1; i++)
{
XamarinFormsNav.RemovePage(XamarinFormsNav.NavigationStack[i]);
}
}
public void NavigateToUri(Uri uri)
{
if (uri == null)
{
throw new ArgumentException("Invalid URI");
}
Device.OpenUri(uri);
}
async Task NavigateToView(Type viewModelType)
{
if (!_map.TryGetValue(viewModelType, out Type viewType))
{
throw new ArgumentException("No view found in view mapping for " + viewModelType.FullName + ".");
}
// Use reflection to get the View's constructor and create an instance of the View
var constructor = viewType.GetTypeInfo()
.DeclaredConstructors
.FirstOrDefault(dc => !dc.GetParameters().Any());
var view = constructor.Invoke(null) as Page;
var vm = ((App)Application.Current).Kernel.GetService(viewModelType);
view.BindingContext = vm;
await XamarinFormsNav.PushAsync(view, true);
}
public void RegisterViewMapping(Type viewModel, Type view)
{
_map.Add(viewModel, view);
}
void OnCanGoBackChanged() => CanGoBackChanged?.Invoke(this, new PropertyChangedEventArgs("CanGoBack"));
}
}
It appears to me that there is a NavigateTo that takes a parameter. I tried it and it kind of goes nowhere without any errors in the log. There's nothing in the text about this method to explain how to use it.
I probably have to do something in the "catching" view model for this. Can someone help me out?
First, you must extend from the parameterized version of BaseViewModel. In your case, since you are passing in a PaymentRequest, this would be:
public class CheckoutViewModel : BaseViewModel<PaymentRequest>
Then BaseViewModel<T> has a virtual Init method that you can implement
public class BaseViewModel<TParameter> : BaseViewModel
{
protected BaseViewModel(INavService navService, IAnalyticsService analyticsService)
: base(navService, analyticsService)
{
}
public override void Init()
{
Init(default(TParameter));
}
public virtual void Init(TParameter parameter)
{
}
}

c# validation automation in data anontations

Summary: Im working with C# 4.5 version and more specifically in Web API.
Im trying to build an object and wrap it with attributes so when I receive a HTTP POST request, validation will be made in modelState.
a little example before code:
Lets say I have this following request object
public class PlayerRequest
{
[TeamId]
public string TeamId {set;get;}
[UserId]
public string UserId {set;get;}
}
now, I want to be able to just add an attribute to the class and it will check if class contains TeamId and UserId and if so, validate in db that in fact user has access to team.
so lets say, the declaration will be something like:
[PairsValidate]
public class TeamRequest
{
//...
}
What I aim to create is not a specific validation for TeamId and UserId but to create some sort of a pool of attribute pairs and run a simple loop to detect them and validate.
code so far:
[AttributeUsage(AttributeTargets.Class)]
public sealed class AccessValidator : ValidationAttribute
{
private readonly AttributePairValidator[] _validators =
{
UserIdTeamIdValidator.GetInstance(AccessManager.UserAccessToTeam)
};
public override bool IsValid(object value)
{
PropertyInfo[] properties = value.GetType().GetProperties();
foreach (PropertyInfo p in properties)
{
foreach (AttributePairValidator valPair in _validators)
{
valPair.Accept(/* here is the problem */ , p.GetValue as string);
}
}
}
}
public class AttributePairValidator
{
protected string fieldA;
protected string fieldB;
protected Func<string, string, Task<bool>> _validationMethod;
protected static object _lockObj = new object();
protected AttributePairValidator(Func<string, string, Task<bool>> validationMethod)
{
_validationMethod = validationMethod;
}
public bool Accept (ValidationAttribute attr, string val)
{
return true;
}
protected async Task<bool> Check()
{
if (!String.IsNullOrEmpty(fieldA) && !String.IsNullOrEmpty(fieldB))
return await _validationMethod(fieldA, fieldB);
return true;
}
}
public sealed class UserIdTeamIdValidator : AttributePairValidator
{
private static UserIdTeamIdValidator _instance = null;
private UserIdTeamIdValidator(Func<string, string, Task<bool>> validationMethod) : base (validationMethod)
{
}
public static UserIdTeamIdValidator GetInstance(Func<string, string, Task<bool>> validationMethod)
{
lock (_lockObj)
{
if (_instance == null)
_instance = new UserIdTeamIdValidator(validationMethod);
}
return _instance;
}
public async Task<bool> Accept(UserIdAttribute attr, string val)
{
fieldA = val;
return await Check();
}
public async Task<bool> Accept(TeamIdAttribute attr, string val)
{
fieldB = val;
return await Check();
}
}
other issue, if you guys already know how to solve it.
Im validating the request itself by headers and im storing some data in the actionContext's principal. In controllers i use: ActionContext.RequestContext.Principal.Identity.Name
is there any way to get this data when in validationAttribute scope?
Thanks.

NavigationService WithParam of Caliburn Micro for WP

As is well known, CM doesn't support passing a object of complex type through NavigationService like MVVM Light. So I searched for a workaround and did it like this.
There are two viewmodels: MainPageViewModel and SubPageViewModel.
I first defined 3 classes, namely GlobalData, SnapshotCache and StockSnapshot. StockSnapshot is the type of which the object I want to pass between the 2 viewmodels.
public class SnapshotCache : Dictionary<string, StockSnapshot>
{
public StockSnapshot GetFromCache(string key)
{
if (ContainsKey(key))
return this[key];
return null;
}
}
public class GlobalData
{
private GlobalData()
{
}
private static GlobalData _current;
public static GlobalData Current
{
get
{
if (_current == null)
_current = new GlobalData();
return _current;
}
set { _current = value; }
}
private SnapshotCache _cachedStops;
public SnapshotCache Snapshots
{
get
{
if (_cachedStops == null)
_cachedStops = new SnapshotCache();
return _cachedStops;
}
}
}
public class StockSnapshot
{
public string Symbol { get; set; }
public string Message { get; set; }
}
Next, I call the navigation service on MainPageViewModel like this:
StockSnapshot snap = new StockSnapshot {Symbol="1", Message = "The SampleText is here again!" };
GlobalData.Current.Snapshots[snap.Symbol] = snap;
NavigationService.UriFor<SubPageViewModel>().WithParam(p=>p.Symbol,snap.Symbol).Navigate();
And on SubPageViewModel I've got this:
private string _symbol;
public string Symbol
{
get { return _symbol; }
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
}
}
public StockSnapshot Snapshot
{
get { return GlobalData.Current.Snapshots[Symbol]; }
}
And that's where the problem lies. When I run the program, I find out that it always runs to the getter of Snapshot first, when Symbol hasn't been initialized yet. So later I've tried adding some extra code to eliminate the ArgumentNullException so that it can run to the setter of Symbol and then everything goes fine except that the UI doesn't get updated anyway.
Could anyone tell me where I've got wrong?
Thx in advance!!
Why not just use:
private string _symbol;
public string Symbol
{
get { return _symbol;}
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
NotifyOfPropertyChange(() => Snapshot);
}
}
public StockSnapshot Snapshot
{
get { return Symbol!=null? GlobalData.Current.Snapshots[Symbol]:null; }
}
In this case you don't try and get the data from GlobalData when Symbol is null (sensible approach anyway!) and when "Symbol" is set you call NotifyOfPropertyChange() on Snapshot to force a re-get of the property.

Encapsulating Action<T> and Func<T>?

I'm trying to make a design for some sort of IExecutable interface. I will not get into details, but the point is that I have several Actions that need to be executed from a base class. They may take different parameters (no big deal), and they may/may not return a value.
So far, this is my design:
public abstract class ActionBase
{
// ... snip ...
}
public abstract class ActionWithResultBase<T>: ActionBase
{
public abstract T Execute();
}
public abstract class ActionWithoutResultBase: ActionBase
{
public abstract void Execute();
}
So far, each of my concrete actions need to be a child from either ActionWithResultBase or ActionWithoutResult base, but I really don't like that. If I could move the definition of Execute to ActionBase, considering that the concrete class may or may not return a value, I will have achieved my goal.
Someone told me this could be done with using Func and Action, for which I totally agree, but I can't find a way to have that into one single class so that the caller would know if the action is going to return a value or not.
Brief: I want to do something like:
// Action1.Execute() returns something.
var a = new Action1();
var result = a.Execute();
// Action2.Execute() returns nothing.
var b = new Action2();
b.Execute();
If you want a lightweight solution, then the easiest option would be to write two concrete classes. One will contain a property of type Action and the other a property of type Func<T>:
public class ActionWithResult<T> : ActionBase {
public Func<T> Action { get; set; }
}
public class ActionWithoutResult : ActionBase {
public Action Action { get; set; }
}
Then you can construct the two types like this:
var a1 = new ActionWithResult<int> {
CanExecute = true,
Action = () => {
Console.WriteLine("hello!");
return 10;
}
}
If you don't want to make Action property read/write, then you could pass the action delegate as an argument to the constructor and make the property readonly.
The fact that C# needs two different delegates to represent functions and actions is quite annoying. One workaround that people use is to define a type Unit that represents "no return value" and use it instead of void. Then your type would be just Func<T> and you could use Func<Unit> instead of Action. The Unit type could look like this:
public class Unit {
public static Unit Value { get { return null; } }
}
To create a Func<Unit> value, you'll write:
Func<Unit> f = () => { /* ... */ return Unit.Value; }
The following interfaces should do the trick -- it's essentially copying the Nullable pattern
public interface IActionBase
{
bool HasResult { get; }
void Execute() { }
object Result { get; }
}
public interface IActionBase<T> : IActionBase
{
new T Result { get; }
}
public sealed class ActionWithReturnValue<T> : IActionBase<T>
{
public ActionWithReturnValue(Func<T> action) { _action = action; }
private Func<T> _action;
public bool HasResult { get; private set; }
object IActionBase.Result { get { return this.Result; } }
public T Result { get; private set; }
public void Execute()
{
HasResult = false;
Result = default(T);
try
{
Result = _action();
HasResult = true;
}
catch
{
HasResult = false;
Result = default(T);
}
}
}
public sealed class ActionWithoutReturnValue : IActionBase
{
public bool HasResult { get { return false; } }
object IActionBase.Result { get { return null; } }
public void Execute() { //... }
}
You know that you can ignore the return value of a method right? You don't have to use it.
what about something simple:
public class ActionExecuter
{
private MulticastDelegate del;
public ActionExecuter(MulticastDelegate del)
{
this.del = del;
}
public object Execute(params object[] p)
{
return del.DynamicInvoke(p);
}
}

Categories

Resources