mercredi 1 février 2017

WPF DataGrid : how to bind an object to reflect the item whose row is checked

I have a datagrid populated with elements and a checkbox for each element. I'm looking for a way to have an object in my ViewModel be whichever element currently has its checkbox checked.
Here is my XAML so far :

<Window x:Class="fun_with_DataGridCheckBoxColumns.MainWindow"
    xmlns="http://ift.tt/o66D3f"
    xmlns:x="http://ift.tt/mPTqtT"
    xmlns:d="http://ift.tt/pHvyf2"
    xmlns:mc="http://ift.tt/pzd6Lm"
    xmlns:local="clr-namespace:fun_with_DataGridCheckBoxColumns"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<DockPanel>
    <StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
        <Label Content="Chosen One : " />
        <Label Content="{Binding ChosenOne.Name, Mode=OneWay}" />
    </StackPanel>
    <DataGrid ItemsSource="{Binding People}" AutoGenerateColumns="False" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="ID" Binding="{Binding ID, Mode=OneWay}" IsReadOnly="True"/>
            <DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=OneWay}" IsReadOnly="True"/>
            <DataGridCheckBoxColumn Header="Is Chosen"/>
        </DataGrid.Columns>
    </DataGrid>
</DockPanel>

and my CS :

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace fun_with_DataGridCheckBoxColumns
{
    public partial class MainWindow : Window
    {
        public Person ChosenOne { get; set; }

        public MainWindow()
        {
        InitializeComponent();
        DataContext = new Viewmodel();
        }
    }

    public class Viewmodel : INotifyPropertyChanged
    {
        public ObservableCollection<Person> People { get; private set; }
        private Person _chosenOne = null;
        public Person ChosenOne
        {
            get
            {
                if (_chosenOne == null) { return new Person { Name = "Does Not Exist" }; }
                else return _chosenOne;
            }
            set
            {
                _chosenOne = value;
                NotifyPropertyChanged("ChosenOne");
            }
        }

        public Viewmodel()
        {
            People = new ObservableCollection<Person>
            {
                new Person { Name = "John" },
                new Person { Name = "Marie" },
                new Person { Name = "Bob" },
                new Person { Name = "Sarah" }
            };
        }

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

    public class Person
    {
        private static int person_quantity = 0;
        private int _id = ++person_quantity;
        public int ID { get { return _id; } }
        public string Name { get; set; }
    }
}

Here is the behavior I am looking for :

  • ChosenOne in ViewModel becomes whichever Person has its checkbox checked
  • When a checkbox is checked, all others are unchecked
  • If no checkboxes are checked, sets ChosenOne to null

Basically it is the same behavior as if I had put this in the DataGrid (XAML) :

SelectedItem="{Binding ChosenOne, Mode=TwoWay}"

But in my case ChosenOne can not be the SelectedItem of the datagrid since I need SelectedItem for something else, and I have to use checkboxes for Company reasons.

I have not found how to simulate this "SelectedItem" logic with checkboxes.

I know I could put a "bool IsChosen" property in my Person class and bind the checkbox to it, but I would really rather avoid this. It will be my solution if all else fails.

Thank you.




Aucun commentaire:

Enregistrer un commentaire