Setting list in Xamarin.Forms - xamarin.forms

I am working to build an app which will use the code behind to supply new text via bindings when you click the next button. Each time I set the page to load on the virtual phone it times out the hot reload... The worst part is that I am not getting any errors either. Any ideas?
Content Page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="List.MainPage">
<ContentPage.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
<Label Text="{Binding TitleText}" />
<ScrollView VerticalOptions="FillAndExpand">
<StackLayout>
<Label Text="{Binding EngText}" />
<Label Text="{Binding ItText}" />
</StackLayout>
</ScrollView>
<Button Text="Next Page" Clicked="OnNavigateButtonClicked" />
</StackLayout>
</ContentPage.Content>
Code Behind:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace List
{
public partial class MainPage : ContentPage
{
List<MainPage> Contacts { get; set; }
public string TitleText { get; set; }
public string EngText { get; set; }
public string ItText { get; set; }
int ndx = 0;
public MainPage()
{
InitializeComponent();
Contacts = new List<MainPage>
{
// repeat this for as many contacts as you need
new MainPage
{
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"
},
new MainPage
{
TitleText = "Title2",
EngText = "EngText2",
ItText = "ItText2"
},
};
// display the first contact
BindingContext = Contacts[ndx];
}
void OnNavigateButtonClicked(object sender, EventArgs e)
{
// increment your index
ndx++;
// check that we haven't gone too far
if (ndx < Contacts.Count)
{
BindingContext = Contacts[ndx];
}
}
}
}

