net maui form validation - c#

Still learning.
I am trying to do a model validation using CommunityToolkit.Mvvm and I stuck in the styles (min there).
Here is what i tried:
Model:
public abstract partial class BaseModel : ObservableObject, IDataErrorInfo
{
private Dictionary<string, string> validationErrors = new Dictionary<string, string>();
private bool isValid = false;
protected Dictionary<string, string> ValidationErrors
{
get => validationErrors;
private set => SetProperty(ref validationErrors, value);
}
protected bool IsValid
{
get => isValid;
private set => SetProperty(ref isValid, value);
}
[RelayCommand]
protected void Validate()
{
var context = new ValidationContext(this);
var errors = new List<ValidationResult>();
isValid = Validator.TryValidateObject(this, context, errors, true);
foreach (var error in errors)
{
var columnName = error.MemberNames.First();
if(!validationErrors.ContainsKey(columnName))
validationErrors.Add(columnName, error.ErrorMessage);
}
}
// check for general model error
public string Error { get; private set; } = null;
// check for property errors
public string this[string columnName]
{
get
{
Validate();
return validationErrors[columnName];
}
}
}
public class PlayerModel : BaseModel
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[StringLength(4096)]
public string LocalImageLink { get; set; }
[Required]
[StringLength(4096)]
public string WebImageLink { get; set; }
[Required]
[StringLength(255)]
public string Club { get; set; }
[Required]
[StringLength(32)]
public string Birthday { get; set; }
[Required]
[StringLength(255)]
public string BirthPlace { get; set; }
[Required]
[Range(0, 100)]
public int Weight { get; set; }
[Required]
[Range(0, 2.5)]
public double Height { get; set; }
[Required]
public string Description { get; set; }
public string PositionName { get; set; }
[Required]
[Range(1, 7)]
public int PositionId { get; set; }
public PlayerModel()
{
}
public PlayerModel(int id, string name, string localImageLink, string webImageLink, string club, string birthday, string birthPlace, int weight, double height, string description, string positionName, int positionId)
{
Id = id;
Name = name;
LocalImageLink = localImageLink;
WebImageLink = webImageLink;
Club = club;
Birthday = birthday;
BirthPlace = birthPlace;
Weight = weight;
Height = height;
Description = description;
PositionName = positionName;
PositionId = positionId;
}
public PlayerModel(int id, string name, string localImageLink, string webImageLink, string club, string birthday, string birthPlace, int weight, double height, string description, PositionModel position)
{
Id = id;
Name = name;
LocalImageLink = localImageLink;
WebImageLink = webImageLink;
Club = club;
Birthday = birthday;
BirthPlace = birthPlace;
Weight = weight;
Height = height;
Description = description;
PositionName = position.Name;
PositionId = position.Id;
}
public PlayerModel(PlayerEntity player)
{
Id = player.Id;
Name = player.Name;
LocalImageLink = player.LocalImageLink;
WebImageLink = player.WebImageLink;
Club = player.Club;
Birthday = player.Birthday;
BirthPlace = player.BirthPlace;
Weight = player.Weight;
Height = player.Height;
Description = player.Description;
PositionName = player.Position.Name;
PositionId = player.Position.Id;
}
public PlayerEntity ToEntity()
{
return new PlayerEntity
{
Id = Id,
Name = Name,
LocalImageLink = LocalImageLink,
WebImageLink = WebImageLink,
Club = Club,
Birthday = Birthday,
BirthPlace = BirthPlace,
Weight = Weight,
Height = Height,
Description = Description,
Position = new PositionEntity
{
Id = PositionId,
Name = PositionName
}
};
}
public void ToEntity(PlayerEntity player)
{
player.Id = Id;
player.Name = Name;
player.LocalImageLink = LocalImageLink;
player.WebImageLink = WebImageLink;
player.Club = Club;
player.Birthday = Birthday;
player.BirthPlace = BirthPlace;
player.Weight = Weight;
player.Height = Height;
player.Description = Description;
player.Position.Id = PositionId;
player.Position.Name = PositionName;
}
}
View:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiUI.Pages.AddOrUpdatePlayer"
xmlns:local="clr-namespace:Backend.Models;assembly=Backend.Models"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">
<ContentPage.BindingContext>
<local:PlayerModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem IconImageSource="save.svg" Clicked="OnSaveClick"/>
</ContentPage.ToolbarItems>
<ScrollView Margin="10">
<VerticalStackLayout>
<VerticalStackLayout>
<Label Text="Name" />
<Entry x:Name="name" Text="{Binding Name, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing">
<Entry.Behaviors>
<toolkit:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding Validate}" />
</Entry.Behaviors>
</Entry>
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Position" />
<Picker x:Name="position" Title="Select..."
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding PositionId, Mode=TwoWay}" />
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Club" />
<Entry x:Name="club" Text="{Binding Club, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing" />
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Birthday" />
<DatePicker x:Name="birthday" Date="{Binding Birthday, Mode=TwoWay}" />
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Birth place" />
<Entry x:Name="birthplace" Text="{Binding BirthPlace, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing" />
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Weight" />
<Entry x:Name="weight" Text="{Binding Weight, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing" Keyboard="Numeric"/>
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Height" />
<Entry x:Name="height" Text="{Binding Height, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing" Keyboard="Numeric"/>
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Image link" />
<Entry x:Name="webImageLink" Text="{Binding WebImageLink, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing"/>
</VerticalStackLayout>
<VerticalStackLayout Margin="0,10">
<Label Text="Description" />
<Editor x:Name="description" Text="{Binding Description, Mode=TwoWay}"
AutoSize="TextChanges"/>
</VerticalStackLayout>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
View code behind:
[QueryProperty(nameof(Player), "player")]
public partial class AddOrUpdatePlayer : ContentPage
{
private PlayerModel player;
public PlayerModel Player
{
get => player;
set
{
player = value;
OnPropertyChanged();
}
}
private readonly IPositionClient positionClient;
private readonly IPlayerClient playerClient;
private delegate Task Action();
private Action asyncAction;
public AddOrUpdatePlayer(IPositionClient positionClient, IPlayerClient playerClient)
{
InitializeComponent();
SetUpControls();
SetTitle();
SetActionPointer();
this.positionClient = positionClient;
this.playerClient = playerClient;
}
protected async override void OnAppearing()
{
BindingContext = player;
await SetUpPositionPicker();
}
private void SetUpControls()
{
birthday.MinimumDate = new DateTime(1900, 1, 1);
birthday.MaximumDate = DateTime.Now.Date;
}
private async Task SetUpPositionPicker()
{
position.ItemsSource = await this.positionClient.GetAllAsync();
}
private void SetTitle()
{
Title = this.player is null ?
"Add new player" :
$"Update {player.Name}";
}
private void SetActionPointer()
{
asyncAction = this.player is null ?
AddNewPlayer :
UpdatePlayer;
}
private async Task AddNewPlayer()
{
var player = BindingContext as PlayerModel;
await playerClient.CreateAsync(player);
await Shell.Current.GoToAsync(PageRoutes.HomePage, true);
}
private async Task UpdatePlayer()
{ }
private async void OnSaveClick(object sender, EventArgs e)
{
await asyncAction();
}
}
Global style:
<?xml version="1.0" encoding="UTF-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit">
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
<On Platform="WinUI" Value="{StaticResource WinUIEntryStyle}" />
</OnPlatform>
</Entry.Style>
<Entry.Behaviors>
<mct:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding Validate}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger
TargetType="Entry"
Binding="{Binding UserName.IsValid}"
Value="False">
<Setter Property="BackgroundColor" Value="{StaticResource ErrorColor}" />
</DataTrigger>
</Entry.Triggers>
</ResourceDictionary>
I need to change the Entry.Triggers DataTrigger Binding property to adapt to my solution if it's possible.
My another problem is that when the button is clicked to save the BindingContext is null, no data is banded to the model in the AddNewPlayer() function.

