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 .
Related
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.
Is there a way to bind the state of FontColor of Label in Xamarin so that it highlights the Label when Entry(textbox) gets focus?
<CollectionView x:Name="documentsListView" ItemsSource="{Binding DocumentsList}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0"
Margin="0,10,0,0"
Text="{Binding Name}" FontSize="Body"/>
<Entry Grid.Column="1" Grid.RowSpan="1"
IsPassword="False"
Keyboard="Numeric"
Placeholder="{Binding Count}"
Text="{Binding Count, Mode=OneWayToSource}"
Unfocused="{Binding OnTextboxLostFocus}"
Focused="{Binding OnTextboxGotFocus}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I need to highlight the element which is going to be changed when the user enters data to the Entry(textbox), and because the space between elements in CollectionView should be less so that I can show as much data per scroll as possible it might confuse the user which element exactly he is changing. I thought about passing a label as a parameter to the Events but could not find out how to bind the label.
Bind the textColor of label to a property in model and update that textColor when the entry focused/unfocused.
Here is an example I use:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
this.BindingContext = new DncMvvmPageModel();
}
}
public class DncMvvmPageModel
{
public ObservableCollection<Document> DocumentsList { get; set; }
public Command OnTextboxLostFocus { get; }
public Command OnTextboxGotFocus { get; }
public DncMvvmPageModel()
{
OnTextboxLostFocus = new Command(OnTextboxLostFocusMethod);
OnTextboxGotFocus = new Command(OnTextboxGotFocusMethod);
DocumentsList = new ObservableCollection<Document>();
DocumentsList.Add(new Document() {TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
DocumentsList.Add(new Document() { TextColor = Color.Gray });
}
public void OnTextboxLostFocusMethod(object sender) {
FocusEventArgs args = sender as FocusEventArgs;
Entry entry = args.VisualElement as Entry;
Document docu = entry.BindingContext as Document;
docu.TextColor = Color.Red;
}
public void OnTextboxGotFocusMethod(object sender)
{
FocusEventArgs args = sender as FocusEventArgs;
Entry entry = args.VisualElement as Entry;
Document docu = entry.BindingContext as Document;
docu.TextColor = Color.Blue;
}
}
public class Document : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
Color textColor;
public Color TextColor
{
set
{
if (textColor != value)
{
textColor = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TextColor"));
}
}
}
get
{
return textColor;
}
}
}
And in Xaml:
<CollectionView x:Name="documentsListView" ItemsSource="{Binding DocumentsList}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="0" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Column="0"
Margin="0,10,0,0"
Text="Name" FontSize="Body" TextColor="{Binding TextColor}"/>
<Entry Grid.Column="1" Grid.RowSpan="1"
IsPassword="False"
Keyboard="Numeric"
Placeholder="placeholder"
Text="Count">
<Entry.Behaviors>
<behaviors:EventToCommandBehavior EventName="Focused"
Command="{Binding BindingContext.OnTextboxGotFocus, Source={x:Reference MyPage}}" />
<behaviors:EventToCommandBehavior EventName="Unfocused"
Command="{Binding BindingContext.OnTextboxLostFocus, Source={x:Reference MyPage}}" />
</Entry.Behaviors>
</Entry>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Result:
Feel free to ask me any question if you have:).
I have a problem where I am using a collectionview and I am trying to get the bindings to work.
I have tried to set the Height, as I saw in other posts, by setting the miniumheight and heightrequest of every element involved, with no success. I have a listview with a textcell and that is the only way I can get this to work.
Model
[Table("Category")]
public class Category
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
}
ViewModel
public class CategoriesViewModel : BaseViewModel
{
public ObservableCollection<Category> Categories { get; }
public Command LoadCategoriesCommand { get; }
public Command AddCategoryCommand { get; }
public CategoriesViewModel()
{
Title = "Categories";
Categories = new ObservableCollection<Category>();
LoadCategoriesCommand = new Command(async () => await ExecuteLoadCategoriesCommand());
AddCategoryCommand = new Command(OnAddCategory);
}
async Task ExecuteLoadCategoriesCommand()
{
IsBusy = true;
try
{
Categories.Clear();
var categories = await DataStore.GetCategoriesAsync();
foreach (var cat in categories)
{
Categories.Add(cat);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
Debug.WriteLine(ex.Message);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
View
xmlns:local="clr-namespace:FreeScanner.ViewModels"
xmlns:model="clr-namespace:FreeScanner.Models"
x:Name="BrowseCategoriesPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Command="{Binding AddCategoryCommand}" />
</ContentPage.ToolbarItems>
<RefreshView x:DataType="local:CategoriesViewModel" Command="{Binding LoadCategoriesCommand}"
IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<CollectionView x:Name="CategoriesListView"
ItemsSource="{Binding Categories}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10" x:DataType="model:Category">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding Name}"
FontAttributes="Bold" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<!--<ListView ItemsSource="{Binding Categories}"
x:Name="CategoriesListView"
SelectionMode="None"
RowHeight="50">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Category">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>-->
</RefreshView>
When I debug and hover over the CollectView ItemSource Categories I do see the Count = 1 and I can verify that is correctly loading from the database.
=>Try this it's working well
public class CategoriesViewModel : BaseViewModel
{
public ObservableCollection<Category> Categories { get; set; } = new ObservableCollection<Category>();
public Command LoadCategoriesCommand { get; }
public Command AddCategoryCommand { get; }
public CategoriesViewModel()
{
Title = "Categories";
LoadCategoriesCommand = new Command(async () => await ExecuteLoadCategoriesCommand());
AddCategoryCommand = new Command(OnAddCategory);
}
async Task ExecuteLoadCategoriesCommand()
{
IsBusy = true;
try
{
Categories.Clear();
var categories = await DataStore.GetCategoriesAsync();
foreach (var cat in categories)
{
Categories.Add(new Category{Name=cat.Name});
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
Debug.WriteLine(ex.Message);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
=>This is my Xaml
<CollectionView x:Name="CourseList" IsVisible="{Binding IsVisibleCourse}" ItemsSource="{Binding Courses}" >
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="5" Spacing="-6">
<Frame BorderColor="LightGray" HeightRequest="{OnIdiom Phone=140,Tablet=210}" CornerRadius="10" HasShadow="False" Padding="5">
<StackLayout Padding="5" Orientation="Vertical" Spacing="5">
<ffimageloading:CachedImage x:Name="Pic" LoadingDelay="0"
Margin="0,5,0,0" WidthRequest="{OnIdiom Phone=60,Tablet=100}"
HeightRequest="{OnIdiom Phone=60,Tablet=100}" Source="{Binding ImageURL}"
HorizontalOptions="CenterAndExpand" VerticalOptions="Center"
BackgroundColor="Transparent" Aspect="Fill" >
<ffimageloading:CachedImage.Transformations>
<ffTransformations:CircleTransformation />
</ffimageloading:CachedImage.Transformations>
</ffimageloading:CachedImage>
<Label Text="{Binding Name}" MaxLines="2" Style="{StaticResource TextDefaultStyle}" TextColor="Black" HorizontalTextAlignment="Center" VerticalOptions="Fill"/>
<Label Text="{Binding Description}" MaxLines="2" Style="{StaticResource TextMicroStyle}" HorizontalTextAlignment="Center" VerticalOptions="Fill" FontAttributes="Bold"/>
</StackLayout>
<Frame.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Path=BindingContext.CourseListCommand, Source={x:Reference CourseList}}" CommandParameter="{Binding .}"/>
</Frame.GestureRecognizers>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
i'm trying to bind a command to a button inside a listView, but without success. I follow all other answers posted here, like this one:
Xamarin Forms Button Command binding inside a ListView
The actual result is that nothing happens. A thing that i notice is that visual studio, when i type after x:Reference suggests me just GridWebcam, like if it doesn't see other reference elements. What can i do?
Here my code:
<ContentPage
...
x:Name="WebcamList">
<ContentPage.Resources>
...
<ContentPage.Content>
<ListView ItemsSource="{Binding ListOfWebcam}"
SeparatorVisibility="None"
CachingStrategy="RecycleElement"
RowHeight="250"
VerticalOptions="FillAndExpand"
x:Name="ListWebcam">
<ListView.Header>
<StackLayout x:Name="HeaderStackLayout"
Padding="5,25,0,30"
Orientation="Horizontal"
HorizontalOptions="FillAndExpand">
<Label x:Name="LabelHeader"
Text="Webcam:"
FontSize="Large"
FontAttributes="Bold"
TextColor="{x:Static statics:Palette.PrimaryColor}"
VerticalOptions="Center"
HorizontalOptions="Start" Margin="10,0,0,0"/>
</StackLayout>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<controls:ExtendedViewCell IsEnabled="False">
<controls:ExtendedViewCell.View>
<Grid x:Name="GridWebcam">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame Grid.Column="1"
Grid.RowSpan="2"
CornerRadius="20"
BackgroundColor="{x:Static statics:Palette.PrimaryColor}"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
HasShadow="True"
Margin="5,10">
<StackLayout>
<Label Text="{Binding t_str_vid,Converter={StaticResource WebcamNameConverter}}"
FontSize="Medium"
TextColor="White"
FontAttributes="Bold"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
</Label>
<Label TextColor="White"
FontSize="Medium"
Text="{Binding direzione,Converter={StaticResource DirectionToStringConverter}}"/>
<StackLayout Orientation="Horizontal">
<ffimageloading:CachedImage LoadingPlaceholder="Rolling.gif"
DownsampleToViewSize="False"
VerticalOptions="FillAndExpand"
HorizontalOptions="StartAndExpand"
Source="{Binding image1}"/>
<iconize:IconButton Text="fas-play-circle"
FontSize="50"
HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
TextColor="White"
Command="{Binding BindingContext.OpenVideoWebcamCommand, Source={x:Reference WebcamList}}"
CommandParameter="{Binding .}"
BackgroundColor="Transparent"/>
</StackLayout>
</StackLayout>
</Frame>
</Grid>
</controls:ExtendedViewCell.View>
</controls:ExtendedViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
```
public class WebcamListViewModel : BaseViewModel
{
public ICommand OpenVideoWebcamCommand { set; get; }
private List<Webcam> _ListOfWebcam { get; set; }
public List<Webcam> ListOfWebcam
{
get { return _ListOfWebcam; }
set
{
_ListOfWebcam = value;
OnPropertyChanged();
}
}
private Task DownloadFramesTask;
CancellationTokenSource tokenSourceDownloadFrames = new CancellationTokenSource();
CancellationToken cancellationTokenDownloadFrames;
public WebcamListViewModel(INavigationService navigationService, IApiAutostradeManagerFactory apiAutostradeManagerFactory) : base(navigationService,apiAutostradeManagerFactory)
{
OpenVideoWebcamCommand = new Command<Webcam>(async (webcam) => {
await navigationService.NavigateAsync(Locator.WebcamVideoPopUpPage);
Messenger.Default.Send(new InfoWebcamVideoMessage(webcam.c_mpr, webcam.c_uuid, webcam.t_str_vid));
});
}
Well it could be related to this mysterious controls:ExtendedViewCell of yours :)
Also did you disable the ListView selection: <ListView ... SelectionMode="None" /> ?
As Roubachof said that I don't know if it is related to controls:ExtendedViewCell,please check if you have binding BindingContext, then you can take a look the following code:
<StackLayout>
<ListView x:Name="listview1" ItemsSource="{Binding persons}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Id}" />
<Label Text="{Binding name}" />
<Button
Command="{Binding BindingContext.command, Source={x:Reference listview1}}"
CommandParameter="{Binding Id}"
Text="Delete item" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page1 : ContentPage
{
public ObservableCollection<person> persons { get; set; }
public RelayCommand1 command { get; set; }
public Page1 ()
{
InitializeComponent ();
persons = new ObservableCollection<person>();
for(int i =0;i<20;i++)
{
person p = new person()
{
Id = i,
name = "cherry" + i
};
persons.Add(p);
command = new RelayCommand1(obj => method1((int)obj));
}
this.BindingContext = this;
}
public void method1(int Id)
{
persons.RemoveAt(Id);
//IEnumerable<person> list = persons.Where(x => x.Id == Id);
//foreach (person m in list)
//{
//}
}
}
public class person
{
public int Id { get; set; }
public string name { get; set; }
}
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.