How when reacting to a selected item in collection view:
<CollectionView ItemsSource="{Binding items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<HorizontalStackLayout x:Name="horizontalStackLayout ">
<Label
Text="{Binding title}" />
</HorizontalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
and the code-behind:
private async void OnItemSelected(object sender, SelectionChangedEventArgs e)
{
// ???
// this returns null:
// HorizontalStackLayout selectedView = (HorizontalStackLayout) ((CollectionView) sender).FindByName("horizontalStackLayout");
}
to find the HorizontalStackLayout and change its attribute?
1
x:Name
attribute inside a DataTemplate
does not directly associate the named element with the CollectionView’s FindByName
method. Each item in the CollectionView
has its own DataTemplate
. So your approach won’t work.
If Manipulating properties of HorizontalStackLayout is you goal, Then you need to find another way.
One Way is to create new properties in you model class and bind them to the property you want to manipulate when selection changed.
public class Item
{
public string title;
public Color ItemBackgroundColor = Colors.Red; // new property in model class
}
in xaml bind it
<DataTemplate>
<HorizontalStackLayout BackgroundColor={Binding ItemBackgroundColor} x:Name="horizontalStackLayout ">
<Label
Text="{Binding title}" />
</HorizontalStackLayout>
</DataTemplate>
Now you can set the property in SelectionChanged event
private async void OnItemSelected(object sender, SelectionChangedEventArgs e)
{
foreach(var selectedItem in e.CurrentSelection)
{
if(selectedItem is Item _item)
{
_item.ItemBackgroundColor = Colors.Green;
}
}
}
Note: Make sure changes in model properties are notified to view.
One of the easier ways to react to a selection in CollectionView
is to bind the SelectedItem
property. I kludged a CollectionView
onto the MAUI default as a minimal example of doing this.
<CollectionView
MinimumHeightRequest="400"
BackgroundColor="Azure"
SelectionMode="Single"
ItemsSource="{Binding items}"
SelectedItem="{Binding SelectedItem}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="1" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame
Margin="10"
Padding="10"
BackgroundColor="White"
CornerRadius="10"
HasShadow="True"
InputTransparent="true">
<HorizontalStackLayout>
<Label
Text="{Binding title}"
FontSize="16"
FontAttributes="Bold"
TextColor="Black" />
</HorizontalStackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Binding context for MainPage
class MainPageBindingContext : INotifyPropertyChanged
{
public ObservableCollection<Item> items { get; } = new ObservableCollection<Item>
{
new Item{title = "Item A"},
new Item{title = "Item B"},
};
// Because we're in "single" mode, we bind SelectedItem, not SelectedItems
public Item? SelectedItem
{
get => _selectedItem;
set
{
if (!Equals(_selectedItem, value))
{
_selectedItem = value;
OnPropertyChanged();
// Quick and dirty. You will want to improve on this.
foreach (var item in items)
{
if(ReferenceEquals(_selectedItem, item))
{
item.title = $"{item.title} SELECTED";
}
else
{
item.title = item.title.Replace(" SELECTED", string.Empty);
}
}
}
}
}
Item? _selectedItem = default;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler? PropertyChanged;
}
Binding context for Item
class Item : INotifyPropertyChanged
{
public string title
{
get => _title;
set
{
if (!Equals(_title, value))
{
_title = value;
OnPropertyChanged();
}
}
}
string _title = "{New Item}";
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler? PropertyChanged;
}
1