Related

Maui Bindings don't work unless I make a new Object each time I want to change a property

Just getting started with Maui and trying to learn how to properly bind values to a view. I'm having trouble getting bound values to update in the view when the bound data changes, unless I create a new object for each change.
I have a view:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:SimultaneousEquations.Models"
x:Class="SimultaneousEquations.Views.SolveView"
Title="Solver">
<ContentPage.BindingContext>
<models:Solver />
</ContentPage.BindingContext>
<VerticalStackLayout>
<Label
Text="Solver"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Label FontSize="22" Text="{Binding FirstEquation.AsString}" VerticalOptions="End" HorizontalOptions="Center" />
<Label FontSize="22" Text="{Binding SecondEquation.AsString}" VerticalOptions="End" HorizontalOptions="Center" />
<Label>
<Label.FontSize>22</Label.FontSize>
<Label.Text>
<MultiBinding StringFormat="{}{0} = {1}">
<Binding Path="FirstEquation.FirstVariable" />
<Binding Path="FirstValue" />
</MultiBinding>
</Label.Text>
</Label>
<Label>
<Label.FontSize>22</Label.FontSize>
<Label.Text>
<MultiBinding StringFormat="{}{0} = {1}">
<Binding Path="FirstEquation.SecondVariable" />
<Binding Path="SecondValue" />
</MultiBinding>
</Label.Text>
</Label>
<Grid ColumnDefinitions="*,*" ColumnSpacing="4">
<Button Text="Solve"
Clicked="SolveButton_Clicked" />
</Grid>
</VerticalStackLayout>
</ContentPage>
The views code behind:
public partial class SolveView : ContentPage
{
private Solver solver;
public SolveView()
{
InitializeComponent();
LoadEquations();
BindingContext = solver;
}
private void LoadEquations()
{
if (BindingContext is Models.Solver)
{
solver = new Solver();
solver.FirstEquation = new Equation();
solver.SecondEquation = new Equation();
solver.FirstEquation.FirstVariable = "X";
solver.FirstEquation.SecondVariable = "Y";
solver.FirstEquation.FirstCoefficient = 2;
solver.FirstEquation.SecondCoefficient = -3;
solver.FirstEquation.Answer = -9;
solver.SecondEquation.FirstVariable = "X";
solver.SecondEquation.SecondVariable = "Y";
solver.SecondEquation.FirstCoefficient = -1;
solver.SecondEquation.SecondCoefficient = 1;
solver.SecondEquation.Answer = 2;
}
}
private void SolveButton_Clicked(object sender, EventArgs e)
{
if (BindingContext is Models.Solver)
{
LoadEquations();
solver.SolveSimultaneousEquations();
BindingContext = solver;
}
}
}
A Solver class:
internal class Solver
{
public Equation FirstEquation { get; set; }
public Equation SecondEquation { get; set; }
public float FirstValue { get; set; }
public float SecondValue { get; set; }
public void SolveSimultaneousEquations()
{
float x =// sums here
float y =// sums here
FirstValue = x;
SecondValue = y;
}
}
An Equation class:
internal class Equation
{
public string FirstVariable { get; set; }
public string SecondVariable { get; set; }
public int FirstCoefficient { get; set; }
public int SecondCoefficient { get; set; }
public int Answer { get; set; }
public string AsString { get { return EquationAsString(); }}
private String EquationAsString()
{
//stuff here
}
}
Note in the code behind of the view that LoadEquations() creates a new Solver() and populates its values. These values are displayed in the view on app start up as desired. However, in SolveButton_Clicked I have to call LoadEquations again in order for the Bindings to FirstValue and SecondValue to display. If I only call solver.SolveSimultaneousEquations(); the values of FirstValue and SecondValue in the Solver object get updated but the view does not.
Can anybody explain why this is the case and how I might fix it without creating a new Solver() for each change in the underlying data?