you are using the same class MainPage for your UI and your data. The constructor of MainPage creates 2 new instances of `MainPage, each of which call their constructor and create 2 more instances, which recurses forever until you use all the memory and crash
you need two different classes, one for your UI and one for your data
public class Data
{
public string TitleText { get; set; }
public string EngText { get; set; }
public string ItText { get; set; }
}
public partial class MainPage : ContentPage
{
List<Data> Contacts { get; set; }
int ndx = 0;
public MainPage()
{
InitializeComponent();
Contacts = new List<Data>
{
// repeat this for as many contacts as you need
new Data
{
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"
},
new Data
{
TitleText = "Title2",
EngText = "EngText2",
ItText = "ItText2"
},
};

Related

Clear entry text from ViewModel using RelayCommand

I would like to clear entry text from my ViewModel which is binded there. In the code below I tried it by using a RelayCommand, but it doesn't work.
What i want to accomplish: When clicking button named AddQuestionToQuiz, a function is executed by using Command on the button. The function OnCreateQuizClick(), located in my ViewModel, is triggerd and this function needs to clear my entry text, which i don't get for the moment.
I also tried to use a regular Command instead of using a RelayCommand, but also here it doesn't want to work.
EDIT: UNDERNEATH CODE WORKS FINE - GOT UPDATED
Code is used to clear entry text when clicking on a button from your ViewModel, implementing INotifyPropertyChanged Interface
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}
EDIT: VIEWMODEL UPDATED
.xaml - code
<Button x:Name="AddQuestionToQuiz" WidthRequest="200" Command="{Binding CreateQuizCommand}" Style="{StaticResource ButtonStyle}" Text="Add question to quiz"></Button>
ViewModel - code
internal class CreateQuizPageViewModel : INotifyPropertyChanged
{
// Quiz Name Input
public String QuizNameInput { get; set; }
private String quizQuestionInput = "";
public String QuizQuestionInput
{
get { return quizQuestionInput; }
set { quizQuestionInput = value; OnPropertyChanged(); }
}
public RelayCommand CreateQuizCommand { get; set; }
public CreateQuizPageViewModel()
{
CreateQuizCommand = new RelayCommand(OnCreateQuizClick);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnCreateQuizClick()
{
QuizQuestionInput = "";
}
}

how to pass multiple labels through ContentPage

I am looking for help setting the code behind to pass multiple Labels using the next button. Basically I want to have a label set when the page is opened, press the next button and have a new label replace the current one (without setting new content pages). I a beginner working in Xamarin.Forms and I am not really understanding the data binding process... If anyone has a good reference (other than the Microsoft Website) that would help as well. Pretty sure the code below will not do anything yet... Thanks in advance :)
this is the ContentPage:
<ContentPage.Content>
<StackLayout>
<Label Text="{Binding TitleText}" />
<ScrollView VerticalOptions="FillAndExpand">
<StackLayout>
<Label Text="{Binding EngText}" />
<Label Text="{Binding ItText}" />
</StackLayout>
</ScrollView>
This is what I started for the code behind:
''''''
namespace MVVM2
{
public partial class MainPage : ContentPage
{
List<MainPage> Contacts { get; set; }
int ndx = 0;
public string TitleText { get; set; }
public string EngText { get; set; }
public string ItText { get; set; }
public MainPage()
{
InitializeComponent();
Contacts = new List<MainPage>();
// repeat this for as many contacts as you need
Contacts.Add(new MainPage
{
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"
});
Contacts.Add(new MainPage
{
TitleText = "Title2",
EngText = "EngText2",
ItText = "ItText2"
});
Contacts.Add(new MainPage
{
TitleText = "Title3",
EngText = "EngText3",
ItText = "ItText3"
});
// display the first contact
BindingContext = Contacts[ndx];
}
private void OnNavigateButtonClicked(object sender, EventArgs e)
{
// increment your index
ndx++;
// check that we haven't gone too far
if (ndx < Contacts.Count)
{
BindingContext = Contacts[ndx];
}
}
}
}
if you just want to display different text when the button is clicked, you don't need to navigate to a new page
first, create a List to hold your buttons and a variable to keep track of which one is displayed. These two lines should be in the body of your class but NOT inside any specific method
List<Contact> contacts { get; set; }
int ndx = 0;
then in your constructor setup your data
public MainPage()
{
InitializeComponent();
contacts = new List<Contact>();
// repeat this for as many contacts as you need
contacts.Add(new Contact {
TitleText = "Title1",
EngText = "EngText1",
ItText = "ItText1"});
// display the first contact
BindingContext = contacts[ndx];
}
finally, handle the button click
async void OnNavigateButtonClicked(object sender, EventArgs e)
{
// increment your index
ndx++;
// check that we haven't gone too far
if (ndx < contacts.Count) {
BindingContext = contacts[ndx];
}
}

Media Plugin not displaying an image after capture MVVM

I am using Media Plugin and everything worked fine until i have decided to move my logic to ViewModel.
This is my Xaml
<Frame BackgroundColor="LightGray" HasShadow="True">
<Image
x:Name="Photo"
Grid.Row="2"
HeightRequest="100"
Source="{Binding postViewModel.SelectedPhoto}"
VerticalOptions="Start"/>
</Frame>
My Binding to MasterViewModel
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
My Master
class MasterPostsViewModel : BaseViewModel
{
public PostViewModel postViewModel { get; set; }
public CategoriesViewModel categoriesViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
categoriesViewModel = new CategoriesViewModel();
postViewModel = new PostViewModel(navigation);
}
}
Taking Picture in View Model
private MediaFile _selectedPhoto;
public MediaFile SelectedPhoto { get => _selectedPhoto; set => SetValue(ref
_selectedPhoto, value); }
private async Task TakePicture()
{
await Permission();
var imageSource = await DependencyService.Get<IMessage>().ShowActionSheet(AppResources.AlertPhoto, AppResources.AlertNewPhoto, AppResources.AlertGallery);
if (imageSource == AppResources.AlertNewPhoto)
{
var imageFileName = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions()
{
Name = $"{DateTime.UtcNow}.jpg",
DefaultCamera = Plugin.Media.Abstractions.CameraDevice.Rear,
PhotoSize = PhotoSize.Medium,
SaveToAlbum = true
});
if (imageFileName == null) return;
else
{
SelectedPhoto = imageFileName;
}
}
}
I can see tthe adress of the picture however the picture doesnt display on my xaml. I have tried to follow this
Bind Plugin.Media fromViewModel
But still didnt work. Please some suggestion on what am i doing wrong
I use you code and write a demo with binding a string, it works well. You can have a look at the code and may get some idea from it.
Code in xaml:
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding postViewModel.SelectedPhoto}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Button Text="click me" Command ="{Binding postViewModel.NewCommand}"/>
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
MasterPostsViewModel ViewModel;
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = ViewModel = new MasterPostsViewModel(Navigation);
}
}
class MasterPostsViewModel
{
public PostViewModel postViewModel { get; set; }
public MasterPostsViewModel(INavigation navigation)
{
postViewModel = new PostViewModel();
}
}
class PostViewModel : INotifyPropertyChanged
{
string _selectedPhoto;
public ICommand NewCommand { private set; get; }
public event PropertyChangedEventHandler PropertyChanged;
public PostViewModel()
{
SelectedPhoto = "default text";
NewCommand = new Command(TakePicture);
}
private void TakePicture()
{
SelectedPhoto = "test text After click button";
}
public string SelectedPhoto
{
set
{
if (_selectedPhoto != value)
{
_selectedPhoto = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
}
get
{
return _selectedPhoto;
}
}
}
Sample project has been uploaded here.

Xamarin forms - Pass argument to the bindingcontext viewmodel specified in a xaml file

I have a xaml file with with an entry. I am binding the specific entry to a specific viewmodel. But the viewmodel expects a Navigator. How can I pass the navigator from the xaml file to the viewmodel?
<Entry Text="{Binding Signature, Mode=TwoWay}">
<Entry.BindingContext>
<vm:SignaturePopupViewModel>
// I need to pass a navigator..
</vm:SignaturePopupViewModel>
</Entry.BindingContext>
</Entry>
The viewmodel expects a navigation object. I use it to pop the page to go back to the previous page after running some code logic.
public SignaturePopupViewModel(INavigation navigation = null)
{
Navigation = navigation;
SendSignatureCommand = new Command(async () =>
{
await SendSignature();
await Navigation.PopAsync();
});
}
You do not need to use INavigation navigation in your SignaturePopupViewModel in your constructor to achieve the Navigation.
Just use a simple way is
await Application.Current.MainPage.Navigation.PopModalAsync(); Or
await Application.Current.MainPage.Navigation.PopAsync()
like following code.
public class SignaturePopupViewModel
{
public ICommand SendSignatureCommand { protected set; get; }
public SignaturePopupViewModel( )
{
SendSignatureCommand = new Command(async () =>
{
await SendSignature();
// if you use the MainPage = new NavigationPage( new MainPage()); in
//App.xaml.cs use following code.
await Application.Current.MainPage.Navigation.PopAsync();
// if not, just use await Application.Current.MainPage.Navigation.PopModalAsync();
});
}
}
Could you create an instance of the SignaturePopupVM in the ViewModel of that page and then bind the Text to that property instead?
VM:
SignaturePopupViewModel SignaturePopupVMInstance { get; private set; }
public ParentVM()//Constructor
{
SignaturePopupVMInstance = new SignaturePopupViewModel(new Navigator());
}
Xaml:
<Entry Text="{Binding SignaturePopupVMInstance.Signature, Mode=TwoWay}"/>
Edit:
public class TabPageVM{
public ChildVM TheVMForTabOne { get; set; }
public AnotherChildVM TheVMForTabTwo { get; set; }
public TabVM TheVMForTabThree { get; set; }
public TabPageVM(){
TheVMForTabOne = new ChildVM(/*parameters*/);
TheVMForTabTwo = new AnotherChildVM(/*parameters*/);
TheVMForTabThree = new TabVM(/*parameters*/);
}
}
Xaml for tabpage:
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Views="clr-namespace:App.ViewsForMyTabs"
x:Class="App.TabPageView"
BarBackgroundColor="#EEEEEE"
BarTextColor="Black"
BindingContext="{Binding TheTabbedPageVMInstance}">
<TabbedPage.Children>
<Views:TheViewForTabOne x:Name="TabOneView"
BindingContext="{Binding TheVMForTabOne}"/>
<Views:TheViewForTabTwo x:Name="TabTwoView"
BindingContext="{Binding TheVMforTabTwo}"/>
<Views:TheViewForTabThree x:Name="TabThreeView"
BindingContext="{Binding TheVMforTabThree}"/>
</TabbedPage.Children>
</TabbedPage>
Lets say TheViewForTabOne has the button on it that takes you to the new page. The VM for that view "TheVMForTabOne" would have something like this:
public class ChildVM{
public SignaturePopupViewModel SignaturePopupVMInstance { get; set; }
public Command NavigateToNewPageWithEntry { get; private set; }
public ChildVM(){
SignaturePopupVMInstance = new SignaturePopupViewModel(/*parameters*/);
NavigateToNewPageWithEntry = new Command(() =>{
//Navigate to new page with SignaturePopupVMInstance as the BindingContext
}
}
}
TheViewForTabOne
...
<Label Text="{Binding SignaturePopupVMInstance.Signature}"/>
<Button Command="{Binding NavigateToNewPageWithEntry}"/>
...

xamarin prism forms property changed not firing

I have a problem with prism.forms and propertychanged.
I have settingsmodel,settingsviewmodel and settingspage that shown code below,
SettingsModel
public class SettingsModel: BindableBase
{
public string Url { get; set; }
public bool IsEnabled{get;set;}
public string ApiUrl { get; set; }
public SettingsModel()
{
this.Url = string.Empty;
this.IsEnabled = false;
this.ApiUrl = string.Empty;
}
}
SettingsViewModel
[ImplementPropertyChanged]
public class SettingsPageViewModel : ViewModelBase
{
readonly INavigationService _navigationService;
readonly IUserDialogs _userDialogs;
readonly IProductService _productService;
#region Constructor
public SettingsPageViewModel(INavigationService navigationService,
IUserDialogs userDialogs, IProductService productService)
{
_navigationService = navigationService;
_userDialogs = userDialogs;
_productService = productService;
this.Settings = new SettingsModel();
SaveCommand = new DelegateCommand(Save).ObservesCanExecute((vm) => Settings.IsEnabled);
}
#endregion
#region Model
SettingsModel _settingsModel;
public SettingsModel Settings
{
get { return _settingsModel; }
set {
if (_settingsModel != null)
_settingsModel.PropertyChanged -= MyPersonOnPropertyChanged;
SetProperty(ref _settingsModel, value);
if (_settingsModel != null)
_settingsModel.PropertyChanged += MyPersonOnPropertyChanged;
Validation();
}
}
public bool IsLoading { get; set; }
public DelegateCommand SaveCommand { get; set; }
#endregion
void MyPersonOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
Validation();
}
#region Methods
async void Save()
{
var result = await _productService.GetProducts(Priority.UserInitiated,"","","");
}
void Validation()
{
Settings.IsEnabled = !string.IsNullOrEmpty(Settings.ApiUrl) ? true : false;
}
#endregion
}
And SettingsPage XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="XXX.Warehouse.SettingsPage">
<Entry Text="{Binding Settings.ApiUrl}" Margin="0,5,0,5"
Placeholder="www.example.com" HorizontalOptions="FillAndExpand" />
<Button Text="Save"
FontSize="16"
BorderRadius="5"
TextColor="White"
BackgroundColor ="#578A17"
IsEnabled="{Binding Settings.IsEnabled}"
Command="{Binding SaveCommand}" />
I want to do when user enter url than IsEnabled property will true, when Url is empty than IsEnabled property will false and save button if IsEnabled is false, button not enabled.
My main problem is, i write Entry url but propertychanged event not fired?
How can i solve this?
Thank you.

Resources