OnPropertyChanged not fire with label text - c#

I am in a Xamarin app I which I have to make a to-do list. I am using listview. I have to set a label text dynamically every time I add a new to-do like so: Number of tasks 2/4 where 2 are the done tasks and 4 are the total ones. Everything goes right but I found some issues when I have to update the label text. I am using the MVVM pattern. In XAML I bind the text value to SetInfoDoneText. In MainPage, I have the bindigContext set to VM(TodoListViewModel). I use INotifyPropertyChanged with OnPropertyChanged. I made a method in which the value of setInfoDoneText is changed. The problem is that the set and get is called only once and when the setInfoDoneText is updated by the method OnPropertyChanged does not fire again. Here is the code.
THE PROBLEM IS WHEN I TRY TO UPDATE lblDoneInfo text (
)
class TodoListViewModel: INotifyPropertyChanged
{
public ObservableCollection<TodoItem> todoItems { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public String setInfoDoneText;
public String SetInfoDoneText
{
get => setInfoDoneText;
set
{
setInfoDoneText = value;
OnPropertyChanged("SetInfoDoneText");
}
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int doneTask = 0;
int totalTask = 0;
public TodoListViewModel()
{
this.todoItems = new ObservableCollection<TodoItem>();
setInfoDoneText = "Number of tasks: " + doneTask + "/" + totalTask;
}
public ICommand AddTodoCommand => new Command(AddTodoItem);
public String newTodoInputValue { get; set; }
public String selectedItem { get; set; }
public bool highPriority { get; set; }
public bool midPriority { get; set; }
public bool lowPriority { get; set; }
Color newColor = new Color();
public void AddTodoItem()
{
if (highPriority)
{
newColor = Color.Red;
AddNewItem(newColor);
highPriority = false;
}
if (midPriority)
{
newColor = Color.Orange;
AddNewItem(newColor);
midPriority = false;
}
if (lowPriority)
{
newColor = Color.Yellow;
AddNewItem(newColor);
lowPriority = false;
}
}
public TodoItem AddNewItem(Color newColor)
{
TodoItem newItem = new TodoItem(newTodoInputValue,
false,
highPriority,
midPriority,
lowPriority,
newColor);
todoItems.Add(newItem);
UpdateDoneInfo();
return newItem;
}
public ICommand RemoveTodoCommand => new Command(RemoveTodoItem);
public void RemoveTodoItem(object o)
{
TodoItem todoItemBeingRemoved = o as TodoItem;
todoItems.Remove(todoItemBeingRemoved);
}
public ICommand EditTodoCommand => new Command(EditTodoItem);
public void EditTodoItem(object o)
{
TodoItem todoItemBeingEdited = o as TodoItem;
int newIndex = todoItems.IndexOf(todoItemBeingEdited);
todoItems.Remove(todoItemBeingEdited);
TodoItem updatedTodo = AddNewItem(newColor);
//todoItems.Add(updatedTodo);
int oldIndex = todoItems.IndexOf(updatedTodo);
todoItems.Move(oldIndex, newIndex);
}
public String UpdateDoneInfo()
{
totalTask = todoItems.Count;
foreach (TodoItem item in todoItems)
{
if (item.complete) doneTask++;
}
return setInfoDoneText = "Number of tasks: " + doneTask + "/" + totalTask;
}
}
<ContentPage.BindingContext>
<local:TodoListViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Entry
x:Name="inputField"
Text="{Binding newTodoInputValue}"
Placeholder="Enter a todo..."
/>
<Label x:Name="lblDoneInfo" Text="{Binding SetInfoDoneText, Mode=TwoWay }">
</Label>
<FlexLayout AlignItems="Center" JustifyContent="SpaceBetween">
<input:CheckBox x:Name="highP"
IsChecked="{Binding highPriority}"
CheckChangedCommand="{Binding AddTodoCommand}"
Margin="0,0,20,0" />
<Label Text="High Priority" FontSize="Medium"/>
<input:CheckBox x:Name="midP"
IsChecked="{Binding midPriority}"
CheckChangedCommand="{Binding AddTodoCommand}"
Margin="0,0,20,0" />
<Label Text="Medium Priority" FontSize="Medium"/>
<input:CheckBox x:Name="lowP"
IsChecked="{Binding lowPriority}"
CheckChangedCommand="{Binding AddTodoCommand}"
Margin="0,0,20,0" />
<Label Text="Low Priority" FontSize="Medium"/>
</FlexLayout>
<ListView x:Name="todoList" ItemsSource="{Binding todoItems}" SelectedItem="{Binding selectedItem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell Height="20">
<FlexLayout JustifyContent="SpaceBetween" AlignItems="Center" Padding="20,0">
<ContentView>
<FlexLayout AlignItems="Center">
<input:CheckBox IsChecked="{Binding complete}" Margin="5" />
<Label x:Name="todoText" TextColor="{Binding color}" Text="{Binding todoText}" FontSize="Large"/>
</FlexLayout>
</ContentView>
<ImageButton
Source="editar_24.png"
BackgroundColor="Transparent"
WidthRequest="100"
HeightRequest="100"
Margin="0,0,20,0"
Command="{Binding Path=BindingContext.EditTodoCommand,
Source={x:Reference todoList}}"
CommandParameter="{Binding .}"/>
<ImageButton
Source="basura_24.png"
BackgroundColor="Transparent"
WidthRequest="100"
HeightRequest="100"
Command="{Binding Path=BindingContext.RemoveTodoCommand,
Source={x:Reference todoList}}"
CommandParameter="{Binding .}"/>
</FlexLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>