how to bind a viewmodel variable to ObservableCollection items in xamarin form?

I have a list of tasks that I need to bind a Boolean variable to its item.
the task object includes a completedDate property that if it has value defines the task as completed.
on the view, I need to check if it has value the button text display the text: "mark as incomplete"
----Task Object-----
public class ProjectTaskLineItemSummary
{
public int TenantId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal? CostMultiplier { get; set; }
public DateTimeOffset? CompletedDate { get; set; }
public int? CompletedByUserId { get; set; }
}
-------viewmodel-------
viewmodel()
{
public ObservableCollection<ProjectTaskLineItemSummary> Tasks { get; set; }
...
bool isCompleted;
public bool IsCompleted
{
get {
return isCompleted;
}
set
{
isCompleted = value;
OnPropertyChanged();
}
}
}
-----view----
<CollectionView Grid.Row="1" ItemsSource="{Binding Tasks}" x:Name="List3">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame
Margin="0,10"
Padding="10"
BackgroundColor="{StaticResource PrimaryWhite}"
BorderColor="{StaticResource PrimaryLightGray}"
CornerRadius="10"
HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image
HeightRequest="20"
Source="iconCalender.png"
WidthRequest="20" />
<Label
FontFamily="{StaticResource MeduimFont}"
Style="{StaticResource LabelMedium}"
Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}"
TextColor="{StaticResource PrimaryBlack}"
/>
</StackLayout>
</StackLayout>
<BoxView
Grid.Row="1"
HeightRequest="1"
Color="{StaticResource PrimaryLightGray}" />
<Label
Grid.Row="2"
Style="{StaticResource LabelMedium}"
Text="{Binding Name}"
TextColor="{StaticResource PrimaryBlack}" />
<Button
x:Name="lstbtnMarkasComplite"
Grid.Row="5"
Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
FontSize="{StaticResource Font12}"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand"
Style="{StaticResource ButtonPurple}"
Text="Mark as Completed" >
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding IsCompleted}" Value="True">
<Setter Property="Text" Value="Mark Task as In Completed"/>
</DataTrigger>
</Button.Triggers>
</Button>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I tried to assign it in view model like below but it doesn't work:
public override async Task InitializeAsync(object navigationData)
{
await SetBusyAsync(async () =>
{
...
Tasks = ObjectMapper.Map<ObservableCollection<ProjectTaskLineItemSummary>>(project.TaskLineItems);
foreach (var task in Tasks)
{
isCompleted = task.CompletedDate.HasValue ? true : false;
}
RaisePropertyChanged(() => Model);
RaisePropertyChanged(() => Notes);
RaisePropertyChanged(() => Files);
RaisePropertyChanged(() => Tasks);
});
}
This could simply be achieved by Binding Text Property of the button and then dynamically set the Text of the button based on the CompletedDate of the entity.
Below is the code snippets for your reference:
Model:
ProjectTaskLineItemSummary.cs
public class ProjectTaskLineItemSummary
{
public int TenantId { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal? CostMultiplier { get; set; }
public DateTimeOffset? CompletedDate { get; set; }
public int? CompletedByUserId { get; set; }
public string CompletedButton { get; set; }
}
View:
<CollectionView ItemsSource="{Binding Tasks}" x:Name="List3" Background="aqua">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Frame Margin="0,10" Padding="10" HasShadow="False">
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" RowSpacing="15">
<StackLayout HorizontalOptions="EndAndExpand" Orientation="Horizontal">
<Image HeightRequest="30" Source="XamarinLogo.png" WidthRequest="80" />
<Label x:Name="mydate" Text="{Binding CompletedDate,StringFormat='{0:MMMM dd, yyyy}'}" TextColor="Black"/>
</StackLayout>
<BoxView Grid.Row="1" HeightRequest="1" Color="Black" />
<Label Grid.Row="2" Text="{Binding Name}" TextColor="Black" />
<Button Grid.Row="5" x:Name="lstbtnMarkasComplite" Padding="15,0"
Clicked="MarkTaskAsCompletedClicked"
CornerRadius="20"
Text="{Binding CompletedButton}"
FontSize="Medium"
HeightRequest="40"
CommandParameter="{Binding Id}"
HorizontalOptions="CenterAndExpand">
</Button>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel:
public class PageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ProjectTaskLineItemSummary> Tasks { get; set; }
bool isCompleted { get; set; }
public bool IsCompleted
{
get => isCompleted;
set
{
isCompleted = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(IsCompleted)));
}
}
string completed { get; set; }
public String CompletedButton
{
get => completed;
set
{
completed = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(CompletedButton)));
}
}
public PageViewModel()
{
Tasks = new ObservableCollection<ProjectTaskLineItemSummary>()
{
new ProjectTaskLineItemSummary(){TenantId = 1, Id =1, Name = "jobs ", Description= "jjjj",CostMultiplier=1 ,CompletedDate =DateTime.UtcNow, CompletedByUserId=1 ,CompletedButton = ""},
new ProjectTaskLineItemSummary(){TenantId = 2, Id =2, Name = "james ",Description= "aaaa",CostMultiplier=2 , CompletedByUserId=2,CompletedButton = "" },
new ProjectTaskLineItemSummary(){TenantId = 3, Id =3, Name = "rollex ",Description= "rrrr",CostMultiplier=3 ,CompletedDate =DateTime.UtcNow, CompletedByUserId=3 ,CompletedButton = ""}
};
setButtonIsCompleted();
}
private void setButtonIsCompleted()
{
foreach (var task in Tasks)
{
if (task.CompletedDate == null)
{
task.CompletedButton = "Mark Task as Completed";
}
else
{
task.CompletedButton = "Mark Task as inCompleted";
}
}
}
}

