I'm trying to apply a style to a button which has been disabled by a command.
I assumed the IsEnabled state was the property that was being triggered by the canexecutechanged event but it seems it does not.
What Button Property is being affected and can I hook into this event so that I can provide a style to the button?
In your viewmodel you can add a property that will lead to enable or disable its buttons. follows an example below.
public Command FacebookLoginCommand { get; set; }
private bool _IsBusy;
public override bool IsBusy
{
get
{
return _IsBusy;
}
set
{
_IsBusy = value;
OnPropertyChanged();
FacebookLoginCommand?.ChangeCanExecute();
GoogleLoginCommand?.ChangeCanExecute();
}
}
public LoginViewModel(IUserDialogs dialogs) : base(dialogs)
{
FacebookLoginCommand = new Command(async () =>
{
using (Dialogs.Loading("Carregando"))
{
IsBusy = true;
await Task.Run(() => new FacebookLoginService(Dialogs).Logar());
await Task.Run(() => Task.Delay(TimeSpan.FromSeconds(3)));
IsBusy = false;
}
}, CanExecute());
private Func<bool> CanExecute()
{
return new Func<bool>(() => !IsBusy);
}
}
Here is the example to login when username length 11 and password at least one.
public class MainViewModel : BaseViewModel
{
public Command LoginIn { get; set; }
public MainViewModel()
{
LoginIn = new Command(async () => await SignIn(), (() => CanExecuteLogin));
}
private string _password;
private string _username;
public string UserName
{
get => _username;
set
{
SetProperty(ref _username, value, nameof(UserName));
SetProperty(ref _canExecuteLogin, IsExecutable(), nameof(CanExecuteLogin));
LoginIn?.ChangeCanExecute();
}
}
public string Password
{
get => _password;
set
{
SetProperty(ref _password, value, nameof(Password));
SetProperty(ref _canExecuteLogin, IsExecutable(), nameof(CanExecuteLogin));
LoginIn?.ChangeCanExecute();
}
}
private bool _canExecuteLogin;
public bool CanExecuteLogin
{
get => _canExecuteLogin;
set => SetProperty(ref _canExecuteLogin, value, nameof(CanExecuteLogin));
}
public bool IsExecutable()
{
if (UserName != null && _password != null)
{
if (UserName.Length == 11 && _password.Length > 0)
return true;
}
return false;
}
private async Task SignIn()
{ //Login Code here }
}
Related
I have a carouselview, in that view I have an ObservableCollection binded as an itemssource. I am able to bind the collection and it would show when I execute the viewmodel's command in the OnAppearing event.
Code that works:
Second Page
public partial class SecondPage : ContentPage
{
public Coll(bool hard, string subject)
{
InitializeComponent();
var vm = (DataSelectionViewModel)BindingContext;
vm.Hard = hard;
vm.Subject = subject;
/* had to set "hard" and "subject" here again, otherwise data won't load */
}
protected override async void OnAppearing()
{
var vm = (DataSelectionViewModel)BindingContext;
base.OnAppearing();
await vm.LoadData.ExecuteAsync().ConfigureAwait(false);
}
}
The viewmodel for second page
public class DataSelectionViewModel : BaseViewModel
{
private string subject;
public string Subject { get => subject; set => SetProperty(ref subject, value); }
private bool hard;
public bool Hard { get => hard; set => SetProperty(ref hard, value); }
public ObservableCollection<Items> FilteredData { get; set; }
public UserSelectionViewModel()
{
_dataStore = DependencyService.Get<IDataStore>();
LoadData= new AsyncAwaitBestPractices.MVVM.AsyncCommand(FilterData);
FilteredData = new ObservableCollection<Items>();
}
public async Task FilterData()
{
FilteredData.Clear();
var filtereddata = await _dataStore.SearchData(Hard, Subject).ConfigureAwait(false);
foreach (var data in filtereddata)
{
FilteredData.Add(data);
}
}
}
First Page where second page gets Hard and Subject values
private async void ButtonClick(object sender, EventArgs e)
{
var vm = (BaseViewModel)BindingContext;
vm.Hard = HardButtonSelected == Hard;
vm.Subject = vm.Subject.ToLower();
await Navigation.PushAsync(new SecondPage(vm.Hard, vm.Subject));
}
So I want to change my code so that if I press the button on the first page, data instantly starts to filter and add to the ObservableCollection and when it's finished, then navigate to the second page. However if I try to load it to the BaseViewModel and then get the data from the second viewmodel it won't show the data.
Code that doesn't work:
Second Page
public partial class SecondPage : ContentPage
{
public SecondPage()
{
InitializeComponent();
}
}
The viewmodel for second page
public class DataSelectionViewModel : BaseViewModel
{
public ObservableCollection<Items> FilteredData { get; set; }
public UserSelectionViewModel()
{
FilteredData = new ObservableCollection<Items>();
}
}
BaseViewModel
public class BaseViewModel : INotifyPropertyChanged
{
private string subject;
public string Subject { get => subject; set => SetProperty(ref subject, value); }
private bool hard;
public bool Hard { get => hard; set => SetProperty(ref hard, value); }
public ObservableCollection<Items> FilteredData { get; set; }
/* BaseViewModel has implementation of SetProperty */
}
First Page where second page gets Hard and Subject values
private async void ButtonClick(object sender, EventArgs e)
{
var vm = (BaseViewModel)BindingContext;
vm.Hard = HardButtonSelected == Hard;
vm.Subject = vm.Subject.ToLower();
}
First Page viewmodel
public class FirstPageViewModel : BaseViewModel
{
public IAsyncCommand MehetButtonClickedCommand { get; }
readonly IPageService pageService;
readonly IFeladatokStore _feladatokStore;
public FeladatValasztoViewModel()
{
_dataStore = DependencyService.Get<IDataStore>();
ButtonClickedCommand = new AsyncCommand(ButtonClicked);
pageService = DependencyService.Get<IPageService>();
}
private async Task ButtonClicked()
{
await FilterData();
await pageService.PushAsync(new SecondPage());
}
private async Task FilterData()
{
FilteredData.Clear();
var datas = await _dataStore.SearchData(Subject, Hard).ConfigureAwait(false);
foreach (var data in datas)
{
FilteredData.Add(data);
}
}
So basically this gives a null exception error. I also tried giving the ObservableCollection as an argument for SecondPage(ObservableCollection x) and that did work, but because I had to make another ObservableCollection for it and copy from one to another it stopped being async and froze for a couple of seconds. So my question is how can I make this async?
To avoid delay, build the new collection in a private variable. Then set the property to that variable:
// Constructor with parameter
public SomeClass(IList<Items> data)
{
SetFilteredDataCopy(data);
}
public ObservableCollection<Items> FilteredData { get; set; }
private void SetFilteredDataCopy(IList<Items> src)
{
var copy = new ObservableCollection<Items>();
foreach (var item in src)
copy.Add(item);
FilteredData = copy;
//MAYBE OnPropertyChanged(nameof(FilteredData));
}
Inside my Xamarin application I have added a Picker control to a page. The setup of that Picker looks like this in xaml:
<controls:ExtendedPicker x:Name="GenderPicker" Style="{StaticResource PickerControl}" Margin="0,0,0,20"
ItemsSource="{Binding GenderItems}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Gender, Mode=TwoWay}" />
In my ViewModel I have this code to populate the picker with some values:
public class GenderItem
{
public string Name { get; set; }
}
public class RegisterViewModel : BaseViewModel
{
private DateTime _birthdate;
public DateTime Birthdate { get => _birthdate; set => SetProperty(ref _birthdate, value); }
public GenderItem Gender { get => _gender; set => SetProperty(ref _gender, value); }
public List<GenderItem> GenderItems { get; set; } = new List<GenderItem>
{
new GenderItem { Naam = "Male" },
new GenderItem { Naam = "Female" },
new GenderItem { Naam = "Confused" }
};
public RegisterViewModel()
{
// This doesn't work
Gender = new GenderItem { Naam = "Female" };
// This works
Birthdate = DateTime.Today.AddYears(-40);
}
}
From within the constructor I try to set a default selected value for the Picker control. But for some reason the value isn't selected in the Picker.
I also set a datetime property with a value and I do see that this value is picked up by the Date control.
Why isn't this working for the Picker control? What can I do to make this work?
SetProperty method
The SetProperty method lives inside my BaseViewModel class. You get this with every new Xamarin project.
public class BaseViewModel : INotifyPropertyChanged
{
public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>();
bool _isBusy = false;
public bool IsBusy
{
get => _isBusy;
set => SetProperty(ref _isBusy, value);
}
string _title = string.Empty;
public string Title
{
get => _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
}
Gender needs to be an element in GenderItems. Right now you are creating a new object that has the same value as an item in GenderItems, but it is not the same object
I am having difficulty displaying a web page in a webview in xamarin forms application.
I started out with eshoponcontainers sample application. which went well till I got to the login stage. When the Authorization request is sent the login page doesnt show up. A test in postman revealed an unathorised client error so I was expecting to see that page loaded in my webview but that is not happening.
My .xaml page is as follows
<AbsoluteLayout
Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="0"
Grid.RowSpan="2"
IsVisible="{Binding IsLogin}">
<WebView
Source="{Binding LoginUrl}"
AbsoluteLayout.LayoutBounds="0, 0, 1, 1"
AbsoluteLayout.LayoutFlags="All">
<WebView.Behaviors>
<OnPlatform x:TypeArguments="Behavior">
<On Platform="iOS, Android">
<On.Value>
<behaviors:EventToCommandBehavior
EventName="Navigating"
EventArgsConverter="{StaticResource WebNavigatingEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</On.Value>
</On>
<On Platform="UWP">
<On.Value>
<behaviors:EventToCommandBehavior
EventName="Navigated"
EventArgsConverter="{StaticResource WebNavigatedEventArgsConverter}"
Command="{Binding NavigateCommand}" />
</On.Value>
</On>
</OnPlatform>
</WebView.Behaviors>
</WebView>
</AbsoluteLayout>
The code behind looks like this
public partial class LoginView : ContentPage
{
private bool _animate;
public LoginView ()
{
InitializeComponent ();
}
protected override async void OnAppearing()
{
var content = this.Content;
this.Content = null;
this.Content = content;
var vm = BindingContext as LoginViewModel;
if (vm != null)
{
vm.InvalidateMock();
if (!vm.IsMock)
{
_animate = true;
await AnimateIn();
}
}
}
protected override void OnDisappearing()
{
_animate = false;
}
public async Task AnimateIn()
{
if (Device.RuntimePlatform == Device.UWP)
{
return;
}
await AnimateItem(Banner, 10500);
}
private async Task AnimateItem(View uiElement, uint duration)
{
try
{
while (_animate)
{
await uiElement.ScaleTo(1.05, duration, Easing.SinInOut);
await Task.WhenAll(
uiElement.FadeTo(1, duration, Easing.SinInOut),
uiElement.LayoutTo(new Rectangle(new Point(0, 0), new Size(uiElement.Width, uiElement.Height))),
uiElement.FadeTo(.9, duration, Easing.SinInOut),
uiElement.ScaleTo(1.15, duration, Easing.SinInOut)
);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}
I have the following inthe "LoginViewModel"
public class LoginViewModel: ViewModelBase
{
private ValidatableObject<string> _userName;
private ValidatableObject<string> _password;
private bool _isMock;
private bool _isValid;
private bool _isLogin;
private string _authUrl;
private ISettingsService _settingsService;
private IOpenUrlService _openUrlService;
private IIdentityService _identityService;
public LoginViewModel(
ISettingsService settingsService,
IOpenUrlService openUrlService,
IIdentityService identityService)
{
_settingsService = settingsService;
_openUrlService = openUrlService;
_identityService = identityService;
_userName = new ValidatableObject<string>();
_password = new ValidatableObject<string>();
InvalidateMock();
AddValidations();
}
public ValidatableObject<string> UserName
{
get
{
return _userName;
}
set
{
_userName = value;
RaisePropertyChanged(() => UserName);
}
}
public ValidatableObject<string> Password
{
get
{
return _password;
}
set
{
_password = value;
RaisePropertyChanged(() => Password);
}
}
public bool IsMock
{
get
{
return _isMock;
}
set
{
_isMock = value;
RaisePropertyChanged(() => IsMock);
}
}
public bool IsValid
{
get
{
return _isValid;
}
set
{
_isValid = value;
RaisePropertyChanged(() => IsValid);
}
}
public bool IsLogin
{
get
{
return _isLogin;
}
set
{
_isLogin = value;
RaisePropertyChanged(() => IsLogin);
}
}
public string LoginUrl
{
get
{
return _authUrl;
}
set
{
_authUrl = value;
RaisePropertyChanged(() => LoginUrl);
}
}
public ICommand MockSignInCommand => new Command(async () => await MockSignInAsync());
public ICommand SignInCommand => new Command(async () => await SignInAsync());
public ICommand RegisterCommand => new Command(Register);
public ICommand NavigateCommand => new Command<string>(async (url) => await NavigateAsync(url));
public ICommand SettingsCommand => new Command(async () => await SettingsAsync());
public ICommand ValidateUserNameCommand => new Command(() => ValidateUserName());
public ICommand ValidatePasswordCommand => new Command(() => ValidatePassword());
public override Task InitializeAsync(object navigationData)
{
if (navigationData is LogoutParameter)
{
var logoutParameter = (LogoutParameter)navigationData;
if (logoutParameter.Logout)
{
Logout();
}
}
return base.InitializeAsync(navigationData);
}
private async Task MockSignInAsync()
{
IsBusy = true;
IsValid = true;
bool isValid = Validate();
bool isAuthenticated = false;
if (isValid)
{
try
{
await Task.Delay(10);
isAuthenticated = true;
}
catch (Exception ex)
{
Debug.WriteLine($"[SignIn] Error signing in: {ex}");
}
}
else
{
IsValid = false;
}
if (isAuthenticated)
{
_settingsService.AuthAccessToken = GlobalSetting.Instance.AuthToken;
await NavigationService.NavigateToAsync<MainViewModel>();
await NavigationService.RemoveLastFromBackStackAsync();
}
IsBusy = false;
}
private async Task SignInAsync()
{
IsBusy = true;
await Task.Delay(10);
LoginUrl = _identityService.CreateAuthorizationRequest();
IsValid = true;
IsLogin = true;
IsBusy = false;
}
private void Register()
{
_openUrlService.OpenUrl(GlobalSetting.Instance.RegisterWebsite);
}
private void Logout()
{
var authIdToken = _settingsService.AuthIdToken;
var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);
if (!string.IsNullOrEmpty(logoutRequest))
{
// Logout
LoginUrl = logoutRequest;
}
if (_settingsService.UseMocks)
{
_settingsService.AuthAccessToken = string.Empty;
_settingsService.AuthIdToken = string.Empty;
}
_settingsService.UseFakeLocation = false;
}
private async Task NavigateAsync(string url)
{
var unescapedUrl = System.Net.WebUtility.UrlDecode(url);
if (unescapedUrl.Equals(GlobalSetting.Instance.LogoutCallback))
{
_settingsService.AuthAccessToken = string.Empty;
_settingsService.AuthIdToken = string.Empty;
IsLogin = false;
LoginUrl = _identityService.CreateAuthorizationRequest();
}
else if (unescapedUrl.Contains(GlobalSetting.Instance.IdentityCallback))
{
var authResponse = new AuthorizeResponse(url);
if (!string.IsNullOrWhiteSpace(authResponse.Code))
{
var userToken = await _identityService.GetTokenAsync(authResponse.Code);
string accessToken = userToken.AccessToken;
if (!string.IsNullOrWhiteSpace(accessToken))
{
_settingsService.AuthAccessToken = accessToken;
_settingsService.AuthIdToken = authResponse.IdentityToken;
await NavigationService.NavigateToAsync<MainViewModel>();
await NavigationService.RemoveLastFromBackStackAsync();
}
}
}
}
private async Task SettingsAsync()
{
await NavigationService.NavigateToAsync<SettingsViewModel>();
}
private bool Validate()
{
bool isValidUser = ValidateUserName();
bool isValidPassword = ValidatePassword();
return isValidUser && isValidPassword;
}
private bool ValidateUserName()
{
return _userName.Validate();
}
private bool ValidatePassword()
{
return _password.Validate();
}
private void AddValidations()
{
_userName.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A username is required." });
_password.Validations.Add(new IsNotNullOrEmptyRule<string> { ValidationMessage = "A password is required." });
}
public void InvalidateMock()
{
IsMock = _settingsService.UseMocks;
}
}
I have a custom checkbox control that I created with an ICommand property and the corresponding bindable property (my checkbox is a Xamarin.Forms XAML Page), the code is:
CheckBox.xaml
<Image x:Name="imgCheckBox"
WidthRequest="20"
HeightRequest="20"/>
<Label x:Name="lblCheckBox"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
<TapGestureRecognizer Tapped="OnCheckBoxTapped"/>
CheckBox.xaml.cs
public partial class CheckBox : ContentView
{
private static ImageSource uncheckedImage;
private static ImageSource checkedImage;
public CheckBox()
{
InitializeComponent();
uncheckedImage = ImageSource.FromResource("cbUnchecked.png");
checkedImage = ImageSource.FromResource("cbChecked.png");
imgCheckBox.Source = uncheckedImage;
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool>(
checkbox =>
checkbox.IsChecked,
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, newValue);
}
});
public bool IsChecked
{
set { SetValue(IsCheckedProperty, value); }
get { return (bool)GetValue(IsCheckedProperty); }
}
void OnCheckBoxTapped(object sender, EventArgs args)
{
IsChecked = !IsChecked;
if (IsChecked)
{
imgCheckBox.Source = checkedImage;
}
else
{
imgCheckBox.Source = uncheckedImage;
}
}
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
}
This checkbox implementation is on another Page called TermsAndConditionsPage, that is also a a Xamarin.Forms XAML Page, the code of the implementation is:
<toolkit:CheckBox Text="{Binding txtCheckBox}"
FontSize="Small"
CheckBoxCommand="{Binding OnCheckBoxTapChanged}"
IsChecked="{Binding IsCheckedChanged, Mode=TwoWay}"/>
<Button Text="Next"
Command="{Binding Next_OnClick}"
IsEnabled="{Binding Next_IsEnabled}"
HorizontalOptions="CenterAndExpand"
Clicked="OnNextClicked"/>
The Code Behind of this page is empty (Constructur with InitializeComponent()).
I also have the ViewModel of this page with this code:
TermsAndConditionsViewModel.cs
private string _txtCheckBox;
public string txtCheckBox
{ get { return _txtCheckBox; }
set
{
_txtCheckBox = value;
OnPropertyChanged("txtCheckBox");
}
}
private bool _Next_IsEnabled;
public bool Next_IsEnabled
{
get { return _Next_IsEnabled; }
set
{
_Next_IsEnabled = value;
OnPropertyChanged("Next_IsEnabled");
}
}
private bool _IsCheckedChanged;
public bool IsCheckedChanged
{
get { return _IsCheckedChanged; }
set
{
_IsCheckedChanged = value;
OnPropertyChanged("IsCheckedChanged");
}
}
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
txtCheckBox = "I agree with the terms and conditions";
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{ Next_IsEnabled = true; }
else
{ Next_IsEnabled = false; }
}
private void NextClicked()
{ App.Current.MainPage = new Views.HelloWorld(); }
#region INPC
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Now, the question time: the problem I'm having is the CheckBoxTapped Command is not working, I mean, it doesn't do anything, although the checkbox image changes every time I touch it, it does not change the Next_IsEnabled property of my button. I'd like to know what I am missing here to make this command work properly.
EDIT
What I'm looking for is a Command that behaves similarly to the one that Buttons have.
Thanks all for your time!
Since the original answer is now obsolete, here is the new method:
using System.Windows.Input;
public partial class MyControlExample : ContentView
{
// BindableProperty implementation
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(MyControlExample), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Helper method for invoking commands safely
public static void Execute(ICommand command)
{
if (command == null) return;
if (command.CanExecute(null))
{
command.Execute(null);
}
}
public MyControlExample()
{
InitializeComponent();
}
// this is the command that gets bound by the control in the view
// (ie. a Button, TapRecognizer, or MR.Gestures)
public Command OnTap => new Command(() => Execute(Command));
}
Something like that (pseudocode):
public class YourClassName : View
{
public YourClassName()
{
var gestureRecognizer = new TapGestureRecognizer();
gestureRecognizer.Tapped += (s, e) => {
if (Command != null && Command.CanExecute(null)) {
Command.Execute(null);
}
};
var label = new Label();
label.GestureRecognizers.Add(gestureRecognizer);
}
public static readonly BindableProperty CommandProperty =
BindableProperty.Create<YourClassName, ICommand>(x => x.Command, null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
I have MainPageViewModel with Items (ObservableCollection). On this page I also have a button, that add new items to Items.
public class MainPageViewModel : Screen {
private DateTime StartActivity = DateTime.MinValue;
public ObservableCollection<ActivityViewModel> Items { get; set; }
public MainPageViewModel(INavigationService navigationService) {
this.Items = new ObservableCollection<ActivityViewModel>();
}
public void AddActivity(string activityName) {
if (this.Items.Count == 0) {
this.Items.Add(new ActivityViewModel() {
Activity = activityName,
Duration = 0
});
StartActivity = DateTime.Now;
}
else {
this.Items[this.Items.Count - 1].Duration = 10;
this.Items.Add(new ActivityViewModel() {
Activity = activityName,
Duration = 0
});
StartActivity = DateTime.Now;
}
}
}
Adding new items works perfect.
But data from Items not recovers when app activates after tombstoning. Try create StorageHandler for my ViewModel. Doesn't help. What I'm doing wrong?
public class MainPageViewModelStorage : StorageHandler<MainPageViewModel> {
public override void Configure() {
Property(x => x.Items)
.InAppSettings()
.RestoreAfterActivation();
}
}
Also try add [SurviveTombstone] for class and for property but Visual Studio don't know that attribute.
public class ActivityViewModel : PropertyChangedBase {
private string _activity;
public string Activity {
get {
return _activity;
}
set {
if (value != _activity) {
_activity = value;
NotifyOfPropertyChange(() => Activity);
}
}
}
private double _duration;
public double Duration {
get {
return _duration;
}
set {
if (value != _duration) {
_duration = value;
NotifyOfPropertyChange(() => Duration);
}
}
}
}
You should store not InAppSettings but InPhoneState.
Check with breakpoint if method Configure is called. If not - something wrong with your bootstrapper. Probably PhoneContainer.RegisterPhoneServices() is missing
Turn on catching first chance exception in Visual Studio (Ctrl+Alt+E, and put checkbox against CLR Exceptions). Probably your view model cannot be deserialized properly.