how to pass multiple labels through ContentPage - xamarin.forms

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];
}
}

Related

Get the value in entry field then reflect/react to a label in xamarin form

My problem is idk how to reflect in a label depending of input value in a entry field by a customer.
To make the things clear, let's start in our database.
Our database
Few information about our realtime database.
In our DELIVERY TABLE, we have 3 types of delivery(standard, reservation and express). In express, by the word itself, it's a rush delivery and we will require a DELIVERY FEE from the customer.
Another table is PRODUCT. We have 2 product for now, MINERAL(PROD1) AND SPARKLING(PROD2). The price of PROD1 is 35 ana PROD2 is 40.
What I've try right now is I put an SelectedIndexChanged in my picker delivery type and picker product type.
//This is my deliverytype event
private async void Picker_DeliveryType_SelectedIndexChanged(object sender, EventArgs e)
{
DELIVERY deliverySave = Picker_DeliveryType.SelectedItem as DELIVERY;
var selectedDeliveryItem = deliverySave.deliveryType;
var note = deliverySave.deliveryFee;
if(selectedDeliveryItem == "Express")
{
await DisplayAlert("Note", "Estimated Delivery: 2 hours from now", "OK");
labelDeliveryFee.Text = "Delivery Fee:" + note;
entryfieldReservationDate.IsEnabled = false;
}
else if(selectedDeliveryItem == "Standard")
{
await DisplayAlert("Note", "Within the day", "OK");
entryfieldReservationDate.IsEnabled = true;
}
else
{
await DisplayAlert("Note", "Enter Reservation Date", "OK");
entryfieldReservationDate.IsEnabled = true;
}
}
//This is my product type event
private void Picker_ProductType_SelectedIndexChanged(object sender, EventArgs e)
{
PRODUCT prod = Picker_ProductType.SelectedItem as PRODUCT;
var selectedProductItem = prod.productType;
var productPricing = prod.productPrice;
if (selectedProductItem == "Mineral")
{
labelProductPrice.Text = Convert.ToString(productPricing);
}
else
{
labelProductPrice.Text = Convert.ToString(productPricing);
}
}
AND my expected output is I want the 2 SelectedIndexChanged will put inside my order button.
//this is my order button click functio now
async private void Button_Clicked(object sender, EventArgs e)
{
if (selectedDeliveryType == "Standard")
{
if (selectedProductItem == "Mineral")
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
else
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
}
else if (selectedDeliveryType == "Reservation")
{
if (selectedProductItem == "Mineral")
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
else
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
}
else
{
int deliveryfee = deliverySave.deliveryFee;
if (selectedProductItem == "Mineral")
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
else
{
//some code here
waterOrder.orderTotalAmount = totalprice;
}
}
//some code here
var SaveData = await waterorderRepos.Save(waterOrder);
var SaveDataToCustomerNotification = await waterorderRepos.SaveCustomerNotification(customerNotification);
if (SaveData)
{
await this.DisplayAlert("Order", "Order successfully", "OK");
ClearData();
CloseAllPopup();
return;
}
else
{
await this.DisplayAlert("Order", "We cannot process your order at the moment.", "OK");
}
}
I will show you some visual presentation between my work now and my expected output.
This is the image.
Please help me guys, idk how to it.Also, no MVVM please cause IDK how to do it. Thank you so much.
Based on the complexity of your code, I recommend that you use the MVVM pattern for implementation.
I created a demo and achieved your function.
You can refer to the following code:
1.create a view model MyViewModel.cs
public class MyViewModel: INotifyPropertyChanged
{
public ObservableCollection<Delivery> Deliveries { get; set; }
private Delivery _deliverySelectedItem;
public Delivery DeliverySelectedItem
{
get => _deliverySelectedItem;
set {
SetProperty(ref _deliverySelectedItem, value);
// update the TotalAmount
caculateTotalAmount();
}
}
public ObservableCollection<Product> Products { get; set; }
//add SelectedItem here
private Product _productSelectedItem;
public Product ProductSelectedItem
{
get => _productSelectedItem;
set {
SetProperty(ref _productSelectedItem, value);
// update the TotalAmount
caculateTotalAmount();
}
}
private int _quantity;
public int Quantity
{
get => _quantity;
set
{
SetProperty(ref _quantity, value);
// update the TotalAmount
caculateTotalAmount();
}
}
private int _totalAmount;
public int TotalAmount
{
get => _totalAmount;
set
{
SetProperty(ref _totalAmount, value);
}
}
private void caculateTotalAmount() {
if (String.IsNullOrEmpty(Quantity.ToString() ) || Quantity == 0) {
TotalAmount = 0;
return;
}
if (ProductSelectedItem!=null && DeliverySelectedItem!=null) {
TotalAmount = ProductSelectedItem.productPrice * Quantity + DeliverySelectedItem.deliveryFee;
}
}
public MyViewModel() {
Products = new ObservableCollection<Product>();
Products.Add(new Product { ProductId = 01, productType = "Products", productPrice = 10 });
Products.Add(new Product { ProductId = 02, productType = "02", productPrice = 12 });
Products.Add(new Product { ProductId = 03, productType = "Products", productPrice = 13 });
Products.Add(new Product { ProductId = 04, productType = "Products", productPrice = 15 });
Deliveries = new ObservableCollection<Delivery>();
Deliveries.Add(new Delivery { deliveryFee = 10, deliveryType = "Express" });
Deliveries.Add(new Delivery { deliveryFee = 20, deliveryType = "Standard" });
Deliveries.Add(new Delivery { deliveryFee = 30, deliveryType = "Standard" });
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
2.create class Delivery.cs and Product.cs
public class Delivery
{
public string deliveryType { get; set; }
public int deliveryFee { get; set;}
}
public class Product
{
public int ProductId { get; set; }
public string productType { get; set; }
public int productPrice { get; set; }
}
3.MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pickerapp2023112="clr-namespace:PickerApp2023112"
x:Class="PickerApp2023112.MainPage">
<ContentPage.BindingContext>
<pickerapp2023112:MyViewModel></pickerapp2023112:MyViewModel>
</ContentPage.BindingContext>
<StackLayout>
<Picker x:Name="Picker_DeliveryType" ItemsSource="{Binding Deliveries}" ItemDisplayBinding="{Binding deliveryFee}" SelectedItem="{Binding DeliverySelectedItem}"
Title="Select a delivery type"
TitleColor="Red">
</Picker>
<Picker x:Name="Picker_ProductType" ItemsSource="{Binding Products}" ItemDisplayBinding="{Binding productPrice}" SelectedItem="{Binding ProductSelectedItem}"
Title="Select a product type"
TitleColor="Red">
</Picker>
<StackLayout Orientation="Horizontal">
<Label Text="Please input quantity: " BackgroundColor="CadetBlue"/>
<Entry Placeholder="0" Text="{Binding Quantity}" TextColor="Red" HorizontalOptions="FillAndExpand"></Entry>
</StackLayout>
<Label x:Name="labelDeliveryFee" Text="{Binding DeliverySelectedItem.deliveryFee,StringFormat='The delivery free is {0:F1}'}" HorizontalOptions="StartAndExpand" BackgroundColor="Yellow"></Label>
<Label x:Name="labelProductPrice" Text="{Binding ProductSelectedItem.productPrice,StringFormat='The product price is {0:F2}'}" HorizontalOptions="StartAndExpand" BackgroundColor="Yellow"></Label>
<StackLayout Orientation="Horizontal">
<Label Text="The total amount: " BackgroundColor="CadetBlue"/>
<Entry Placeholder="0" Text="{Binding TotalAmount}" TextColor="Red" HorizontalOptions="FillAndExpand"></Entry>
</StackLayout>
</StackLayout>
</ContentPage>
Note:
1.I add two objects for the SelectedItem property of two Pickers and implement interface INotifyPropertyChanged for this ViewModel, if we change the value of the property, the UI will update automatically. The same is true for other properties.
private Delivery _deliverySelectedItem;
public Delivery DeliverySelectedItem
{
get => _deliverySelectedItem;
set {
SetProperty(ref _deliverySelectedItem, value);
}
}
public ObservableCollection<Product> Products { get; set; }
//add SelectedItem here
private Product _productSelectedItem;
public Product ProductSelectedItem
{
get => _productSelectedItem;
set {
SetProperty(ref _productSelectedItem, value);
}
}
In this condition, we don't need add event SelectedIndexChanged for Picker.

Avalonia Datagrid cell value does not update when model property is updated from another column

I have a Datagrid with two columns that bind to the same property
<DataGrid
Margin="10"
BorderBrush="Black"
BorderThickness="1"
Grid.Row="1"
Grid.ColumnSpan="3"
Items="{Binding Logs}"
AutoGenerateColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn
Header="Temp Date"
Binding="{Binding Date,Mode=TwoWay}"
>
</DataGridTextColumn>
<DataGridTemplateColumn
Header="Calendar Column"
CellTemplate="{Binding TestTemplate}"
CellEditingTemplate="{Binding EditingTemplate}"
>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I have also created a cell template in the view model as such:
// Cell Data Template
TestTemplate = new FuncDataTemplate<EntryLog>((value, namescope) =>
new TextBlock
{
[!TextBlock.TextProperty] = new Binding("Date",BindingMode.TwoWay),
});
// Cell Editing Data Template
EditingTemplate = new FuncDataTemplate<EntryLog>((value, namescope) =>
{
var grid = new Grid();
var tb = new TextBlock
{
[!TextBlock.TextProperty] = new Binding("Date", BindingMode.TwoWay),
};
grid.Children.Add(tb);
var calendar = new Calendar();
calendar.DisplayDate = value.Date;
calendar.SelectedDate = value.Date;
Popup popup = new Popup();
popup.Child = calendar;
popup.IsOpen = true;
calendar.SelectedDatesChanged += (s, e) =>
{
value.Date = calendar.SelectedDate.Value.Date;
//tb.Text = value.Date.ToString();
};
grid.Children.Add(popup);
return grid;
});
In the UI, it looks like this when editing:
My issue is, whenever I update one column, the other column does not get updated. The itemsource is a Observable Collection of my model
My model:
public class EntryLog : INotifyPropertyChanged
{
private DateTime _date;
public DateTime Date
{
get => _date;
set
{
if (_date != value)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Date)));
_date = value;
}
}
public string Description { get; set; }
public double Hours { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
}
Was wondering if anyone can help me out? Maybe I'm missing something that I just can't identify.
Silly me, I was invoking the propertychanged event before the change of the actual value. The correct way is just to swap that:
public class EntryLog : INotifyPropertyChanged
{
private DateTime _date;
public DateTime Date
{
get => _date;
set
{
if (_date != value)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Date)));
_date = value;
}
}
}
public string Description { get; set; }
public double Hours { get; set; }
public event PropertyChangedEventHandler? PropertyChanged;
}

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 = "";
}
}

Setting list in 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"
},
};

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.

Resources