Trying to set picker within listview MVVM Xamarin

Trying to set the initial value to a picker through the use of SelectedItem. I can do this without any issue if the picker isn't within a listview. However, once I try to accomplish this in a listview no dice.
I never can get the picker to display the initially downloaded value. If I use the same binding for an entry it displays the expected string.
Thoughts??
This can be reproduced in this simplistic standalone project. Please help. Thanks.
https://github.com/smarcus3/DebuggingProject
XAML
<ListView x:Name="listView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding downloadedRecipeIngredients}"> <!--SelectedItem="{Binding SelectedItem}"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<!-- Element Label -->
<Entry VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" Text="{Binding IngredientName}"/>
<!--<Picker x:Name="pickerIngredient" HorizontalOptions = "StartAndExpand" ItemsSource="{Binding listIngredients}" BindingContext="{Binding Source={x:Reference Page}, Path=BindingContext}" SelectedItem="{Binding IngredientName}" WidthRequest="100"/>-->
<Picker x:Name="pickerIngredientancestor" HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding listIngredients, Source={RelativeSource AncestorType={x:Type viewModel:testPageViewModel}}}" SelectedItem="{Binding IngredientName}"/>
<Entry Text="{Binding Quantity}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<Entry Text="{Binding UnitName}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<Entry Text="{Binding Comments}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<!-- Assessment Menu Icon -->
<Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.btnPress, Source={x:Reference Page}}" CommandParameter="{Binding .}" />
</Label.GestureRecognizers>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
VIEW MODEL
public class testPageViewModel : BaseViewModel
{
clRecipeIngredient[] _downloadedRecipeIngredients;
public clRecipeIngredient[] downloadedRecipeIngredients
{
get {
return _downloadedRecipeIngredients;
}
set
{
//if (downloadedRecipeIngredients != value)
//{
_downloadedRecipeIngredients = value;
OnPropertyChanged("downloadedRecipeIngredients");
//}
}
}
//Lists for Pickers
ObservableCollection<string> _listIngredients = new ObservableCollection<string>();
public ObservableCollection<string> listIngredients { get { return _listIngredients; } }
private clRecipeDataBase recipeDataBase;
public testPageViewModel()
{
recipeDataBase = new clRecipeDataBase();
btnPress = new Command<clRecipeIngredient>(madeIt);
getData();
}
async void getData()
{
//PICKER INGREDIENT DATA
clIngredient[] tmp = await recipeDataBase.getIngredientData();
for (int i = 0; i < tmp.Length; i++)
{
_listIngredients.Add(tmp[i].IngredientName);
}
_downloadedRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE
OnPropertyChanged("downloadedRecipeIngredients");
}
public ICommand btnPress { get; private set; }
void madeIt(clRecipeIngredient x)
{
Console.WriteLine(x.IngredientName + " -- " + x.Comments);
//_downloadedRecipeIngredients.Remove(x);
}
}
clRecipeIngredients
public class clRecipeIngredient
{
public int RecipeIngredientsID { get; set; }
public int RecipeIDLookedUP { get; set; }
public int IngredientIDLookedUp { get; set; }
public double Quantity { get; set; }
public int UnitIDLookedUp { get; set; }
public bool HiddenFlag { get; set; }
public string UnitName { get; set; }
public string IngredientName { get; set; }
public string Comments { get; set; }
I checked your sample and you could modify it like following .
in Xaml
<Picker HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding Path=BindingContext.listIngredients, Source={x:Reference Page}}" SelectedItem="{Binding IngredientName, Mode=TwoWay}" />
in ViewModel
ObservableCollection had implemented the interface INotifyPropertyChanged in default . So you could simplify the code in your ViewModel .
Note : You could not set the value of SelectItem as a string directly even if they are equal . You need to set it like following
ing.IngredientName = listIngredients[0];
So the ViewModel could like
public class testPageViewModel : BaseViewModel
{
public ObservableCollection<clRecipeIngredient> downloadedRecipeIngredients
{
get;set;
}
public ObservableCollection<string> listIngredients { get; set; }
//private clRecipeDataBase recipeDataBase;
public testPageViewModel()
{
//recipeDataBase = new clRecipeDataBase();
btnPress = new Command<clRecipeIngredient>(madeIt);
downloadedRecipeIngredients = new ObservableCollection<clRecipeIngredient>();
listIngredients = new ObservableCollection<string>();
getData();
}
async void getData()
{
//PICKER INGREDIENT DATA
//clIngredient[] arrayIngredients = await recipeDataBase.getIngredientData();
//clIngredient[] arrayIngredients = new clIngredient[5];
//arrayIngredients[0].IngredientName = "Apple";
//arrayIngredients[1].IngredientName = "Salt";
//arrayIngredients[2].IngredientName = "Buuter";
//arrayIngredients[3].IngredientName = "Flour";
//arrayIngredients[4].IngredientName = "Egg";
listIngredients.Add("Apple");
listIngredients.Add("Salt");
listIngredients.Add("Butter");
listIngredients.Add("Flour");
listIngredients.Add("Egg");
//for (int i = 0; i < arrayIngredients.Length; i++)
//{
// _listIngredients.Add(arrayIngredients[i].IngredientName);
//}
//clRecipeIngredient[] arryRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE
clRecipeIngredient ing = new clRecipeIngredient();
ing.IngredientName = listIngredients[0];
ing.Quantity = 1;
ing.UnitName = "Cups";
ing.Comments = "Comments0";
clRecipeIngredient ing2 = new clRecipeIngredient();
ing2.IngredientName = listIngredients[1];
ing2.Quantity = 2;
ing2.UnitName = "Whole";
ing2.Comments = "Comments1";
downloadedRecipeIngredients.Add(ing);
downloadedRecipeIngredients.Add(ing2);
}
public ICommand btnPress { get; private set; }
void madeIt(clRecipeIngredient x)
{
Console.WriteLine(x.IngredientName + " -- " + x.Comments);
//_downloadedRecipeIngredients.Remove(x);
}
}
And don't forget to implement INotifyPropertyChanged in your model as the value of IngredientName will been changed .
public class clRecipeIngredient : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(
[CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));
}
//...
string ingredientName;
public string IngredientName
{
get
{
return ingredientName;
}
set
{
if (ingredientName != value)
{
ingredientName = value;
OnPropertyChanged("IngredientName");
}
}
}
//...
}
Its still unclear why you cannot directly set the selectedItem with a string as I can do in pickers not inside of a ListView.
However, setting the picker to use the SelectedIndex property works great. This is the KEY. For ListView's containing pickers I will not set their value based on INDEX instead of SelectedItem.
Final Code Snippets
XAML
<Picker HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding Path=BindingContext.listIngredients, Source={x:Reference Page}}" SelectedIndex="{Binding IngredientIDLookedUp}" />
View Model
clRecipeIngredient[] arryRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE
clRecipeIngredient ing = new clRecipeIngredient();
_downloadedRecipeIngredients.Clear();
for (int i = 0;i < arryRecipeIngredients.Length;i++)
{
for (int j=0; j<_listIngredients.Count;j++)
{
//FIND THE SELECTED INDEX BASED ON PICKER’S LIST and STORE IN THE CUSTOM CLASS
if(arryRecipeIngredients[i].IngredientName == _listIngredients[j])
{
arryRecipeIngredients[i].IngredientIDLookedUp = j;
}
}
_downloadedRecipeIngredients.Add(arryRecipeIngredients[i]);
}

