0

Inside the DataTemplate of a CollectionView I have modifiable UI-Elements (for example Checkbox or Entry) bound to the underlying objects properties. Whenever the user modifies an UI-Element inside the CollectionView, I want to know which object has been modified.

My first approach was to add an optional value to the class definition public bool? modified { get; set; } and update this property in the ItemSelection-Callback. Yet, the Item-Selection event will not get triggered when the user clicks on an UI-Element inside the CollectionView.

What would be a better approach to achieve this goal?

<CollectionView Margin="0,5"
                ItemsSource="{Binding digitalInputs}"
                SelectionMode="Single"
                SelectionChanged="OnSelectDigitalInput">
  [...]
  <Entry Text="{Binding pName}"/>
  [...]
</CollectionView>
public class DigitalOutput
{
    public string pName { get; set; }
    public bool? modified { get; set; }
    [...]
}

public partial class DigitalView : ContentPage
{
  public ObservableCollection<DigitalOutput> digitalOutputs { get; }
  [...]
  void OnSelectDigitalOutput(System.Object sender, Microsoft.Maui.Controls.SelectionChangedEventArgs e)
  {
        (e.CurrentSelection.FirstOrDefault() as DigitalOutput).modified = true;
  }
}
fonzane
  • 368
  • 1
  • 4
  • 16
  • 2
    This is what INotifyPropertyChanged is for – Jason May 02 '23 at 14:11
  • 1
    Please show your code, it's much easier to understand the problem and explain how to solve this when we can see what you're trying to do. – Julian May 02 '23 at 14:51
  • Thank you for adding the code. This still doesn't clarify what exactly your goal is. There may be a better way to achieve what you want to do. As Jason already mentioned, the `INotifyPropertyChanged` interface was designed exactly for the purpose of receiving notifications about changes to observable objects. – Julian May 03 '23 at 09:08
  • 1
    Each control is responsible for its own behaviour; AFAIK you'll have to register INotifyPropertyChanged for EACH control on your items. To make this easier, instead of using controls such as Checkbox directly, define a *subclass* of each control (e.g. "MyCheckbox", that has the desired behavior. This gives you a "reusable" control. Then in XAML, use "MyCheckbox" instead of "Checkbox". https://stackoverflow.com/a/74779935/199364 – ToolmakerSteve May 03 '23 at 18:12
  • I haven't had the time to test this, but I will accept it as an answer. – fonzane May 10 '23 at 08:39

2 Answers2

2

You can use MVVM to achieve it as Jason said that.

Page.xaml:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:MauiApp5.ViewModels"
             x:Class="MauiApp5.MainPage">
    <ContentPage.BindingContext>
        <vm:MonkeysViewModel/>
    </ContentPage.BindingContext>

   <VerticalStackLayout>
        <CollectionView ItemsSource="{Binding Monkeys}"
                        SelectionMode="Single"
                        SelectionChangedCommand="{Binding MonkeySelectionChangedCommand}"
                        SelectedItem="{Binding SelectedMonkey}">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                    <VerticalStackLayout>
                        <Label Text="{Binding Name}"/>
                    </VerticalStackLayout>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

ViewModel:

 public class MonkeysViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<Monkey> Monkeys { get; set; }
        Monkey selectedMonkey;
        public Monkey SelectedMonkey
        {
            get
            {
                return selectedMonkey;
            }
            set
            {
               selectedMonkey = value;
               OnPropertyChanged(nameof(SelectedMonkey));
            }
        }
       public ICommand MonkeySelectionChangedCommand { get; set; }
       void MonkeySelectionChanged()
        {
            var m = Monkeys;
            selectedMonkey.IsModified= true;
        }

       public MonkeysViewModel()
        {
            CreateMonkeys();
            MonkeySelectionChangedCommand= new Command(MonkeySelectionChanged);
        }
        void CreateMonkeys()
        {
            Monkeys = new ObservableCollection<Monkey>
            {
                new Monkey() { Name = "suma", IsModified = false },
                new Monkey() { Name = "demo", IsModified = false },
                new Monkey() { Name = "duff", IsModified = false },
                new Monkey() { Name = "cume", IsModified = false }
            };
        }

       public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

When you click the UI-Element inside the CollectionView, Property IsModified will change.

Jianwei Sun - MSFT
  • 2,289
  • 1
  • 3
  • 7
1

Let a name to your collectionView and use SelectedItem, sample

<CollectionView x:Name="collectionViewDigital"
            Margin="0,5"
            ItemsSource="{Binding digitalInputs}"
            SelectionMode="Single">

Then,

public DigitalView()
{
    InitializeComponent();

    collectionViewDigital.SelectionChanged += (s, e) => {
        Trace.WriteLine($"Selected: {collectionViewDigital.SelectedItem}"); // cast 
    };
}
Sith2021
  • 3,245
  • 30
  • 22