PropertyChanged only fires when you set the value of the public property SetInfoDoneText. Your code is setting the value of the field setInfoDoneText (lowercase).
It is generally best practice to make the field private to prevent this
private String setInfoDoneText;

Related

Xamarin Forms Editing Item with SQL

I have an application where items are added to a collection view and then you can tap on that item where a detail page comes up. With this detail page, you can edit the title and description given and then it should update in the collection view. However, when changes are made to the item, the item is not changed within the observable collection. How do I go about this?
Here is my xaml for the detail page:
<StackLayout Spacing="5" Padding="10" AnchorY="0.5">
<!--<Label Text="Title:" FontSize="Medium" FontAttributes="Bold" TextColor="White"/>-->
<Editor Text="{Binding Text}" FontSize="Title" TextColor="White"/>
<!--<Label Text="Note:" FontSize="Medium" FontAttributes="Bold" TextColor="White"/>-->
<Editor Text="{Binding Description}" FontSize="Small" TextColor="White"/>
<Grid ColumnDefinitions="*,*,*">
<Label Grid.Column="0" Grid.ColumnSpan="2" Text="Due Date:" FontSize="Medium" FontAttributes="Bold" TextColor="White" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand"/>
<Label Grid.Column="0" Grid.ColumnSpan="2" Padding="50,4,0,0" Text="{Binding DueDate}" FontSize="Small" TextColor="White" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"/>
</Grid>
<Button Text="Save"
Command="{Binding SaveChangesCommand}"
VerticalOptions="EndAndExpand"
HorizontalOptions="EndAndExpand"
BackgroundColor="Black"
BorderWidth="1.5"
BorderColor="White"
CornerRadius="50">
</Button>
</StackLayout>
Here is my DataBaseService:
public async Task UpdateItem(Item item)
{
var databasePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyData3.db");
var db = new SQLiteAsyncConnection(databasePath);
await db.UpdateAsync(item);
OnUpdatedItem?.Invoke();
}
OnUpdateItem?.Invoked();
private void LocalDatabaseService_OnUpdatedItem()
{
_ = ExecuteLoadItemsCommand();
}
and here is the OnSaveChanges
private async void OnSaveChanges(Item item)
{
Text = Text;
Description = Description;
DueDate = DueDate;
await LocalDatabaseService.UpdateItem(item);
await Shell.Current.GoToAsync("..");
}
LoadItems:
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Items.Clear();
var items = await LocalDatabaseService.GetAllItems();
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
Item Model:
public class Item : INotifyPropertyChanged
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Text
{
get { return Text; }
set
{
Text = value;
if (Text != value)
{
OnPropertyChanged("Text");
Text = value;
}
}
}
public string Description
{
get { return Description; }
set
{ Description = value;
if(Description != value)
{
OnPropertyChanged("Description");
Description = value;
}
}
}
public DateTime DueDate { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
any help is appreciated, Thanks.
From the code you posted, we found the parameter in method OnSaveChanges is useless since it is always null.
You can change your code like this in class ItemDetailViewModel:
public Command SaveChangesCommand { get; }
public ItemDetailViewModel()
{
SaveChangesCommand = new Command(OnSaveChanges);
}
And method OnSaveChanges:
private async void OnSaveChanges()
{
Item item = new Item();
item.Id = Int32.Parse(ItemId);
item.Text = Text;
item.Description = Description;
item.DueDate = DueDate;
await LocalDatabaseService.UpdateItem(item);
await Shell.Current.GoToAsync("..");
}

CheckBox create a string from the values of the selected objects

I have a collection view with a checkbox.
It looks like the following:
[Image][PetName][Checkbox]
I want to create a string with all the names of the pets which have been selected the pass this value through a function.
I have tried the following code but I am getting object reference is null in selectedPets.Add(ob) I'm sure Im proably going the wrong way with this but I am new to coding.
public List<PetProfile> selectedPets;
private void CheckBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
var checkbox = sender as CheckBox;
var ob = checkbox.BindingContext as PetProfile;
if (ob != null)
{
selectedPets.Add(ob);
}
}
private string CreatePetName()
{
var stringBuilder = new StringBuilder();
var listlenght = selectedPets.Count;
foreach (var pet in selectedPets)
{
if (selectedPets.Count == 0)
{
stringBuilder.Append(pet.PetName);
}
else if (listlenght > 0 && pet == selectedPets[0] )
{
stringBuilder.Append(pet.PetName + " ");
}
else if (pet == selectedPets[listlenght])
{
stringBuilder.Append(" " + pet.PetName );
}
else
{
stringBuilder.Append(" " + pet.PetName + " ");
}
}
return stringBuilder.ToString();
}
private async void SubmitBtn_Clicked(object sender, EventArgs e)
{
var Petnames = CreatePetName();
}
XAML:
<CollectionView x:Name="petCollectionView" ItemsSource="{Binding EmptyPetInfo}" HeightRequest="200">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" RowDefinitions="80" ColumnDefinitions="120,60,60">
<Image Grid.Column="0"
Grid.Row="0"
x:Name="PetImage"
Source="{Binding imageUrl}"/>
<Label Grid.Column="1"
Grid.Row="0"
Text="{Binding PetName}"
FontAttributes="Bold"
x:Name="labelpetname" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<CheckBox Grid.Row="0" Grid.Column="2" HorizontalOptions="End" IsChecked="{Binding Selected, Mode=TwoWay}" BindingContext="{Binding .}" CheckedChanged="CheckBox_CheckedChanged"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I want to create a string with all the names of the pets which have been selected the pass this value through a function.
I suggest you don't need to use CheckBox_CheckedChanged event to get selected PetName, you can create one class name Petclass, implementing INotifyPropertyChanged, to notify Selected property changed.
public class Petclass:ViewModelBase
{
public string imageUrl { get; set; }
public string PetName { get; set; }
private bool _Selected;
public bool Selected
{
get { return _Selected; }
set
{
_Selected = value;
RaisePropertyChanged("Selected");
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then loading some data to test in collectionview. foreach EmptyPetInfo Collectionview ItemsSource="{Binding EmptyPetInfo}" to check Selected property is true or false.
<CollectionView
x:Name="petCollectionView"
HeightRequest="200"
ItemsSource="{Binding EmptyPetInfo}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid
Padding="10"
ColumnDefinitions="120,60,60"
RowDefinitions="80">
<Image
x:Name="PetImage"
Grid.Row="0"
Grid.Column="0"
Source="{Binding imageUrl}" />
<Label
x:Name="labelpetname"
Grid.Row="0"
Grid.Column="1"
FontAttributes="Bold"
HorizontalTextAlignment="Center"
Text="{Binding PetName}"
VerticalTextAlignment="Center" />
<CheckBox
Grid.Row="0"
Grid.Column="2"
HorizontalOptions="End"
IsChecked="{Binding Selected, Mode=TwoWay}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public partial class Page17 : ContentPage
{
public ObservableCollection<Petclass> EmptyPetInfo { get; set; }
public Page17()
{
InitializeComponent();
EmptyPetInfo = new ObservableCollection<Petclass>()
{
new Petclass(){imageUrl="check.png",PetName="pet 1"},
new Petclass(){imageUrl="delete.png",PetName="pet 2"},
new Petclass(){imageUrl="favorite.png",PetName="pet 3"},
new Petclass(){imageUrl="flag.png",PetName="pet 4"}
};
this.BindingContext = this;
}
private void btn1_Clicked(object sender, EventArgs e)
{
var stringBuilder = new StringBuilder();
foreach (Petclass pet in EmptyPetInfo)
{
if(pet.Selected)
{
stringBuilder.Append(pet.PetName + " ");
}
}
string str = stringBuilder.ToString();
}
}
Using ObservableCollection Class, represent a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
you need to initialize selectedPets before you use it
public List<PetProfile> selectedPets = new List<PetProfile>();

Can't update data bindings from object in Xamarin forms

Im trying to update my xamarin bindings from a object in my viewmodel. What am I doing wrong.
When i see look at the page and look at "User", i see my user object is there.
Page:
<StackLayout BindableLayout.ItemsSource="{Binding User}"
Orientation="Horizontal">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:User">
<StackLayout>
<Label Text="{Binding Name, Mode=OneWay}"/>
<Label Text="{Binding PhoneNumber, Mode=OneWay}"/>
<Label Text="{Binding Adress, Mode=OneWay}"/>
<Label Text="{Binding Email, Mode=OneWay}"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
What you need to know about the Viewmodel with mvvhelpers
public User User { get; set; }
public MyProfileViewModel()
{
Title = "MyProfile";
RefreshCommand = new AsyncCommand(Refresh);
AddCommand = new AsyncCommand(Add);
// RemoveCommand = new AsyncCommand<User>(Remove);
User = new User();
}
public async refrsh(){
User = await MyProfileService.GetMyProfile(2);
nameDisplay = User.Name;
}
string nameDisplay;
public string NameDisplay
{
get => nameDisplay;
set => SetProperty(ref nameDisplay, value);
}
According to Jason's opinion, you don't need to use BindableLayout and a DataTemplate if you only have a single User object, you can bind User Object to StackLayout BindingContext directly.
<StackLayout BindingContext="{Binding User}">
<Label Text="{Binding Name, Mode=OneWay}" />
<Label Text="{Binding PhoneNumber, Mode=OneWay}" />
<Label Text="{Binding Adress, Mode=OneWay}" />
<Label Text="{Binding Email, Mode=OneWay}" />
</StackLayout>
If you have collection of IEnumerable User, you can use BindableLayout.ItemsSource and a DataTemplate
You need to implement INotifyPropertyChanged for User properties, it will notify the data changes.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The complete code:
<StackLayout>
<StackLayout BindingContext="{Binding User}">
<Label Text="{Binding Name, Mode=OneWay}" />
<Label Text="{Binding PhoneNumber, Mode=OneWay}" />
<Label Text="{Binding Adress, Mode=OneWay}" />
<Label Text="{Binding Email, Mode=OneWay}" />
</StackLayout>
<Button
x:Name="btn1"
Command="{Binding changecommand}"
Text="change user data" />
</StackLayout>
public partial class Page27 : ContentPage
{
public Page27()
{
InitializeComponent();
this.BindingContext = new MyProfileViewModel();
}
}
public class MyProfileViewModel
{
public User User { get; set;}
public ICommand changecommand { get; }
public MyProfileViewModel()
{
User = new User();
User.Name = "cherry";
User.PhoneNumber = "123";
User.Adress = "location 1";
User.Email = "xxxxx.#outlook.com";
changecommand = new Command(() =>
{
User.Name = "barry";
});
}
}
public class User:ViewModelBase
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged("Name");
}
}
private string _PhoneNumber;
public string PhoneNumber
{
get { return _PhoneNumber; }
set
{
_PhoneNumber = value;
RaisePropertyChanged("PhoneNumber");
}
}
private string _Adress;
public string Adress
{
get { return _Adress; }
set
{
_Adress = value;
RaisePropertyChanged("Adress");
}
}
private string _Email;
public string Email
{
get { return _Email; }
set
{
_Email = value;
RaisePropertyChanged("Email");
}
}
}