Xamarin XAML DateTime and TimeSpan Binding

I can't seem to get the Binding working on a DateTime and TimeSpan field in my XAML. Everything appears to be OK, but the Picker's aren't loading the right values in. I'd also like to format the date and time slightly differently.
The date and time are coming in from a JSON response from an API.
I make this API call which maps to the model I posted above.
public static List<Race> GetRaces()
{
var raceList = new List<Race>();
string APIServer = Application.Current.Properties["APIServer"].ToString();
string Token = Application.Current.Properties["Token"].ToString();
var client = new RestClient(APIServer);
var request = new RestRequest("api/race", Method.GET);
request.AddHeader("Content-type", "application/json");
request.AddHeader("Authorization", "Bearer " + Token);
var response = client.Execute(request) as RestResponse;
raceList = JsonConvert.DeserializeObject<List<Race>>(response.Content);
return raceList;
}
Here's the model.
public class Race
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime RaceDate { get; set; }
public TimeSpan RaceStartTime { get; set; }
public string ContactEmail { get; set; }
public string ContactNumber { get; set; }
public string Description { get; set; }
public int MaxEntries { get; set; }
public int CurrentEntries { get; }
public bool IsOpenForEntries { get; set; }
public bool IsPublished { get; set; }
public string Visibility { get; set; }
}
I then have a View Model which is being bound including these getters and setters.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RestSharp;
using TechsportiseApp.API.Models;
using Xamarin.Forms;
using TechsportiseApp.API;
namespace TechsportiseApp.MainUI.Models
{
public class RacesViewModel : INotifyPropertyChanged
{
bool _visibility;
public bool Visibility { get; set; }
int _id;
public int Id
{
get
{
return _id;
}
set
{
if (_id != value)
{
_id = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("Id");
}
}
}
int _name;
public int Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
_name = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("Name");
}
}
}
string _description;
public string Description
{
get
{
return _description;
}
set
{
if (_description != value)
{
_description = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("Description");
}
}
}
string _contactEmail;
public string ContactEmail
{
get
{
return _contactEmail;
}
set
{
if (_contactEmail != value)
{
_contactEmail = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("ContactEmail");
}
}
}
string _contactNumber;
public string ContactNumber
{
get
{
return _contactNumber;
}
set
{
if (_contactNumber != value)
{
_contactNumber = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("ContactNumber");
}
}
}
DateTime _raceDate;
public DateTime RaceDate
{
get
{
return _raceDate;
}
set
{
if (_raceDate != value)
{
_raceDate = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("RaceDate");
}
}
}
TimeSpan _raceStartTime;
public TimeSpan RaceStartTime
{
get
{
return _raceStartTime;
}
set
{
if (_raceStartTime != value)
{
_raceStartTime = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("RaceStartTime");
}
}
}
int _maxEntries;
public int MaxEntries
{
get
{
return _maxEntries;
}
set
{
if (_maxEntries != value)
{
_maxEntries = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("MaxEntries");
}
}
}
public int currentEntries { get; }
bool _isOpenForEntries;
public bool IsOpenForEntries
{
get
{
return _isOpenForEntries;
}
set
{
if (_isOpenForEntries != value)
{
_isOpenForEntries = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("IsOpenForEntries");
}
}
}
bool _isPublished;
public bool IsPublished
{
get
{
return _isPublished;
}
set
{
if (_isPublished != value)
{
_isPublished = value;
// trigger some action to take such as updating other labels or fields
OnPropertyChanged("IsPublished");
}
}
}
public List<Race> RaceList
{
get
{
var racelist = RacesAPI.GetRaces();
return racelist;
}
}
int _racesIndex;
public int RacesIndex
{
get
{
return _racesIndex;
}
set
{
if (_racesIndex != value)
{
_racesIndex = value;
// trigger some action to take such as updating other labels or fields
Id = RaceList[value].Id;
Visibility = true;
OnPropertyChanged("RacesIndex");
OnPropertyChanged("Visibility");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
And the XAML that is displaying is here.
<Label x:Name="labelRaceDate"
Text="Race Date"
IsVisible="{Binding Visibility}"/>
<DatePicker x:Name="datepickerRaceDate"
Date="{Binding RaceDate, StringFormat='{0:ddd d MMMM yyyy}'}"
Format="ddd d MMMM yyyy"
IsVisible="{Binding Visibility}"/>
<Label x:Name="labelRaceStartTime"
Text="Race Time"
IsVisible="{Binding Visibility}"/>
<TimePicker x:Name="timepickerRaceStartTime"
Time="{Binding RaceStartTime}"
IsVisible="{Binding Visibility}"/>
However, the result I always get is the default date and time. I have validated that the JSON response values are coming through correctly.
Any ideas?
Here's the full XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TechsportiseApp.MainUI.Races" Title="Races">
<ScrollView Orientation = "Vertical" VerticalOptions="StartAndExpand">
<StackLayout Padding="0,20,0,0">
<Label x:Name="labelTest" Text="{Binding RaceDate}" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelInstructions" Text="Choose a race to view and edit, or create a new one." />
<Picker x:Name="pickerRaceList" ItemsSource="{Binding RaceList, Mode=TwoWay}" Title="Pick a race" ItemDisplayBinding="{Binding Name}" SelectedIndex="{Binding RacesIndex}" />
<Label x:Name="labelName" Text="Name" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryName" Text="{Binding Item.Name, Mode=TwoWay}" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelDescription" Text="Description" IsVisible="{Binding Visibility}"/>
<Editor x:Name="editorDescription" Text="{Binding Item.Description, Mode=TwoWay}" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelContactName" Text="Contact Name" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryContactName" Text="{Binding Item.ContactName, Mode=TwoWay}" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelContactNumber" Text="Contact Number" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryContactNumber" Text="{Binding Item.ContactNumber, Mode=TwoWay}" Keyboard="Telephone" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelContactEmail" Text="ContactEmail" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryContactEmail" Text="{Binding Item.ContactEmail, Mode=TwoWay}" Keyboard="Email" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelRaceDate" Text="Race Date" IsVisible="{Binding Visibility}"/>
<DatePicker x:Name="datepickerRaceDate" Date="{Binding Item.RaceDate, StringFormat='{0:ddd d MMMM yyyy}', Mode=TwoWay}" Format="ddd d MMMM yyyy" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelRaceStartTime" Text="Race Time" IsVisible="{Binding Visibility}"/>
<TimePicker x:Name="timepickerRaceStartTime" Time="{Binding Item.RaceStartTime, Mode=TwoWay}" IsVisible="{Binding Visibility}"/>
<Label x:Name="labelMaxEntries" Text="Max Entries" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryMaxEntries" Text="{Binding Item.MaxEntries, Mode=TwoWay}" Keyboard="Numeric" IsVisible="{Binding Visibility}" />
<Label x:Name="labelCurrentEntries" Text="Current Entries" IsVisible="{Binding Visibility}"/>
<Entry x:Name="entryCurrentEntries" Text="{Binding Item.CurrentEntries}" Keyboard="Numeric" IsVisible="{Binding Visibility}" IsEnabled="false"/>
<Label x:Name="labelIsOpenForEntries" Text="Open For Entries" IsVisible="{Binding Visibility}"/>
<Switch x:Name="switchIsOpenForEntries" IsToggled="{Binding Item.IsOpenForEntries, Mode=TwoWay}" IsVisible="{Binding Visibility}" />
<Button x:Name="saveButton" Text="Save" Clicked="OnSave" IsVisible="{Binding Visibility}"/>
</StackLayout>
</ScrollView>
</ContentPage>

Xamarin.Forms Add and Share Item CrashApp

Good morning to all, I've looked in several places I researched and did not found a solution that worked for my problems.
I'm new to C #, XAML and Xamarin, I am doing an application that creates lists with products on a screen, the screen with products is been populated via Json WebApi. Until there everthing looks fine, but since i tried to add 'ADD' and 'Share' functionalities it is loading the main page and crashes on navigating to listsPage.
I need to get a product from the product Page and add it to another view where my lists are. I created a ContextActions with 'Share' and 'AddToList' but i don't know how to get that product and 'Add' it to my lists. Same problem with 'Share' when i get the MenuItem and try to pass it to a Task in my ProductViewModel i get a NullReferenceException, but the object is not null.
I appreciate if someone could help me with this issues.
I know the post has got quite long but i wanted to give every possible info.
Here is my Lists Page:
<ListView x:Name="listaView" ItemSelected="listSelected" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Padding="20,0,20,0"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand">
<Label Text="{Binding Name}"
VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand" />
<Image Source="check.png"
HorizontalOptions="Start"
IsVisible="{Binding Done}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
and my listDetail:
<Label Text="Name" />
<Entry x:Name="nameEntry" Text="{Binding Name}" />
<Label Text="Description" />
<Entry x:Name="descriptionEntry" Text="{Binding Description}" />
<Label Text="Typ" />
<controls:BindablePicker x:TypeArguments="enums:Typ" SelectedItem="{Binding Typ}" />
<Label Text="Done" />
<Switch x:Name="doneEntry" IsToggled="{Binding Done}" />
<Label Text="Products:" />
<ListView ItemsSource="{Binding Products}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Aspect="AspectFit" HeightRequest="20" WidthRequest="20" Source="{Binding Image}" />
<Label Text="{Binding Name}" />
<Label Text="{Binding Price, StringFormat='R${0:C2}'}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Save" Clicked="salveClicked" />
<Button Text="Delete" Clicked="deleteClicked" />
<Button Text="Cancel" Clicked="cancelClicked" />
<Button Text="Speak" Clicked="speakClicked" />
</StackLayout>
</ContentPage>
ListDetails CODE BEHIND:
public ProductListDetailPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, true);
}
void saveClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
App.Database.SaveList(lista);
this.Navigation.PopAsync();
}
void deleteClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
App.Database.DeleteList(lista.ListaID);
this.Navigation.PopAsync();
}
void cancelClicked(object sender, EventArgs e)
{
var lista = (Lists)BindingContext;
this.Navigation.PopAsync();
}
void speakClicked(object sender, EventArgs e)
{
var lists = (Lists)BindingContext;
DependencyService.Get<ITextToSpeech>().Speak(lists.Name+ " " + lists.Descrip);
}
}
}
I believe the problem is in my model but have no idea what it is
Product Model:
public class Product : INotifyPropertyChanged
{
private int id;
public int ProductID
{
get { return id; }
set
{
id = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(id)));
}
}
private string name;
public string Name
{
get { return name; }
set
{
name= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(name)));
}
}
private double price;
public double Price{
get { return price; }
set
{
price= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(price)));
}
}
private string dtFab;
public string DtFab
{
get { return dtFab; }
set
{
dtFab= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(dtFab)));
}
}
private string dtValid;
public string DtValid {
get { return dtValid; }
set
{
dtValid= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(dtValid)));
}
}
private string amount;
public string Amount{
get { return quantidade; }
set
{
amount= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(amount)));
}
}
private string descrip;
public string Descrip{
get { return descrip; }
set
{
descrip= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(descrip)));
}
}
private string image;
public string Image
{
get { return image; }
set
{
image= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(image)));
}
}
private ICollection<ListProduct> listProduct;
public ICollection<ListProduct> ListProduct{
get { return listProduct; }
set
{
listProduct= value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(listProduct)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
ListsModel:
public class Lists
{
public Lista()
{
}
[PrimaryKey, AutoIncrement]
public int ListID get; set; }
public string Name { get; set; }
public string Descrip { get; set; }
public bool Done { get; set; }
public Typ Typ { get; set; }
public virtual ICollection<ListsProduct> ListsProducts{ get; set; }
}
public class ListsProduct
{
public int ListsProductID{ get; set; }
public int ListID { get; set; }
public int ProductID { get; set; }
public virtual Lists Lists { get; set; }
public virtual Product Product { get; set; }
}
}
ListProductModel:
SearchProductPage:
<StackLayout Orientation="Vertical">
<SearchBar Text="{Binding SearchBarText}" />
<Button x:Name="btnPesquisar" Text="Search" Command="{Binding SearchCommand}" />
<ListView ItemsSource="{Binding Products}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Share" Clicked="ShareProduct" />
<MenuItem Text="Add To" Clicked="AddProduct" />
</ViewCell.ContextActions>
<ViewCell.View>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" >
<Image Aspect="AspectFit" HeightRequest="20" WidthRequest="20" Source="{Binding Image}" />
<Label Text="{Binding Name}" />
<Label Text="{Binding Price, StringFormat='R${0:C2}'}" HorizontalOptions="End" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
SearchPage CODE BEHIND:
public partial class SearchPage: ContentPage
{
ProductsViewModel viewModel;
public TelaPesquisaView()
{
InitializeComponent();
this.BindingContext = new ViewModels.ProductsViewModel();
}
public async void AddProduct(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
await viewModel.AddToList(al.BindingContext as Product);
var produtoLista = new ListsPage();
await Navigation.PushAsync(produtoLista);
}
public async void ShareProduct(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
if (al != null) {
await viewModel.Share(al.BindingContext as Produto);
}
}
}
}
And the ProductViewModel
public class ProductViewModel : INotifyPropertyChanged
{
private string searchBarText = string.Empty;
public string SearchBarText {
get { return searchBarText ; }
set
{
if (searchBarText != value)
{
searchBarText = value ?? string.Empty;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(searchBarText )));
if (SearchCommand.CanExecute(null))
{
SearchCommand.Execute(null);
}
}
}
}
// filtrar somente os 5 primeiros
#region Command SearchCommand
private Xamarin.Forms.Command searchCommand;
public ICommand SearchCommand{
get
{
searchCommand= searchCommand?? new Xamarin.Forms.Command(DoSearchCommand, ExecuteCommand);
return searchCommand;
}
set
{
searchCommand= (Xamarin.Forms.Command)value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(searchCommand)));
}
}
private void DoSearchCommand()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
}
private bool ExecuteCommand()
{
return true;
}
#endregion
private ObservableCollection<Models.Produto> products;
public ObservableCollection<Models.Produto> Products {
get
{
ObservableCollection<Models.Product> searchProducts = new ObservableCollection<Models.Product>();
if (products != null)
{
List<Models.Product> prod = (from p in products
where p.Name.ToLower().Contains(searchBarText.ToLower())select p).Take(3).ToList<Models.Product>();
if (prod != null && prod.Any())
{
searchedProducts = new ObservableCollection<Models.Product>(prod);
}
}
return searchedProducts ;
}
set
{
products = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Products)));
}
}
public ProductsViewModel()
{
SearchCommand = new Xamarin.Forms.Command(async () =>
{
var products = await ApiProducts.Api.GetAsync();
Products = new ObservableCollection<Models.Product>(products );
});
}
public async Task AddToList(ListsProduct prod)
{
Lists list = new Lists();
list.ListsProduct.Add(prod);
App.Database.SaveList(list);
}
public async Task Share(Models.Product prod)
{
var title = prod.NomeProduto;
var message = prod.ToString();
// Share message and an optional title.
await CrossShare.Current.Share(message, title );
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Here is the starting point for you. For the rest you can use the same pattern passing your new product in constructors or just implement setters in your view models.
Define binding to get a new product
<MenuItem Text="Adicionar à" Clicked="AdicionaProduto" CommandParameter="{Binding .}" />
Then pass it to your ListasView constructor as parameter
public async void AdicionaProduto(object sender, EventArgs e)
{
var al = ((MenuItem)sender);
var produtoLista = new ListasView(al.CommandParameter as Produto);
await Navigation.PushAsync(produtoLista);
}
To be able to do that you need to change a constructor
public ListasView(Produto newProduto = null)
{
InitializeComponent();
//this.BindingContext = new ViewModels.ListasViewModel();
if (newProduto != null)
{
//do something
int x = 0;
}
You can take that newProduto and store it in your DB or pass further to other models or views via constructor or some setters.

Categories

Resources