I have problem with IsVisible. I want that after select item in ListView StackLayout shows up with label but item selected not works. Of course i have rest of code. IsVisible = false its working so i have only problem with show info. I Have tried almsot everything changing code but not works fine.
public new string Title { get; set; }
public string Info { get; set; }
public int Timer { get; set; }
private bool _isVisible = false;
public new bool IsVisible
{
get => _isVisible;
set => Set(ref _isVisible, value);
}
private void Set(ref bool _isVisible, bool value)
{
return;
}
private void DO ()
{
Task.Factory.StartNew(() =>
{
ChallengeList.ItemsSource = new List<MainPage>
{
new MainPage {Title = "Cuipka", Info="Cipka"},
new MainPage {Title = "Cuipka", Info="Cipka"},
new MainPage {Title = "Cuipka", Info="Cipka"},
new MainPage {Title = "Cuipka", Info="Cipka"},
};
});
}
private void ChallengeList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
// if (e.SelectedItem == null)
// return;
if (e.SelectedItem is MainPage viewModel)
{
viewModel.IsVisible = true;
}
// ChallengeList.SelectedItem = null;
}
XAML:
<ListView x:Name="ChallengeList" SeparatorColor="#3d122c" HasUnevenRows="True"
ItemSelected="ChallengeList_ItemSelected" RelativeLayout.YConstraint="{ConstraintExpression ElementName=Lab, Constant=0,Factor=1,Property=Height,Type=RelativeToView}"
RelativeLayout.HeightConstraint="{ConstraintExpression Property=Height,Factor=0.8,Type=RelativeToParent}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" BackgroundColor="#40FFFFFF" Padding="10">
<StackLayout HorizontalOptions="CenterAndExpand">
<Label Text="{Binding Title}" TextColor="#ff3f50" FontSize="17" FontAttributes="Bold" HorizontalOptions="Center"/>
<StackLayout HorizontalOptions="CenterAndExpand" IsVisible="{Binding IsVisible}" x:Name="More" Padding="5">
<Label Text="sdfghjkhgfdsfghjkljhgfdsadfghjkljhgfdsaSDFGHJKJHGFDSAsdfghjkhgfds" TextColor="#ff3f50" FontSize="17" FontAttributes="Bold" HorizontalOptions="Center"
LineBreakMode="WordWrap"/>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm not quite sure what you are doing with the Set function, but I would normally set up properties in my viewmodel like this:
private bool _isVisible;
public bool IsVisible
{
get { return _isVisible; }
set { _isVisible = value; OnPropertyChanged(nameof(IsVisible)); }
}
private bool _isVisible = false;
public bool IsVisible
{
get
{
return _isVisible
}
set
{
Set(() => IsVisible, ref _isVisible, value);
}
}
This worked for me !
Related
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;
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("..");
}
I have a problem with Binding Data to a Picker in Xamarin Forms and hpe somebody can help me. I have a ContentPage which holds a picker. The Itemssource for that Picker is queried async from a web service. Also, the view (or rather, the ViewModel) is passed a seleted item. For whatever reason, setting the itemssource breaks the binding of the SelectedItem property.
Here is my ViewModel -
public class ExerciseViewModel:BaseViewModel
{
private ApiServices apiService = new ApiServices();
private Exercise exercise;
public Exercise Exercise
{
get => exercise;
set
{
exercise = value;
OnPropertyChanged();
}
}
private List<ExerciseCategory> exerciseCategories = new List<ExerciseCategory>();
public List<ExerciseCategory> ExerciseCategories
{
get => exerciseCategories;
set
{
exerciseCategories = value;
OnPropertyChanged();
}
}
public ExerciseViewModel()
{
GetCategoriesCommand.Execute(null);
Exercise = new Exercise() { Name = "Neue Übung", Category = ExerciseCategories.FirstOrDefault() };
}
public ExerciseViewModel(Exercise ex)
{
Exercise = ex;
GetCategoriesCommand.Execute(null);
}
public ICommand GetCategoriesCommand
{
get
{
return new Command(async () =>
{
ExerciseCategories = await apiService.GetExerciseCategories();
});
}
}
public ICommand AddExerciseCommand
{
get
{
return new Command(async () =>
{
Exercise.Id = await apiService.AddExercise(Exercise);
});
}
}
}
This is the Entity in question - the necessary operators are overloaded, INotifyPropertyChanged is implemented in the BaseClass -
public class ExerciseCategory:BaseClass
{
private string name;
private int id;
[Key]
public int Id
{
get => id;
set
{
id = value;
OnPropertyChanged();
}
}
public string Name
{
get => name;
set
{
name = value;
OnPropertyChanged();
}
}
public override bool Equals(object obj)
{
var other = (obj as ExerciseCategory);
if (other is null)
return false;
return this == other;
}
public static bool operator !=(ExerciseCategory e1, ExerciseCategory e2)
{
return !(e1 == e2);
}
public static bool operator ==(ExerciseCategory e1, ExerciseCategory e2)
{
if (e1.Id == e2.Id)
if (e1.Name == e2.Name)
return true;
return false;
}
}
This is the page's CodeBehind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NewExercisePage : ContentPage
{
public NewExercisePage(ExerciseViewModel viewModel, bool controlsLocked = false)
{
try
{
this.BindingContext = viewModel;
InitializeComponent();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void UpdateWebView(object sender, TextChangedEventArgs e)
{
Uri uriResult;
bool result = Uri.TryCreate(e.NewTextValue, UriKind.Absolute, out uriResult)
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
if (result)
exerciseVideoViewer.Source = e.NewTextValue;
}
}
Finally, here is the XAML -
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="PerformanceTM.Views.NewExercisePage">
<ContentPage.Content>
<StackLayout>
<WebView x:Name="exerciseVideoViewer" HeightRequest="200" WidthRequest="200"></WebView>
<Grid x:Name="LayoutGrid">
<Label Text="Kategorie" Grid.Column="0" Grid.Row="0"/>
<Picker x:Name="CategoryPicker" ItemsSource="{Binding ExerciseCategories}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding Exercise.Category}" Grid.Column="1" Grid.Row="0"/>
<Label Text="Name" Grid.Column="0" Grid.Row="1"/>
<Entry Text="{Binding Exercise.Name}" Grid.Column="1" Grid.Row="1"/>
<Label Text="Video URL" Grid.Column="0" Grid.Row="2"/>
<Entry Text="{Binding Exercise.VideoUrl}" Grid.Column="1" Grid.Row="2" TextChanged="UpdateWebView"/>
<Label Text="Beschreibung" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="3"/>
<Editor Text="{Binding Exercise.Description}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4"/>
</Grid>
<Button Text="Speichern" Command="{Binding AddExerciseCommand}"/>
<Label Text="{Binding Exercise.Category.Name}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
When I select an ExerciseCategory from the CategoryPicker, Binding works. However, the ExerciseCategory that is prvided to the ViewModel via the "ex" parameter does not result in the proper Category being selected in the Picker.
Since the Categories are not (necessarily) present by the time I call InitializeComponent, I suspect this disconnection between the Exercise.Category and the Picker.SelectedItem comes from that late binding. Still, I cannot really figure out how to fix that. Any help is appreciated.
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!
public class Zicker : INotifyPropertyChanged
{
public class MyClass
{
public string HeyName { get; set; }
public string HeySurname { get; set; }
public int HeyAge { get; set; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string name = null)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<MyClass> _yourList = new ObservableCollection<MyClass>();
public ObservableCollection<MyClass> YourList
{
get
{
return _yourList;
}
set
{
_yourList = value;
RaisePropertyChanged("YourList");
RaisePropertyChanged("BindMeLabel");
}
}
public int BindMeLabel
{
get { return _yourList.Sum(a => a.HeyAge); }
}
public void WonCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RaisePropertyChanged("BindMeLabel");
}
public List<string> heresamplenames = new List<string> { "Mohamed", "Zaran", "Ivan" };
public List<string> heresamplesurnames = new List<string> { "Pakou", "Simmone", "Zagoev" };
public List<int> heresampleages = new List<int> { 17,33,50 };
public Zicker()
{
ObservableCollection<MyClass> vs = new ObservableCollection<MyClass>();
for (int i = 0; i < 3; i++)
{ vs.Add(new MyClass { HeyName = heresamplenames[i], HeySurname = heresamplesurnames[i], HeyAge = heresampleages[i] }); }
YourList = vs; YourList.CollectionChanged += WonCollectionChanged;
}
}
<ContentPage.Content>
<StackLayout Orientation="Vertical" HorizontalOptions="Center" VerticalOptions="Center">
<ContentView HorizontalOptions="Fill" VerticalOptions="Fill">
<ListView HorizontalOptions="Center" VerticalOptions="Center" HasUnevenRows="True" ItemsSource="{Binding YourList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="{Binding Path=HeyName}" Grid.Column="0" FontSize="12" TextColor="Black"></Label>
<Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="{Binding Path=HeySurname}" FontSize="12" TextColor="Black" Grid.Column="1"/>
<Entry HorizontalTextAlignment="Center" VerticalOptions="Center" Text="{Binding Path=HeyAge}" FontSize="12" Keyboard="Numeric" TextColor="Black" Grid.Column="2"/>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
<Label Text="{Binding BindMeLabel}" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" FontSize="40" TextColor="Black"></Label>
</StackLayout>
</ContentPage.Content>
public MainPage()
{
InitializeComponent();
BindingContext = new Zicker();
}
My Problem: In this List, there are three names, surnames, and ages. At the bottom, there is also a label which should be shown as the sum of Ages collection.
When the UI is starting, Label is working well. But, if I try to change any Ages entries, there is a big problem with the binding label.
I want to use MVVM structure but due to this problem, label binding is working just start up.
If you are updating the HeyName property, binding is not updating because the class MyClass does not implement INotifyPropertyChanged.
Try to replace the MyClass class with this code:
public class MyClass : INotifyPropertyChanged
{
private string name;
private string surname;
private int age;
public string HeyName
{
get => name;
set
{
name = value;
RaisePropertyChanged("HeyName");
}
}
public string HeySurname
{
get => surname;
set
{
surname = value;
RaisePropertyChanged("HeySurname");
}
}
public int HeyAge
{
get => age;
set
{
age = value;
RaisePropertyChanged("HeyAge");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string name = null)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
EDIT:
Sorry, the CollectionChanged is not called when you update the HeyAge property, because it is called only if the collection is changed, but not when a property of an item in the collection changes.
Try to add the OnAgeChanged event into the class MyClass and call it when the HeyAge property change:
public class MyClass : INotifyPropertyChanged
{
public event EventHandler OnAgeChanged;
public int HeyAge
{
get => age;
set
{
age = value;
RaisePropertyChanged("HeyAge");
OnAgeChanged?.Invoke(this, EventArgs.Empty);
}
}
...
...
Then, when you add a new MyClass object into the collection, register the event in the ViewModel like this:
public Zicker()
{
ObservableCollection<MyClass> vs = new ObservableCollection<MyClass>();
for (int i = 0; i < 3; i++)
{
var test = new MyClass()
{
HeyName = heresamplenames[i],
HeySurname = heresamplesurnames[i],
HeyAge = heresampleages[i],
};
test.OnAgeChanged += Test_OnAgeChanged;
vs.Add(test);
}
YourList = vs;
YourList.CollectionChanged += WonCollectionChanged;
}
private void Test_OnAgeChanged(object sender, EventArgs e)
{
RaisePropertyChanged("BindMeLabel");
}
Note that the WonCollectionChanged it's not necessary any more.
Note also that the variable vs is not needed, you can work directly into the YourList object instead.