Xamarin Forms Bind Bool Based on Multiple Values

I've implemented checkboxes in my Xamarin Forms App using the following article:
https://alexdunn.org/2018/04/10/xamarin-tip-build-your-own-checkbox-in-xamarin-forms/
I'm trying to use the new BindableLayout to build a list of title's (Mr, Mrs etc):
<StackLayout x:Name="parent"
Grid.Row="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
BindableLayout.ItemsSource="{Binding Titles}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Horizontal">
<control:CheckBoxView VerticalOptions="CenterAndExpand"
IsChecked="..."
CheckedCommand="{Binding BindingContext.CheckCommand, Source={x:Reference parent}}"
CheckedCommandParameter="{Binding Identifier}"
HorizontalOptions="Start"
OutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryBackgroundColor, Source={x:Reference parent}}"
CheckedOutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}"
CheckColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}">
</control:CheckBoxView>
<Label Margin="0, 0, 20, 0"
VerticalOptions="Center"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Start"
HorizontalOptions="FillAndExpand"
TextColor="{Binding BindingContext.Campaign.CampaignProfile.TextColor, Source={x:Reference parent}}"
FontSize="{Binding BindingContext.Campaign.CampaignProfile.TextSize, Source={x:Reference parent}}"
WidthRequest="150"
MinimumWidthRequest="100"
Text="{Binding Identifier}" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
The above code works almost as expected - I get a label and checkbox for each title in Titles. However I need a way to ensure that only one title is checked - this is what I can't get to work.
In the CheckCommand I set a property (SelectedTitle) to the Identifier of the set in CheckedCommandParameter - works fine, however I need some way to compare the value of Identifier and SelectedTitle.
I've been trying to get this working using an IValueConverter, however I can't bind a value to the CommandParameter, I've also tried DataTriggers, however that didn't work either.
Update:
This is with the DataTriggers - it feels like the CheckBoxView isn't setting the IsChecked property
<control:CheckBoxView VerticalOptions="CenterAndExpand"
IsChecked="False"
CheckedCommand="{Binding BindingContext.CheckCommand, Source={x:Reference parent}}"
CheckedCommandParameter="{Binding Identifier}"
HorizontalOptions="Start"
OutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryBackgroundColor, Source={x:Reference parent}}"
CheckedOutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}"
CheckColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}">
<control:CheckBoxView.Triggers>
<DataTrigger TargetType="control:CheckBoxView"
Binding="{Binding BindingContext.SelectedTitle, Source={x:Reference parent}}"
Value="{Binding Identifier}">
<Setter Property="IsChecked"
Value="True"/>
</DataTrigger>
</control:CheckBoxView.Triggers>
Over time I'd been adding more and more complexity to this, which is usually an indication you're going in the wrong direction. In the end this was solved using standard MVVM. I added a new class called Salutation:
public class Salutation : INotifyPropertyChanged
{
private string identifier = null;
private string name = null;
private bool selected = false;
public event PropertyChangedEventHandler PropertyChanged;
[JsonProperty(PropertyName = "identifier", NullValueHandling = NullValueHandling.Ignore)]
public string Identifier
{
get
{
return identifier
}
set
{
identifier = value;
OnPropertyChanged();
}
}
[JsonProperty(PropertyName = "name", NullValueHandling = NullValueHandling.Ignore)]
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
[JsonProperty(PropertyName = "selected", NullValueHandling = NullValueHandling.Ignore)]
public bool Selected
{
get
{
return selected;
}
set
{
selected = value;
OnPropertyChanged();
}
}
public override int GetHashCode() => (Identifier).GetHashCode();
public bool Equals(Salutation other) => (Identifier) == (other?.Identifier);
public override string ToString()
{
return Identifier;
}
public override bool Equals(object obj)
{
if (!(obj is Salutation item))
{
return false;
}
return Equals(item);
}
public static bool operator ==(Salutation salutation1, Salutation salutation2)
{
if (ReferenceEquals(salutation1, salutation2))
{
return true;
}
if (ReferenceEquals(salutation1, null) || ReferenceEquals(salutation2, null))
{
return false;
}
return salutation1.Equals(salutation2);
}
public static bool operator !=(Salutation salutation1, Salutation salutation2)
{
return !(salutation1 == salutation2);
}
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I then added some Property and Collection changed event handlers:
Titles = new ObservableRangeCollection<Models.Salutation>();
Titles.CollectionChanged += Titles_CollectionChanged;
private bool handleEvent = true;
private void Titles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach (Models.Salutation item in e.NewItems)
item.PropertyChanged += Salutation_PropertyChanged;
if (e.OldItems != null)
foreach (Models.Salutation item in e.OldItems)
item.PropertyChanged -= Salutation_PropertyChanged;
}
private void Salutation_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(!handleEvent)
{
return;
}
if (e.PropertyName == "Selected")
{
handleEvent = false;
if (sender is Models.Salutation selectedSalutation)
{
if (selectedSalutation.Selected)
{
Reset(sender as Models.Salutation);
}
}
handleEvent = true;
}
}
The XAML:
<control:CheckBoxView VerticalOptions="CenterAndExpand"
IsChecked="{Binding Selected, Mode=TwoWay}"
HorizontalOptions="Start"
OutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryBackgroundColor, Source={x:Reference parent}}"
CheckedOutlineColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}"
CheckColor="{Binding BindingContext.Campaign.CampaignProfile.EntryTextColor, Source={x:Reference parent}}">
</control:CheckBoxView>
Much simpler!

