I have code that is a sample bowling recap where 3 games are entered and are summarized in a Series column. The problem that I am experiencing is the Series column is not getting updated as I anticipate. Obviously, I am doing something wrong but can not see what I am doing wrong. Below is the code that supports the application:
<?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="HoriView.MainPage">
<StackLayout>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, Bowlers!"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Bowling Recap Snippet"
FontSize="18"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
<ScrollView>
<HorizontalStackLayout HorizontalOptions="Center">
<CollectionView SelectionMode="Single" ItemsSource="{Binding scores}" ItemsLayout="HorizontalList">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Game}" FontSize="Medium" WidthRequest="83" VerticalOptions="Center" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="End"></Label>
<Entry Grid.Row="1" Text="{Binding Score}" TextChanged="UpdateGame" Keyboard="Numeric" ReturnType="Next" Placeholder="Game" WidthRequest="83" FontSize="Medium" ></Entry>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</HorizontalStackLayout>
</ScrollView>
</StackLayout>
</ContentPage>
using System.Collections.ObjectModel;
namespace HoriView;
public partial class MainPage : ContentPage
{
public ObservableCollection<Games> scores { get; set; }
string header = "Game";
public MainPage()
{
InitializeComponent();
scores= new ObservableCollection<Games>();
for(int i=0; i<4; i++)
{
if (i == 3)
{
header = "Series";
}
else
{
header = "Game" + (i+1).ToString();
}
scores.Add(new Games()
{
Game=header,
Score=0
});
}
BindingContext = this;
}
public void UpdateGame(object sender, TextChangedEventArgs e)
{
scores[scores.Count-1].Score = 0;
for(int i=0; i< scores.Count-1; i++)
{
scores[scores.Count - 1].Score += scores[i].Score;
}
}
}
public class Games
{
public string Game { get; set; }
public int Score { get; set; }
}
Any help would be appreciated. Thanks.
Your Games class doesn't implement the INotifyPropertyChanged interface.
You can either implement that manually:
public class Games : INotifyPropertyChanged
{
private string _game;
public string Game
{
get => _game;
set
{
if(_game == value) return;
_game = value;
OnPropertyChanged();
}
}
private int _score;
public int Score
{
get => _score;
set
{
if(_score == value) return;
_score = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Alternatively, you could also inherit from ObservableObject from the MVVM Community Toolkit and then simply write the following using Source Generators:
public partial class Games : ObservableObject
{
[ObservableProperty]
private string _game;
[ObservableProperty]
private int _score;
}
In this case, you only define the backing fields and the Source Generators will create the uppercase, observable properties Game and Score for you.
Related
I am binding ObservableCollection with CollectionView.
<CollectionView ItemsSource="{Binding LeftExercises}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:ExerciseModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="{Binding SetNumber}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
<Label Grid.Column="1" Grid.Row="0" Text="{Binding Weight}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
<Label Grid.Column="2" Grid.Row="0" Text="{Binding Reps}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
private ObservableCollection<ExerciseModel> _leftExercises;
public ObservableCollection<ExerciseModel> LeftExercises
{
get => _leftExercises;
set
{
if (_leftExercises != value)
{
_leftExercises = value;
OnPropertyChanged(nameof(LeftExercises));
}
}
}
When I add a new object to the Collection, it will reflect in my UI but whenever I try to update the value of any object, it will not reflect.
This is my model
public class ExerciseModel
{
public int SetNumber { get; set; }
public decimal Weight { get; set; }
public int Reps { get; set; }
public ExerciseType ExerciseType { get; set; }
public Side Side { get; set; }
}
I am incrementing the Reps (update Reps property) from the below command.
private Command _dummyLeftIncreaseRepsCommand;
public Command dummyLeftIncreaseRepsCommand
{
get
{
return _dummyLeftIncreaseRepsCommand ??= new Command(() =>
{
ExerciseModel lastObj = LeftExercises.Last(x => x.Side == SharedVM.ActiveSide);
lastObj.Reps += 1;
});
}
}
Your ExerciseModel class needs to implement INotifyPropertyChanged.
To do so, simply add the interface name to your class code, like this:
class MyClass : INotifyPropertyChanged
Alternatively, you can set the ItemsSource for the object again. Doing so will update the visual as well. You can do this at runtime like this:
XAML file:
<CollectionView x:Name="MyView">[...]
Code in background:
MyView.ItemsSource = MyCollectionVar;
I advise you to use CommunityToolkit.MVVM and CommunityToolkit.MAUI, when you try to bind commands, observable properties and collections to your XAML.
The benefit of this will be that you wont have to write walls of code, and spend hours to figure out why it is not working.
It saves you time, that you can put in something useful, instead of dealing with boilerplate code.
What I am trying to implement: I want to have four image buttons that if I click on one button then there will be a little check mark displayed showing that I select the check mark. And if I click on another button, then the check mark of the previous button will disappear and my newly selected button will show a little check mark.
The code I currently have:
<Grid HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand"
Margin="20, 80">
<Grid.RowDefinitions>
<RowDefinition Height="120" />
<RowDefinition Height="120" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<ImageButton Source="select_payment_placeholder.png"
Grid.Row="0"
Grid.Column="0" />
<ImageButton Source="select_payment_placeholder.png"
Grid.Row="0"
Grid.Column="1" />
<ImageButton Source="select_payment_placeholder.png"
Grid.Row="1"
Grid.Column="0" />
<ImageButton Source="select_payment_placeholder.png"
Grid.Row="1"
Grid.Column="1" />
</Grid>
This is the demo of the effect that I am trying to implement.
Here I used the Xamarin.Forms CollectionView as the parent layout .
Create the CheckItem :
public class CheckItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string ContentImage { set; get; }
private bool isShow;
public bool IsShow
{
set
{
if (isShow != value)
{
isShow = value;
OnPropertyChanged("IsShow");
}
}
get
{
return isShow;
}
}
public ICommand TapCommand
{
get
{
return new Command((e) =>
{
var item = (e as CheckItem);
// logic on item
if (item.isShow)
{
PageMain.checkItems[0].IsShow = false;
PageMain.checkItems.Remove(PageMain.checkItems[0]);
PageMain.checkItems.Add(item);
}
else
{
item.IsShow = true;
if (PageMain.checkItems.Count == 0)
{
PageMain.checkItems.Add(item);
}
else
{
PageMain.checkItems[0].IsShow = false;
PageMain.checkItems.Remove(PageMain.checkItems[0]);
PageMain.checkItems.Add(item);
}
}
});
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then the CheckModel can be set a list data with four item :
public class CheckModel
{
public List<CheckItem> CheckItems { set; get; }
public CheckModel()
{
CheckItems = new List<CheckItem>();
CheckItems.Add( new CheckItem() { ContentImage = "XamarinLogo.png", IsShow = false});
CheckItems.Add( new CheckItem() { ContentImage = "XamarinLogo.png", IsShow = false});
CheckItems.Add( new CheckItem() { ContentImage = "XamarinLogo.png", IsShow = false});
CheckItems.Add( new CheckItem() { ContentImage = "XamarinLogo.png", IsShow = false});
}
}
In the ContentPage , the Xaml code contains a CollectionView and which contains the ImageButton and MarkIcon . And the MarkIcon default is invisible .
<?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="ImageButonSingleCheck.PageMain">
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="Start"
HorizontalOptions="Start" />
<CollectionView x:Name="MyCollectionView"
ItemsSource="{Binding CheckItems}"
SelectionMode="None"
SelectionChanged="CollectionView_SelectionChanged">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<RelativeLayout x:Name="Item" HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" HeightRequest="120">
<ImageButton x:Name="MyImageButton"
BackgroundColor="LightYellow"
Source="{Binding ContentImage}"
Command="{Binding TapCommand}"
CommandParameter="{Binding Source={x:Reference Item}, Path=BindingContext}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=.15,Constant=0}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=1,Constant=0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=.8,Constant=0}" />
<Image x:Name="CheckImage"
Source="Tick.png"
BackgroundColor="AliceBlue"
IsVisible="{Binding IsShow}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView,ElementName=MyImageButton,Property=Y,Factor=1,Constant=5}"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,ElementName=MyImageButton,Property=X,Factor=1,Constant=150}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=0,Constant=40}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=0,Constant=40}" />
</RelativeLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
In ContentPage , invoked the CheckModel :
public partial class PageMain : ContentPage
{
CheckModel checkModel;
public static List<CheckItem> checkItems { set; get; }
public PageMain()
{
InitializeComponent();
checkModel = new CheckModel();
BindingContext = checkModel;
checkItems = new List<CheckItem>();
}
}
The effect :
Here is the Sample .
Why gives me this code "Don't know about Xamarin.Forms.Color" exception?
Exception detail:
System.AggregateException Zpráva=One or more errors occurred. (Don't know about Xamarin.Forms.Color) Zdroj= StackTrace: at System.Threading.Tasks.Task.ThrowIfExceptional (System.Boolean includeTaskCanceledExceptions) [0x00011] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2027 at System.Threading.Tasks.Task.Wait (System.Int32 millisecondsTimeout, System.Threading.CancellationToken cancellationToken) [0x00043] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2759 at System.Threading.Tasks.Task.Wait () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2625 at Notes.Data.NoteDatabase..ctor (System.String dbPath) [0x00015] in C:\Users\foksak\source\repos\Notes\Notes\Notes\Data\NoteDatabase.cs:15 at Notes.App.get_Database () [0x0000e] in C:\Users\foksak\source\repos\Notes\Notes\Notes\App.xaml.cs:18 at Notes.NotesPage.OnAppearing () [0x0001b] in C:\Users\foksak\source\repos\Notes\Notes\Notes\NotesPage.xaml.cs:19 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.39(intptr,intptr
at (wrapper native-to-managed) Android.Runtime.DynamicMethodNameCounter.39(intptr,intptr)
Note model:
namespace Notes.Models
{
public class Note
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Text { get; set; }
public string Title { get; set; }
public string Picture { get; set; }
public string Colorr { get; set; }
public Color Colorrr { get; set; }
}
}
c# of data input page:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NoteEntryPage : ContentPage
{
Dictionary<string, string> dic = new Dictionary<string, string>() {
{"Color1","#d8daea"},
{"Color2", "#ecd67b"},
{"Color3", "#3f6018"},
{"Color4", "#ff8847" }
};
public NoteEntryPage()
{
InitializeComponent();
}
async void OnSaveButtonClicked(object sender, EventArgs e)
{
var note = (Note)BindingContext;
string x = dic[note.Colorr];
note.Colorrr = Color.FromHex(x);
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}
async void OnDeleteButtonClicked(object sender, EventArgs e)
{
var note = (Note)BindingContext;
await App.Database.DeleteNoteAsync(note);
await Navigation.PopAsync();
}
}
Input works. The problem was also in the case when i manualy wrote "#ff8847" instead of x.
Data is desplayer in page with code below.
Data seed in c#:
protected override async void OnAppearing()
{
base.OnAppearing();
listView.ItemsSource = await App.Database.GetNotesAsync();
}
Xaml:
<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="Notes.NotesPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="+"
Clicked="OnNoteAddedClicked" />
</ContentPage.ToolbarItems>
<ListView x:Name="listView"
Margin="20" RowHeight="80"
ItemSelected="OnListViewItemSelected">
<ListView.ItemTemplate>
<DataTemplate >
<ViewCell>
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="Fill">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<StackLayout Orientation="Horizontal" Grid.Column="1" Grid.Row="0">
<Label Text="{Binding Title}" FontSize="22" FontAttributes="Bold" />
</StackLayout>
<Label Text="{Binding Text}" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2"/>
<Frame CornerRadius="5" HasShadow="true" Grid.RowSpan="2" BackgroundColor="{Binding Colorrr}" Margin="7" WidthRequest="35">
<Image Source="{Binding Picture}" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="30"/>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
You can't have a Color field in your sqlite db, so ignore it.
namespace Notes.Models
{
public class Note
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; }
public string Text { get; set; }
public string Title { get; set; }
public string Picture { get; set; }
public string Colorr { get; set; }
[Ignore]
public Color Colorrr { get; set; }
}
}
You also have to initialize your Colorrr field before binding it to your view like this:
note.Colorrr = note.Colorr.FromHex(x);
Then name your frame to update the color, and remove the binding:
<Frame x:Name="ColorFrame" CornerRadius="5" HasShadow="true" Grid.RowSpan="2" Margin="7" WidthRequest="35">
<Image Source="{Binding Picture}" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="30"/>
</Frame>
And update it in your save method:
async void OnSaveButtonClicked(object sender, EventArgs e)
{
var note = (Note)BindingContext;
string x = dic[note.Colorr];
ColorFrame.BackgroundColor = Color.FromHex(x);
await App.Database.SaveNoteAsync(note);
await Navigation.PopAsync();
}
Then it should work as expected.
Another cleaner path: use the MVVM pattern and create a NoteViewModel implementing INotifyPropertyChanged that will wrap your Note object.
What I'm trying to implement is a message chat bubble wherein you tap the control/message and the message would expand showing details of the date and the seen / sent status below. I do have a DataTemplate Selector for the different controls I have for the Sender and the Receiver.
My problem is changing the Height of the message in the ListView. I tried implementing the Binding of the RowDefinition to whatever the Height variable is in my Message class (the class that holds info regarding the message). Although the height was updated, it didn't reflect on the ListView. I've scoured over the internet for existing chat UI templates but I think most of them are paid. Hence, I'm trying to do follow Change WPF DataTemplate for ListBox item if selected. But for Xamarin, there's no ListBoxItem as there's only a ListView.
On a further note, I am working on Android and iOS. An example that is cross-platform to solve this will be greatly appreciated. Below are parts of my code.
datatemplate.cs
class MessageTemplateSelector : DataTemplateSelector
{
public MessageTemplateSelector()
{
ReceiverDataTemplate = new DataTemplate(typeof(MessageReceiver));
SenderDataTemplate = new DataTemplate(typeof(MessageSender));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var message = item as Message;
if (message == null)
return null;
return message.isSender ? ReceiverDataTemplate : SenderDataTemplate;
}
private readonly DataTemplate ReceiverDataTemplate;
private readonly DataTemplate SenderDataTemplate;
}
MessageSender.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.layout.MessageSender">
<ViewCell.View>
<Grid HorizontalOptions="EndAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Path=Height}"/>
<RowDefinition Height="*" />
<RowDefinition Height="{Binding Path=Height}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Path=timestamp}" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="{Binding Path=Selected}"/>
<Frame Padding="0" CornerRadius="20" Grid.Column="1" Grid.Row="1" HorizontalOptions="EndAndExpand" >
<Grid BackgroundColor="White" VerticalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{Binding Path=text}" VerticalOptions="FillAndExpand" Margin="15,10"/>
</Grid>
</Frame>
<Label Text="Seen" Grid.Column="1" Grid.Row="2" HorizontalOptions="EndAndExpand" IsVisible="{Binding Path=Selected}"/>
</Grid>
</ViewCell.View>
</ViewCell>
Message.cs
class Message
{
public bool isSender { get; set; }
public sbyte status { get; set; }
public string text { get; set; }
public string timestamp { get; set; }
public Message(bool isSender, sbyte status, string text, string timestamp)
{
this.isSender = isSender;
this.status = status;
this.text = text;
this.timestamp = timestamp;
}
public sbyte height = 0;
public sbyte Height { get { return height; } set { height = value; } }
bool selected = false;
public bool Selected
{
get { return selected; }
set { selected = value;if (value) { Height = 25; } else { Height = 0; } }
}
}
Show mainpage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Project.model"
x:Class="Project.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector"></local:MessageTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="conversation"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Message}"
HasUnevenRows="True"
SeparatorVisibility="None"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
IsPullToRefreshEnabled="true"
ItemTapped="Conversation_ItemTapped"
Refreshing="Conversation_Refreshing">
</ListView>
</ContentPage>
MainPage.cs
private void Conversation_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null) return;
Message selectedItem = (Message)e.Item;
Log.Debug("ItemTap","Height before:" + selectedItem.Height);
if (selectedItem.Selected) { ((ListView)sender).SelectedItem = null; selectedItem.Selected = false; }
else { selectedItem.Selected = true; }
Log.Debug("ItemTap", "Height after:" + selectedItem.Height);
}
This is a screenshot of my log that is present in the ItemTapped event in my ListView. As you can see, the height updates but it's not reflecting on the ListView.
I want to create Xamarin search page with using MVVM. I have created a logic in my MainWindowViewModel which has to update my ListView once user entered a character into the searchar. But i have such result: In some reason UI is not updating
I do not know what i done wrong.
And also i would like to invoke ExecuteSearchCommand asynchronysly, and i will approsiate if you show how to implement it correctly.
Thanks.
<ContentPage.BindingContext>
<ViewModels:MainWindowViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<SearchBar SearchCommand="{Binding SearchCommand}"
Text="{Binding EnteredText}"
/>
<Label Text="{Binding EnteredText}"></Label>
<ListView x:Name="lstContatos" ItemsSource="{Binding MyList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Grid MinimumHeightRequest="80" HeightRequest="120" HorizontalOptions="FillAndExpand" >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Text="{Binding PhrasalVerb}"/>
<Button Text="Delete" Grid.Column="1" BackgroundColor="Black" HeightRequest="30" WidthRequest="40" IsVisible="True"/>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
This is my ViewModel that binds to the View
public string EnteredText
{
get { return enteredText; }
set
{
enteredText = value;
this.SearchCommand.Execute(null);
OnPropertyChanged(nameof(EnteredText));
}
}
void ExecuteSearchCommand(object parameter)
{
if (enteredText.Length>=1)
{
MyList = new ObservableCollection<PhV_Get>(phrasalVerbGet
.Where(x => x.PhrasalVerb.ToLower()
.Contains(enteredText.ToLower())).ToList());
}
else
{
MyList = phrasalVerbGet;
}
}
public ObservableCollection<PhV_Get> MyList
{
set
{
phrasalVerbGet = value;
OnPropertyChanged(nameof(MyList));
}
get
{
return phrasalVerbGet;
}
}
public Command SearchCommand {
get
{
return new Command(ExecuteSearchCommand,
CanExecuteSeachCommand);
}
}
public bool CanExecuteSeachCommand(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
The line MyList = new ObservableCollection in ExecuteSearchCommand is a no-no. Your listview is bound to the old ObservableCollection - when you create a new one, then you break that binding and the view will not update because it doesn't know anything about the new one.
When you need to change the contents of an observable collection, do it like this (in pseudocode):
MyList.Clear();
foreach (thing in myListOfThings)
{
MyList.Items.Add(thing);
}
That way you are updating the collection that the ListView is bound to, and the ListView will see the changes.