WPF: How to enable ListView only when first column checkbox is checked and whole row is selected

Jane Jie Chen 346 Reputation points
2023-05-18T14:37:12.34+00:00

Our WPF application needs a complicated table which includes following columns:

First column: "Run" CheckBox to indicate if whole row is selected.

Second column: File Name (label)

Third column: Temperatures: a list view: each Temperature item includes: CheckBox (to select this item), TextBoxes to display/edit Temperature and Hold Time

Fourth column: Cycles (TextBox)

we use the WPF ListView to implement this table.ComplicatedListView_CheckBox

Notice that clicking or mousing over on any cell will selects/unselects whole row. This behavior is not we want.

we need that:

  1. In first column, checking Checkbox of Run, it selects whole row. Unchecking Checkbox of Run, it unselects whole row.
  2. If click or mouse over on other columns, it does not select/unselect a row.
  3. The Temperatures column allows edit Textbox data and allow check/uncheck Checkbox.
  4. The Cycles column allow edit Textbox data.

How could we implement it?

Is the ListView control best choice? Thx!

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,671 questions
{count} votes

Accepted answer
  1. Hui Liu-MSFT 38,251 Reputation points Microsoft Vendor
    2023-05-19T08:07:05.4333333+00:00

    Hi,@Jane Jie Chen. For the problem of clicking on a particular column to select a row and clicking on other columns to select cells, you could use the SelectionUnit property of the DataGrid.

    Xaml:

     <DataGrid x:Name="dg" ItemsSource="{Binding Items}" AutoGenerateColumns="False"
                     Height="300"  PreviewMouseLeftButtonDown="DataGrid_PreviewMouseLeftButtonDown"
                      SelectionUnit="Cell">
                <DataGrid.Columns>
                
                    <DataGridTemplateColumn Header="Run" Width=" 80"  >
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
    
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                       
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Header="FileName" Binding="{Binding FileName}"/>
                    <DataGridTemplateColumn Header="Temperatures">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <CheckBox IsChecked="{Binding IsChecked}" />
                                    <TextBox Text="{Binding Temperature}" />
                                </StackPanel>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                    <DataGridTextColumn Header="Cycles" Binding="{Binding Cycles}"/>
                </DataGrid.Columns>
            </DataGrid>
           
    
        </StackPanel>
    
    
    
    

    Codebedhind:

     public partial class MainWindow : Window, INotifyPropertyChanged
        {
           
            public ObservableCollection<RowData> Items { get; } = new ObservableCollection<RowData>();
    
            public MainWindow()
            {
                InitializeComponent();
                DataContext= this;
             
    
                // Sample data
                Items.Add(new RowData { FileName="name1", Temperature = "20°C", Cycles = "5" });
                Items.Add(new RowData { FileName = "name2", Temperature = "25°C", Cycles = "10" });
                Items.Add(new RowData { FileName = "name3", Temperature = "30°C", Cycles = "15" });
               
            }
    
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set
                {
                    this._isSelected = value;
                    OnPropertyChanged(nameof(IsSelected));
                }
            }
            
            private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                DependencyObject dep = (DependencyObject)e.OriginalSource;
    
                DataGridCell cell = FindVisualParent<DataGridCell>(dep);
    
                if (cell != null)
                {
                    int columnIndex = cell.Column.DisplayIndex;
    
                    if (columnIndex == 0)
                    {
                        ((DataGrid)sender).SelectionUnit = DataGridSelectionUnit.FullRow;
                    }
                    else
                    {
                        ((DataGrid)sender).SelectionUnit = DataGridSelectionUnit.Cell;
                    }
                }
            }
    
            private static T FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
            {
                DependencyObject parent = VisualTreeHelper.GetParent(obj);
    
                if (parent == null)
                    return null;
    
                T parentT = parent as T;
                return parentT ?? FindVisualParent<T>(parent);
            }
    
    
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        public class RowData : INotifyPropertyChanged
        {
           
            private bool _isChecked;
            private string _temperature;
            private string _fileName;
            private string _cycles;
    
           
           
            public bool IsChecked
            {
                get { return _isChecked; }
                set
                {
                    _isChecked = value;
                    OnPropertyChanged(nameof(IsChecked));
                }
            }
    
            public string Temperature
            {
                get { return _temperature; }
                set
                {
                    _temperature = value;
                    OnPropertyChanged(nameof(Temperature));
                }
            }
            public string FileName
            {
                get { return _fileName; }
                set
                {
                    _fileName = value;
                    OnPropertyChanged(nameof(FileName));
                }
            }
    
            public string Cycles
            {
                get { return _cycles; }
                set
                {
                    _cycles = value;
                    OnPropertyChanged(nameof(Cycles));
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
    
    
    

    The result:

    6


    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Jane Jie Chen 346 Reputation points
    2023-05-19T15:51:49.4766667+00:00

    Hi Hui Liu,

    Thanks for providing your solution and really appreciated!

    Your solution by using DataGrid.SelectionUnit ="Cell" is one way to work around for us.

    Your provided solution helps us think a different way.

    We actually continually to use ListView and make it work.

    In the ListView, add event PreviewMouseDown="listViewUDMPCRTemperatureFiles_PreviewMouseDown"

    and implement the event as following in WPF:

    It works:

    1. In first column, checking Checkbox of Run, it selects whole row. Unchecking Checkbox of Run, it unselects whole row.
    2. If click or mouse over on other columns, it does not select/unselect a row.
    3. The Temperatures column allows edit Textbox data and allow check/uncheck Checkbox.
    4. The Cycles column allow edit Textbox data.

    upload example project here (please remove ~.txt before unzip.

    WpfAppDataGrid.zip.txt

    private void listViewUDMPCRTemperatureFiles_PreviewMouseDown(object sender, MouseButtonEventArgs e)
            {
                DependencyObject dep = (DependencyObject)e.OriginalSource;
    
                TextBox textBox = FindParent<TextBox>(dep);
                CheckBox checkBox = FindParent<CheckBox>(dep);
                if (textBox == null && checkBox == null)
                    e.Handled = true;
            }
    
    0 comments No comments