Xamarin Binding Data from ItemSource to a Labels Text

i´ve got 2 things: second one is a list. when i´m clicking the "+1" button, a new element is createt with the name "list.Count"(so first one is 1, 2, 3, 4...). this is working very good. but for the first thing i wanna have only the vurrent value of the "Text"(from the current newest object in the list). But: i do not only want a counter. Maybe you can unterstand better if you check out my code:
<StackLayout>
<StackLayout>
<StackLayout Orientation="Horizontal" >
<Label Text="Erg:" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand" />
<Label Text="{Binding Path=Nummer, Source=Num}" HorizontalOptions="EndAndExpand" VerticalOptions="StartAndExpand" /> *
</StackLayout>
<Button Text="+1" Command="{Binding CountUpCommand}" /> **
<Button Text="Erg x Erg" />
</StackLayout>
<Label Text="--------------" />
<StackLayout>
<ListView ItemsSource="{Binding Nummer}"> **
<ListView.ItemTemplate>
<DataTemplate>
<TextCell
Text="{Binding Num}" **
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
so my question is at the Line with only one * at the end. the other 3 lines with two ** are working. what do i have to do to get the right Binding-Path or -Statment or something like this, to get the number shown up at labelsText ? i think there´s something missing like "itemsource"..
so here s the code from Itemsource (from My CounterVM):
public ObservableCollection<NumberViewModel> Nummer { get; private set; } = new ObservableCollection<NumberViewModel>();
public ICommand CountUpCommand { get; private set; }
public CounterViewModel()
{
CountUpCommand = new Command(CountUp);
}
public void CountUp()
{
int newNumber = Nummer.Count + 1;
Nummer.Add(new NumberViewModel { Num = newNumber});
}
and here the NumverVM, same code as in number.cs:
public class NumberViewModel : BaseViewModel
{
public int Num { get; set; }
}
You can add a Property inside your ViewModel to get the correct value:
public class CounterViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string LastItemsText => Nummer.Last()?.Num.ToString();
public ObservableCollection<NumberViewModel> Nummer { get; private set; } = new ObservableCollection<NumberViewModel>();
public ICommand CountUpCommand { get; private set; }
public CounterViewModel()
{
CountUpCommand = new Command(CountUp);
}
public void CountUp()
{
int newNumber = Nummer.Count + 1;
Nummer.Add(new NumberViewModel { Num = newNumber});
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LastItemsText));
}
}
And instead of
<Label Text="{Binding Path=Nummer, Source=Num}"
do this:
<Label Text="{Binding LastItemsText}"

Categories