I can't understand why the WhenActivated func is not fired for a Xamarin Forms application. The application has a LoginViewModel and a LoginView
LoginView: inherits from ContentPageBase which itself derives from : ContentPage, IViewFor which I think is expected
LoginViewModel: ReactiveObject, IRoutableViewModel, ISupportsActivation
Here's the sample application code.
The first view LoginView is loaded as expected. However, I would ideally like to load services and setup bindings in the WhenActivated func but it is not firing. See the LoginViewModel.
Any ideas?
Source code
thanks
Ok ossentoo, I'm not sure why but seems like if you want to use WhenActivated on your viewmodel you must use when activated on your view, the bindings are placed on view's code behind. Here is a little example:
public class LoginViewModel : ViewModelBase /*IRoutableViewModel, ISupportsActivation etc*/
{
this.WhenActivated(disposables =>
{
Debug.WriteLine("ViewModel activated.").DisposeWith(disposables);
});
}
On the view:
public partial class LoginView : ContentPageBase<LoginViewModel>
{
public LoginView()
{
InitializeComponent ();
this.WhenActivated(new Action<CompositeDisposable>(c =>
{
System.Diagnostics.Debug.WriteLine("From View ! (When Activated)");
/*instead of xaml bindings you can use ReactiveUI bindings*/
this.Bind(ViewModel, vm => vm.UserName,
v => v.UserName.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Password,
v => v.Password.Text).DisposeWith(disposables);
}));
}
}
Let me know if this helped you. :)
Related
I'm trying to make authorization in xamarin forms and i dont know how to structure it. I'm using MVVM and for my authorization i wanna use JWT . I want to check if the token is valid then go to certain page . when i put the validation code inside the onappearing method the page is still visible for a very small amount of time and when i put it inside the constructor of the page the navigation doesn't work. How should this authorization should be done?(should i make another transition page with something like an activity indicator ?)
this is the code i use for the token validation
public async Task CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
}
}
when i put it inside the constructor of the page the navigation
doesn't work.
You can call the push service like this in Main thread:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new myViewModel();
CheckIfUserIsLoggedIn();
}
public async void CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
Device.BeginInvokeOnMainThread(async () => {
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
});
}
}
}
How to use a NSTextView in the background of an Xamarin.Forms application and feed it with key events so that it behaves (input wise) like it was part of a window and is focused?
I already tried to create an instance and feed it all key events but this was not enough (didn't fire any event)
_textView = new NSTextView();
//...
public override void DidFinishLaunching(NSNotification notification)
{
NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyDown, #event =>
{
_textView.KeyDown(#event);
return #event;
});
NSEvent.AddLocalMonitorForEventsMatchingMask(NSEventMask.KeyUp, #event =>
{
_textView.KeyUp(#event);
return #event;
});
Forms.Init();
LoadApplication(new App());
base.DidFinishLaunching(notification);
}
I found a solution to this problem.
I created a KeyHandlingContentView derived from ContentView in Xamarin.Forms and a custom Renderer for Mac that implements INSTextInputClient.
This way the key events can be passed from that custom renderer to the Xamarin.Forms control.
I am having trouble with my CollectionView which is showing duplicates objects each time I display the page, meaning the first time I will have 1 object (which is normal), but if I navigate and return to that page it will duplicate the same object, so I get it twice, then thrice ..etc...
Below is the code in the constructor of the ViewModel of my page:
Cache
.AutoRefresh(x => x.Code)
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _list)
.DisposeMany();
_list is a ReadOnlyObservableCollection<AddressViewModel>
Cache is IObservable<IchangeSet<AddressModel, string>> obtained from a GetAllObjects from Akavache cache.
_blobCache
.GetAllObjects<AddressModel>()
.ToObservableChangeSet(t => t.Code)
.AsObservableCache()
.Connect();
The binding to my CollectionView is
this.OneWayBind(ViewModel, vm => vm.List, v => v.addressList.ItemsSource).DisposeWith(disposables);
Thank you for any help or hint
UPDATE
The duplicating behaviour happens ONLY when navigating between tabs, if I push a new page and go back I find again 1 object which is the intended behaviour.
Here is the code setting the BindingContext of the page
In code behind of the TabbedPage
public partial class MainTabbedPage : ReactiveTabbedPage<MainViewModel>
{
public MainTabbedPage()
{
InitializeComponent();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.AddressVm, x => x.addressView.ViewModel)
.DisposeWith(disposables);
//other tabs...
});
}
}
In the MainViewModel
public AddressViewModel AddressVm => new AddressViewModel(HostScreen);
public MainViewModel(IScreen hostScreen) : base(hostScreen)
{
}
I have a problem when I try to bind the "Enabled" property of my Android Button to a Boolean of my ViewModel using the MvvmCross framework and I really don't know the origin of it.
So I have a ViewModel which contains the two following properties :
private ProjectDetailDTO _projectDetail;
public ProjectDetailDTO ProjectDetail
{
get { return this._projectDetail; }
set
{
_projectDetail = value;
RaisePropertyChanged(() => ProjectDetail);
RaisePropertyChanged(() => HasPicture);
}
}
private bool _hasPicture;
public bool HasPicture
{
get { return ((this.ProjectDetail != null) && !String.IsNullOrEmpty(this.ProjectDetail.Pictures)); }
set { _hasPicture = value;
RaisePropertyChanged(() => HasPicture);
}
}
As you would understand, my button is bind to the HasPicture property. So I have the following code for my button in my .axml file :
<Button
local:MvxLang="Text LblSeePicturesValue"
local:MvxBind="Enabled HasPicture,Click ShowProjectPicturesCommand"
android:id="#+id/buttonPictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
I don't think it's a ViewModel problem because my WP application works well with this code. In fact, my ProjectDetailDTO is filled by calling a web service, so by an asynchronous method. I think it's why when the binding is realized the HasPicture property has the false value. But with my ViewModel code, the HasPicture property should be updated when the ProjectDetailDTO is filled. Is there anything I did wrong in my Android View?
Thanks for any help !
I think what you are seeing here is some interaction between ICommand.CanExecute and the Enabled property. There's a discussion about this on https://github.com/MvvmCross/MvvmCross/issues/729
To work around this, try switching the binding to:
local:MvxBind="Click ShowProjectPicturesCommand;Enabled HasPicture"
(Also note that the separator in bindings is a ; - not a ,)
in a classic Passive-MVP pattern, how can i avoid a reference of the presenter in my view completely & still inject the presenter instance which needs the view instance as a parameter.
with asp.net as an example:
my implemented views (web project) should not have a reference to the Presenters. (Neither IPresenter nor the concrete ones)
when the view instantiates, (basically my web page), the presenter should be instantiated with the current view's reference.
i am using unity as my ioc container.
right now what i do in the web page's code behind is this:
public partial class SomePage : MyBasePage, ISomeView
{
private readonly ISomePresenter presenter;
public SomePage()
{
this.presenter = ResolveSomeWay(this);
}
}
for this i have a reference of the 'Presenter Contracts DLL' in my view implementation. is there a way to avoid this reference completely & still hook up the presenter with the view instance, when the view instantiates?
i just care about the presenter instantiation, since the presenter's constructor can set the passed parameter-view-instance to its View Property & it subscribes to the view's events, for any future communication.
thanks folks for your time.
You could "publish" a new View was instantiated to a Message Bus, to which a Presenter factory could "bind" the instantiated View to a Presenter. Although the View would be agnostic of the presenter, it would not be of the Message Bus.
public partial class SomePage : MyBasePage, ISomeView
{
// Alternative #1
public SomePage(IMessageBus messageBus)
{
// You publish a message saying that a handler for ISomeView is to handle the
// message.
messageBus.Publish<ISomeView>(this);
}
// Alternative #2
public SomePage()
{
Resolver.Resolve<IMessageBus>().Publish<ISomeView>(this);
}
}
// This could be somewhere else in your application (this could be a separate DLL), but
// I will use the Global.asax.cs here, for simplicity
public void Application_Start()
{
Container.Resolve<IMessageBus>()
.Subscribe<ISomeView>(someView =>
{
var presenter = new SomePresenter(someView);
});
}
public interface ISomeView {
event Action<ISomeView> SomeEvent;
}
public class SomePresenter
{
public SomePresenter(ISomeView view) {
// if you attach the presenter to the View event,
// the Garbage Collector won't finalize the Presenter until
// the View is finalized too
view.SomeEvent += SomeEventHandler;
}
private void SomeEventHandler(ISomeView view) {
// handle the event
}
}