WPF Seleziona tutte le caselle di controllo in un DataGrid

 C Programming >> Programmazione C >  >> Tags >> WPF
WPF Seleziona tutte le caselle di controllo in un DataGrid

TLDR; Questo è quello che vuoi, codice qui sotto:

Il posto giusto per farlo sarebbe nel tuo ViewModel. Il tuo CheckBox può avere tre stati, di cui vuoi utilizzare tutti:

  1. Controllato - Ogni articolo è controllato
  2. Deselezionato - Nessun elemento è selezionato
  3. Indeterminato - Alcuni elementi sono selezionati, altri no

Ti consigliamo di aggiornare il CheckBox ogni volta che un elemento viene selezionato/deselezionato e aggiornare tutti gli elementi ogni volta che il CheckBox è stato modificato:l'implementazione di questo solo modo lascerà il CheckBox in uno stato non valido che potrebbe avere un impatto negativo sull'esperienza dell'utente. Il mio consiglio:vai fino in fondo e implementalo correttamente. Per fare ciò è necessario essere consapevoli di quale sia la causa della modifica:la casella di controllo di una voce o la casella di controllo nell'intestazione.

Ecco come lo farei:

Per prima cosa hai bisogno di un ViewModel per i tuoi articoli, ne ho usato uno molto semplificato qui che contiene solo il IsChecked proprietà.

public class Entry : INotifyPropertyChanged
{
    private bool _isChecked;

    public bool IsChecked
    {
        get => _isChecked;
        set
        {
            if (value == _isChecked) return;
            _isChecked = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Il tuo ViewModel principale avrà una raccolta di tutti gli elementi. Ogni volta che un elemento è IsChecked modifiche alla proprietà, dovrai controllare se tutte gli elementi sono selezionati/deselezionati e aggiornano il CheckBox nell'intestazione (o meglio il valore della sua origine dati).

public class ViewModel : INotifyPropertyChanged
{
    public List<Entry> Entries
    {
        get => _entries;
        set
        {
            if (Equals(value, _entries)) return;
            _entries = value;
            OnPropertyChanged();
        }
    }

    public ViewModel()
    {
        // Just some demo data
        Entries = new List<Entry>
        {
            new Entry(),
            new Entry(),
            new Entry(),
            new Entry()
        };

        // Make sure to listen to changes. 
        // If you add/remove items, don't forgat to add/remove the event handlers too
        foreach (Entry entry in Entries)
        {
            entry.PropertyChanged += EntryOnPropertyChanged;
        }
    }

    private void EntryOnPropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        // Only re-check if the IsChecked property changed
        if(args.PropertyName == nameof(Entry.IsChecked))
            RecheckAllSelected();
    }

    private void AllSelectedChanged()
    {
        // Has this change been caused by some other change?
        // return so we don't mess things up
        if (_allSelectedChanging) return;

        try
        {
            _allSelectedChanging = true;

            // this can of course be simplified
            if (AllSelected == true)
            {
                foreach (Entry kommune in Entries)
                    kommune.IsChecked = true;
            }
            else if (AllSelected == false)
            {
                foreach (Entry kommune in Entries)
                    kommune.IsChecked = false;
            }
        }
        finally
        {
            _allSelectedChanging = false;
        }
    }

    private void RecheckAllSelected()
    {
        // Has this change been caused by some other change?
        // return so we don't mess things up
        if (_allSelectedChanging) return;

        try
        {
            _allSelectedChanging = true;

            if (Entries.All(e => e.IsChecked))
                AllSelected = true;
            else if (Entries.All(e => !e.IsChecked))
                AllSelected = false;
            else
                AllSelected = null;
        }
        finally
        {
            _allSelectedChanging = false;
        }
    }

    public bool? AllSelected
    {
        get => _allSelected;
        set
        {
            if (value == _allSelected) return;
            _allSelected = value;

            // Set all other CheckBoxes
            AllSelectedChanged();
            OnPropertyChanged();
        }
    }

    private bool _allSelectedChanging;
    private List<Entry> _entries;
    private bool? _allSelected;
    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

XAML demo:

<DataGrid ItemsSource="{Binding Entries}" AutoGenerateColumns="False" IsReadOnly="False" CanUserAddRows="False">
    <DataGrid.Columns>
        <DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}">
            <DataGridCheckBoxColumn.HeaderTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}, Path=ViewModel.AllSelected}">Select All</CheckBox>
                </DataTemplate>
            </DataGridCheckBoxColumn.HeaderTemplate>
        </DataGridCheckBoxColumn>
    </DataGrid.Columns>
</DataGrid>

Quello che fai nel tuo esempio è scorrere l'elemento di dati non attraverso i controlli (suppongo che tu non abbia controlli come ItemsSource).
Nel link hai postato YourClass è la classe di ViewModel, oggetto dati per la riga della griglia.

Questo dovrebbe funzionare con modifiche minime al codice dalla tua parte (ma preferirei gestirlo nel ViewModel con qualcosa come CheckUncheckCommand + binding di IsChecked al CommandParameter ):

<DataGridCheckBoxColumn x:Name="col0" HeaderStyle="{StaticResource ColumnHeaderGripperStyle}" DisplayIndex="0">

private void CheckUnCheckAll(object sender, RoutedEventArgs e)
{
    var chkSelectAll = sender as CheckBox;
    var firstCol = dgUsers.Columns.OfType<DataGridCheckBoxColumn>().FirstOrDefault(c => c.DisplayIndex == 0);
    if (chkSelectAll == null || firstCol == null || dgUsers?.Items == null)
    {
        return;
    }
    foreach (var item in dgUsers.Items)
    {
        var chBx = firstCol.GetCellContent(item) as CheckBox;
        if (chBx == null)
        {
            continue;
        }
        chBx.IsChecked = chkSelectAll.IsChecked;
    }
}

No