xaml UI is not updating in xamarin forms - c#

I am binding my model with my UI and in my model i have done some calculation but other properties are binding with UI but some properties in which i have done calculation these are not binding with my UI but showing the calculation in my OnPropertyChange event.Kindly help me on this where is issue on my codes Thanks in advance.
-----My model----
public class SaleEntryModel
{
[PrimaryKey, AutoIncrement]
public int SaleID { get; set; }
public string CustomerName { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal Quantity { get; set; }
public decimal Rate { get; set; }
public decimal Total => Rate * Quantity;
public decimal Balance => (Total - (Discount + PaidAmount));
}
-- I am calculating the total and balance from the rate and quantity properties---
----OnPropertyChange event ---
private SaleEntryModel bindSaleEntryModel = new SaleEntryModel();
public SaleEntryModel BindSaleEntryModel
{
get { return bindSaleEntryModel; }
set
{
bindSaleEntryModel = value;
OnPropertyChanged(nameof(BindSaleEntryModel));
}
}
---my xaml code ---
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" Padding="10">
<Label Text="Rate" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="Rate" Margin="2,-5,2,5" Text="{Binding BindSaleEntryModel.Rate,Mode=TwoWay}"
HorizontalOptions="FillAndExpand" Keyboard="Numeric" ReturnType="Next" />
<Label x:Name="RateError" Margin="2,-10,2,5" TextColor="Red" IsVisible="false" FontAttributes="Italic" />
<Label Text="Quantity" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="Quantity" Margin="2,-5,2,5" Text="{Binding BindSaleEntryModel.Quantity,Mode=TwoWay}"
HorizontalOptions="FillAndExpand" Keyboard="Numeric" ReturnType="Next" />
<Label x:Name="QuantityError" Margin="2,-10,2,5" TextColor="Red" IsVisible="false" FontAttributes="Italic" />
<Label Text="Total" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="Total" Margin="2,-5,2,5" IsEnabled="False"
Text="{Binding BindSaleEntryModel.Totals,Mode=TwoWay}"
HorizontalOptions="FillAndExpand" ReturnType="Next"/>
<Label Text="Discount (Rs)" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="Discount" Margin="2,-5,2,5" Text="{Binding BindSaleEntryModel.Discount,Mode=TwoWay}"
HorizontalOptions="FillAndExpand"
Keyboard="Numeric" ReturnType="Next"/>
<Label x:Name="DiscountError" Margin="2,-10,2,5" TextColor="Red" IsVisible="false" FontAttributes="Italic" />
<Label Text="Paid Amount" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="PaidAmount" Margin="2,-5,2,5" Text="{Binding BindSaleEntryModel.PaidAmount,Mode=TwoWay}"
HorizontalOptions="FillAndExpand" Keyboard="Numeric" ReturnType="Next"/>
<Label x:Name="PaidAmountError" Margin="2,-10,2,5" TextColor="Red" IsVisible="false" FontAttributes="Italic" />
<Label Text="Balance" Margin="2,-10" FontAttributes="Bold" />
<Entry x:Name="Balance" Margin="2,-5,2,5" IsEnabled="False"
Text="{Binding BindSaleEntryModel.Balance,Mode=TwoWay}"
HorizontalOptions="FillAndExpand" />
<Grid HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Text="Save" x:Name="btnSave" HorizontalOptions="FillAndExpand"
CornerRadius="10" BorderWidth="2" BackgroundColor="#ff6633" TextColor="#fff" Margin="2"
Grid.Column="0" Grid.Row="0" Command="{Binding SaveCommand}" />
<Button Text="CLEAR" x:Name="btnClear" HorizontalOptions="FillAndExpand"
CornerRadius="10" BorderWidth="2" BackgroundColor="#bfbfbf"
TextColor="#fff" Margin="2" Grid.Column="1" Grid.Row="0" Command="{Binding ClearCommand}" />
</Grid>
</StackLayout>
In above image calculation is showing but not binding in My UI.

You need raise property change for each binding property in SaleEntryModel. Please refer following code.
public partial class CalculationQ : ContentPage
{
public CalculationQ()
{
InitializeComponent();
this.BindingContext = this;
GetSaleEntry();
}
private SaleEntryModel bindSaleEntryModel = new SaleEntryModel();
public SaleEntryModel BindSaleEntryModel
{
get { return bindSaleEntryModel; }
set
{
bindSaleEntryModel = value;
OnPropertyChanged(nameof(BindSaleEntryModel));
}
}
private void GetSaleEntry()
{
BindSaleEntryModel.SaleID = 1;
BindSaleEntryModel.CustomerName = "Murugan";
BindSaleEntryModel.ProductID = 1;
BindSaleEntryModel.ProductName = "Toy";
BindSaleEntryModel.Quantity = 5;
BindSaleEntryModel.Rate = 150;
BindSaleEntryModel.Discount = 5;
BindSaleEntryModel.PaidAmount = 250;
}
}
public class SaleEntryModel : INotifyPropertyChanged
{
public int SaleID { get; set; }
private string _customerName;
public string CustomerName
{
get { return _customerName; }
set
{
_customerName = value;
OnPropertyChange(nameof(CustomerName));
}
}
public int ProductID { get; set; }
private string _productName;
public string ProductName
{
get { return _productName; }
set
{
_productName = value;
OnPropertyChange(nameof(ProductName));
}
}
private decimal _quantity;
public decimal Quantity
{
get { return _quantity; }
set
{
_quantity = value;
OnPropertyChange(nameof(Quantity));
OnPropertyChange(nameof(Total));
OnPropertyChange(nameof(Balance));
}
}
private decimal _rate;
public decimal Rate
{
get { return _rate; }
set
{
_rate = value;
OnPropertyChange(nameof(Rate));
OnPropertyChange(nameof(Total));
OnPropertyChange(nameof(Balance));
}
}
public decimal Total => Rate * Quantity;
public decimal Balance => (Total - (Discount + PaidAmount));
private int _discount;
public int Discount
{
get => _discount;
set
{
_discount = value;
OnPropertyChange(nameof(Discount));
OnPropertyChange(nameof(Balance));
}
}
private int _paidAmount;
public int PaidAmount
{
get => _paidAmount;
set
{
_paidAmount = value;
OnPropertyChange(nameof(PaidAmount));
OnPropertyChange(nameof(Balance));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChange(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
My suggestion is to keep separate model for viewmodel binding instead of using entity model.

Related

How to create a UI with horizontal and vertical scrolling in xamarin forms

I have a design with vertical and horizontal scrolling.I need to add two lists in to the same page.So I have used bindable layout for this but the scrolling is not working on this. Please help me on the below. I am stuck on this for a week.
Here is my View and ViewModel
<StackLayout BackgroundColor="WhiteSmoke">
<Grid ColumnDefinitions="*,*" RowDefinitions="Auto,*,Auto">
<StackLayout
Grid.Row="0"
Grid.Column="1"
BindableLayout.ItemsSource="{Binding ListSource.TestList2}"
Orientation="Horizontal"
VerticalOptions="FillAndExpand">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label
Margin="0" WidthRequest="75"
FontSize="16"
HeightRequest="58"
HorizontalOptions="FillAndExpand"
MaxLines="3"
Text="{Binding Subname}"
VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<StackLayout x:Name="layout1"
Grid.Row="1" Margin="30,0,0,0"
Grid.Column="0" Grid.ColumnSpan="2"
BindableLayout.ItemsSource="{Binding ListSource.TestList}"
Orientation="Vertical">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="*,*">
<Label
Grid.Column="0"
Margin="3"
Text="{Binding name}" />
<StackLayout
Grid.Column="1"
Margin="0"
BindableLayout.ItemsSource="{Binding classes}"
HorizontalOptions="FillAndExpand"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<RadioButton WidthRequest="75"
HorizontalOptions="FillAndExpand"
IsChecked="{Binding IsFailed}"/>
<Entry Placeholder="Subject"
IsVisible="{Binding IsEntry}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</StackLayout>
ViewModel
public class ListViewVM:BaseViewModel
{
private TestClass _listSource;
public TestClass ListSource
{
get { return _listSource; }
set { _listSource = value;
OnPropertyChanged("ListSource");
}
}
//public
public ListViewVM()
{
TestClass kk=new TestClass();
kk.BindData();
ListSource = new TestClass();
ListSource.TestList =new List<TestName>(kk.TestList);
ListSource.TestList2= new List<TestSubject>( kk.TestList2);
ListSource.TestList.ForEach(x => x.classes=kk.TestList2);
var item = ListSource;
}}
Model
public class TestClass
{
public List<TestName> TestList { get; set; }
public List<TestSubject> TestList2 { get; set; }
public void BindData()
{
TestList = new List<TestName>() { new TestName {id=1,name="Jo"},
new TestName {id=2,name="Annie" },
new TestName {id=3,name="Alex" }};
TestList2 = new List<TestSubject>() { new TestSubject { id=1,Subname="Maths"},
new TestSubject { id=2,Subname="Physics"},
new TestSubject { id=2,Subname="Chemestry"},
new TestSubject { id=2,Subname="Other",IsEntry=true}};
}
}
public class TestName
{
public int id { get; set; }
public string name { get; set; }
public List<TestSubject> classes { get; set; }
}
public class TestSubject
{
public int id { get; set; }
public string Subname { get; set; }
public bool IsFailed {get;set;}
public bool IsOthersSelected { get; set; }
public bool IsEntry { get; set; }
}
Current UI
Some conditions is there , the user should select only one option from each row, but in my design they can select multiple option. If I add the RadioButton without that stacklayout the selection case is working but that time I cannot add the Entry field.If the user select "other" , then I need display an input field at the end along with "other" option for adding the other subject . So that field also mandatory for me.

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

MessagingCenter not subscribing

I'm new to Xamarin (and new in coding in general).
I'm using xct TouchEffect within a ListView to try to get a LongPress menu.
Since the TouchEffect.LongPressCommand is a Command, I can only bound it to the model page from some reason.
So... I'm trying to send information to the Code-behind via MessagingCenter.
The problem I have is the message is not receiving.
I read a lot and tried to figure it out, and I guess to be able to receive the message, the subscriber needs to be instantiate/initialize first.
The main problem I have is... I don't know how to do it lol.
Or the whole thing I'm trying to do is wrong?
I will add some code but if anything else is needed please let me know.
Note: the loading page (when the app start) is a GroupPage(which working fine), the problem is with the ItemsPage.
Thank you so much for everyone.
ItemsPage.xaml
<ContentPage.BindingContext>
<localvm:ItemViewModel/>
</ContentPage.BindingContext>
<ListView ItemsSource="{Binding Items, Mode=TwoWay}" x:Name="lstView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
SelectedItem="{Binding SelectedItem}" HasUnevenRows="True" RowHeight="50">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0,0,8,0" Margin="4,0,4,0" xct:TouchEffect.LongPressCommand="{Binding LongPressItemCommand}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemName}" TextColor="Black" Grid.Column="1" FontSize="Medium"></Label>
<Label Text="{Binding ItemDescription}" Grid.Column="1" VerticalTextAlignment="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ItemsPage.cs
namespace MobileApp2
{
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
MessagingCenter.Subscribe<Item, Guid>(this, "PopupMenuItemMsg",
(page, itemId) =>
{
Main_PopupMenu(itemId);
});
}
public async void Main_PopupMenu(Guid itemId)
{
DisplayActionSheet("Test", "Test", "OK");
}
}
}
Items.cs (model)
namespace MobileApp2
{
public class Item : INotifyPropertyChanged
{
public Command LongPressItemCommand { get; }
public Guid ItemId { get; set; }
public Guid GroupId { get; set; }
private string itemName = string.Empty;
public string ItemName
{
get { return itemName; }
set
{
if (value != null) itemName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ItemName"));
}
}
private string itemDescription = string.Empty;
public string ItemDescription
{
get
{
return itemDescription.Trim();
}
set
{
if (value != null) itemDescription = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ItemDescription"));
}
}
public Item(string itemName, string itemDescription)
{
ItemName = itemName;
ItemDescription = itemDescription;
}
public Item()
{
LongPressItemCommand = new Command(() =>
{
MessagingCenter.Send<Item, Guid>(this, "PopupMenuItemMsg", ItemId);
});
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
You could check the code below with relative binding of Command.
Model:
public class Item
{
public string ItemName { get; set; }
public string ItemDescription { get; set; }
}
ViewModel:
public class ItemViewModel
{
public ICommand LongPressItemCommand { get; set; }
public Guid ItemId { get; set; }
public ObservableCollection<Item> Items { get; set; }
public ItemViewModel()
{
LongPressItemCommand = new Command(() =>
{
MessagingCenter.Send<ItemViewModel, Guid>(this, "PopupMenuItemMsg", ItemId);
});
CreateCollection();
}
public void LongPress()
{
}
public void CreateCollection()
{
Items = new ObservableCollection<Item>()
{
new Item(){ ItemName="A", ItemDescription="AA"},
new Item(){ ItemName="B", ItemDescription="BB"},
new Item(){ ItemName="C", ItemDescription="CC"},
};
}
}
Xaml:
<ContentPage.BindingContext>
<localvm:ItemViewModel></localvm:ItemViewModel>
</ContentPage.BindingContext>
<StackLayout>
<ListView ItemsSource="{Binding Items, Mode=TwoWay}" x:Name="lstView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
SelectedItem="{Binding SelectedItem}" HasUnevenRows="True" RowHeight="50">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="0,0,8,0" Margin="4,0,4,0" xct:TouchEffect.LongPressCommand="{Binding Path=BindingContext.LongPressItemCommand, Source={x:Reference Name=lstView}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding ItemName}" TextColor="Black" Grid.Column="0" FontSize="Medium"></Label>
<Label Text="{Binding ItemDescription}" Grid.Column="1" VerticalTextAlignment="End"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Code behind:
MessagingCenter.Subscribe<ItemViewModel, Guid>(this, "PopupMenuItemMsg",
(page, itemId) =>
{
Main_PopupMenu(itemId);
});

Xamarin Forms - Change a CheckBox value when other CheckBox changed inside a ListView

I have a ListView with a ItemSource binding to a list object; inside the listview there are some items that are filled with the values of the list object. I have 2 checkboxes that are binding to elements of the list object and want to uncheck one when the other is checked. In the code I wrote, the values are correctly changed in the list object but the checkbox didn't change (stay unchecked). Next is the code that I wrote.
XAML Part
<ListView ItemsSource="{Binding ListaAsistencia}" HasUnevenRows="True" SelectionMode="None" x:Name="AsistList">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame Margin="5,2,5,2" Padding="0" BackgroundColor="Transparent" BorderColor="#915c0d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".7*"></ColumnDefinition>
<ColumnDefinition Width=".3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label Text="{Binding alumno, Mode=TwoWay}" Grid.Row="0" Grid.Column="0" FontSize="Micro" Margin="5,2,2,2"/>
<Entry Placeholder="Notas" Text="{Binding notas}" Grid.Row="1" Grid.Column="0" FontSize="Micro" TextColor="Black" />
<Grid Grid.Row="0" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="AsistiĆ³" Grid.Column="0" HorizontalOptions="End" FontSize="Micro" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding asistencia, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="Black"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".65*"></ColumnDefinition>
<ColumnDefinition Width=".35*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Text="F. Just." Grid.Column="0" FontSize="Micro" HorizontalOptions="End" VerticalOptions="Center"/>
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" CheckedChanged="CheckBox_Just_CheckedChanged" AutomationId="{Binding idalumno_grupo}"/>
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
C# BackEnd Part
private void CheckBox_Just_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var vm = BindingContext as AsisCapturarViewModel;
if ((e.Value) && (!vm.obtainingData))
{
CheckBox switchBox = (CheckBox)sender;
vm.UncheckFalta(switchBox.AutomationId);
}
}
C# View Model Part
public async void UncheckFalta(string idalumno_grupo)
{
if (!String.IsNullOrEmpty(idalumno_grupo))
{
int idalumno_grupoUse = Convert.ToInt32(idalumno_grupo);
ListaAsistencia.Where(a => a.idalumno_grupo == idalumno_grupoUse).ToList().ForEach(s => s.asistencia = false);
}
}
Class used in the List
public class AsistenciaList
{
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public bool asistencia { get; set; }
public string notas { get; set; }
public bool falta_justificada { get; set; }
}
Thanks for your help.
Firstly , agree with Jason .You should implement the interface INotifyPropertyChanged in your model if you want to update UI in runtime .
In addition , since you have used MVVM , you should put all the logic to your viewmodel .
So you can improve your code as following
in your model
public class AsistenciaList:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public int idasistencia { get; set; }
public DateTime fecha { get; set; }
public int idtipo_evento { get; set; }
public string tipo_evento { get; set; }
public int idmaestro_grupo { get; set; }
public int idalumno_grupo { get; set; }
public string alumno { get; set; }
public string notas { get; set; }
private bool asis;
public bool asistencia
{
get
{
return asis;
}
set
{
if (asis != value)
{
asis = value;
NotifyPropertyChanged();
}
}
}
private bool falta;
public bool falta_justificada
{
get
{
return falta;
}
set
{
if (falta != value)
{
falta = value;
NotifyPropertyChanged();
}
}
}
}
xaml
<CheckBox IsChecked="{Binding falta_justificada, Mode=TwoWay}" Grid.Column="1" HorizontalOptions="Start" Color="DarkBlue" AutomationId="{Binding idalumno_grupo}"/>
ViewModel
//...
foreach(AsistenciaList asistencia in ListaAsistencia)
{
asistencia.PropertyChanged += Asistencia_PropertyChanged;
}
//...
private void Asistencia_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName== "falta_justificada")
{
AsistenciaList asistencia = sender as AsistenciaList;
var idalumno_grupo = asistencia.idalumno_grupo;
//...do something you want
}
}

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>

Categories

Resources