Enable button when checkbox is checked in WPF datagrid

4.7k views Asked by At

I have a Datagrid in WPF in which first column has checkbox column and last column has buttons. Initially, I want to make all the buttons disabled and whenever any checkbox is checked then button of that row should get enabled. checkbox is unchecked then button should be disabled.

Searched a Lot but could not find anything related to this.

I am not using MVVM.. How to do this on the code behind?

Thanks

This is my Xaml Code and I am simply assigning my itemsource on the code behind

            <Grid Grid.Row="2" Grid.ColumnSpan="2" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" >
            <Border BorderThickness="0" Margin="10" CornerRadius="15">
                <Border.BitmapEffect>
                    <DropShadowBitmapEffect />
                </Border.BitmapEffect>
                <Grid>
                    <Border x:Name="BDRounded" BorderThickness="0" CornerRadius="15" Background="White"/>
                    <DataGrid HorizontalAlignment="Left" x:Name="dgrdActors" RowHeight="74" AutoGenerateColumns="False" CanUserAddRows="False"
                                      BorderThickness="1,0,0,0" BorderBrush="#FFD1A251" FontSize="28" Foreground="#DCA566" FontFamily="Helvetica Neue" 
                                      CanUserResizeRows="False" AlternatingRowBackground="Linen" AlternationCount="2" Background="#DCA566"
                                      RowHeaderWidth="0" CanUserResizeColumns="False" CanUserSortColumns="False" CanUserReorderColumns="False"
                                      ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" 
                                      HorizontalGridLinesBrush="#FFD1A251" VerticalGridLinesBrush="#FFD1A251" Height="326"
                                      SelectionMode="Extended" SelectionUnit="FullRow" VirtualizingStackPanel.VirtualizationMode="Standard"
                                      Style="{StaticResource DatagridStyle}">
                        <DataGrid.Columns>
                            <DataGridTemplateColumn Width="70" CanUserReorder="False" CanUserResize="False" CanUserSort="False" CellStyle="{StaticResource                      HitVisibilityCellStyle}" HeaderStyle="{StaticResource HeaderStyle}" >
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <Viewbox Margin="-1">
                                            <!--<CheckBox x:Name="chkboxactors" HorizontalAlignment="Center" VerticalAlignment="Center" 
                                                      IsChecked="{Binding IsActorChecked, UpdateSourceTrigger=PropertyChanged}"></CheckBox>-->
                                            <CheckBox x:Name="chkboxActors" HorizontalAlignment="Center" VerticalAlignment="Center"></CheckBox>
                                        </Viewbox>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                            <DataGridTextColumn Header="Actor Name(s)" Width="300" Binding="{Binding ActorName}" CanUserReorder="False" CellStyle="{StaticResource                              CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}"                            IsReadOnly="True">
                                <DataGridTextColumn.ElementStyle>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="TextWrapping" Value="Wrap"/>
                                        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
                                        <Setter Property="VerticalAlignment" Value="Center"></Setter>
                                    </Style>
                                </DataGridTextColumn.ElementStyle>
                            </DataGridTextColumn>
                            <DataGridTextColumn Header="Role(s)" Width="300" Binding="{Binding Role}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}"                     CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
                                <DataGridTextColumn.ElementStyle>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="TextWrapping" Value="Wrap"/>
                                        <Setter Property="HorizontalAlignment" Value="Center"></Setter>
                                        <Setter Property="VerticalAlignment" Value="Center"></Setter>
                                    </Style>
                                </DataGridTextColumn.ElementStyle>
                            </DataGridTextColumn>
                            <DataGridTemplateColumn Width="250" CanUserReorder="False" CanUserResize="False" HeaderStyle="{StaticResource HeaderStyle}"                         CellStyle="{StaticResource HitVisibilityCellStyle}">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <Button x:Name="btnSelectRole" Content="Select Role" Style="{StaticResource DatagridButtonStyle}"></Button>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                        </DataGrid.Columns>

                        <DataGrid.OpacityMask>
                            <VisualBrush Visual="{Binding ElementName=BDRounded}"/>
                        </DataGrid.OpacityMask>
                    </DataGrid>
                </Grid>
            </Border>
        </Grid>
5

There are 5 answers

1
d.moncada On BEST ANSWER

Here you go!

There's no need to do the enabling/disabling in the ViewModel, as this can all be done in XAML.

XAML:

<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Button Width="100">
                        <Button.Style>
                            <Style TargetType="Button">
                                <Setter Property="IsEnabled" Value="false"/>
                                <Style.Triggers>
                                    <DataTrigger Binding="{Binding IsChecked}" Value="true">
                                        <Setter Property="IsEnabled" Value="True"/>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

ViewModel:

public class ViewModel
{
    public List<Data> Items { get; private set; }
    public ViewModel()
    {
        Items = new List<Data>
        {
            new Data(),
            new Data(),
            new Data()
        };
    }
}

public class Data : INotifyPropertyChanged
{
    private bool _isChecked;
    public bool IsChecked 
    { 
        get  {return _isChecked; }
        set 
        {
            _isChecked = value;
            OnPropertyChanged("IsChecked");               
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string property)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(property));
        }
    }
}

Edit: Since you've requested a code-behind implementation, here you go. This works by traversing the visual tree based on the current row that the checkbox was clicked from.

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <CheckBox Click="CheckBox_Clicked"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Button Width="100" x:Name="Button" IsEnabled="false" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Window>

XAML.CS:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        MyDataGrid.ItemsSource = new List<string>
        {
            "test",
            "test1",
            "test2",
            "test3"
        };
    }

    private void CheckBox_Clicked(object sender, RoutedEventArgs e)
    {
        var checkBox = sender as CheckBox;
        if (checkBox != null)
        {
            var associatedRow = VisualTreeHelper.GetParent(checkBox);

            while ((associatedRow != null) && (associatedRow.GetType() != typeof(DataGridRow)))
            {
                associatedRow = VisualTreeHelper.GetParent(associatedRow);
            }

            var dataGridRow = associatedRow as DataGridRow;
            if (dataGridRow != null)
            {
                var associatedButton = FindChild(dataGridRow, "Button");
                if (associatedButton != null)
                {
                    associatedButton.IsEnabled = checkBox.IsChecked.HasValue ? checkBox.IsChecked.Value : false;
                }
            }
        }
    }

    public static Button FindChild(DependencyObject parent, string childName)
    {
        if (parent == null) return null;

        Button foundChild = null;

        var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            var childType = child is Button;
            if (!childType)
            {
                foundChild = FindChild(child, childName);
                if (foundChild != null) break;
            }
            else if (!string.IsNullOrEmpty(childName))
            {
                var frameworkElement = child as FrameworkElement;
                if (frameworkElement != null && frameworkElement.Name == childName)
                {
                    foundChild = (Button)child;
                    break;
                }
            }
            else
            {
                foundChild = (Button)child;
                break;
            }
        }
        return foundChild;
    }
}
2
zhangyiying On

You should use MVVM.DataBinding is convenient .

Xaml

<CheckBox Name="checkbox"  IsChecked="{Binding Checked, Mode = TwoWay}" />
<Button  IsEnabled="{Binding ButtonEnabled , Mode = TwoWay}" />

C#

In ViewModel

public class ViewMode : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private bool _buttonChecked = false;
    public bool ButtonChecked
    { 
       get
       {
        return _buttonChecked;
       }
       set
       {
         if(value == true)
         {
          _buttonChecked = value;
           OnPropertyChanged("ButtonChecked");
         }
       }
   }

   private bool _checked;
   public bool Checked
   { 
     get
      {
        return _checked;
      }
     set
      {
         if(value == true)
         {
            _checked= value;
            ButtonChecked = value;
            OnPropertyChanged("Checked");
         }
      }
    }

   [NotifyPropertyChangedInvocator]
    private virtual void OnPropertyChanged(string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
6
Olaru Mircea On

This can be your DataGrid definition:

<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding source}" AutoGenerateColumns="False" CanUserAddRows="False">
        <DataGrid.Columns>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                            <CheckBox x:Name="TestBox" Content="Test" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTemplateColumn>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                            <Button Content="Click" IsEnabled="{Binding IsChecked}"/>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>

Pretty simple code behind:

public partial class MainWindow : Window
{
    public ObservableCollection<Model> source { get; set; } 

    public MainWindow()
    {
        InitializeComponent();
        source = new ObservableCollection<Model>();
        source.Add(new Model());
        source.Add(new Model());
        this.DataContext = this;
    }
}

This could be your model:

public class Model : DependencyObject
{
    public bool IsChecked
    {
        get { return (bool)GetValue(IsCheckedProperty); }
        set { SetValue(IsCheckedProperty, value); }
    }

    // Using a DependencyProperty as the backing store for IsChecked.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsCheckedProperty =
        DependencyProperty.Register("IsChecked", typeof(bool), typeof(Model), new PropertyMetadata(false));

}

Or implement INPC inteface:

public class Model : INotifyPropertyChanged
{
    private bool _IsChecked;

    public bool IsChecked
    {
        get { return _IsChecked; }
        set
        {
            _IsChecked = value;
            PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
2
Johnathon Sullinger On

If the datagrid is bound to a collection of objects you own (ideally in this case a Facade of a model), then add a IsSelected property to the object that makes up the collection. You can databind your checkbox to that property.

To enable/disable the button, have the model/facade in the collection implement ICommand. You can then use the CanExecute method to enable/disable the button based on the value of IsSelected.

public class User : ICommand, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }        
    }

    public bool IsSelected
    {
        get
        {
            return this.isSelected;
        }
        set
        {
            this.isSelected = value;
            CommandManager.InvalidateRequerySuggested();
            this.OnPropertyChanged("IsSelected");
        }
    }

    public bool CanExecute(object parameter) 
    { 
        return this.IsSelected; 
    }

    public void Execute(object parameter)
    {
        // ... Do stuff ...
    }

    private void RaiseCanExecuteChanged()
    {
        var handler = this.CanExecuteChanged;
        if (handler == null)
        {
            return;
        }

        handler(this, new PropertyChangedEventArgs(property));
    }

    private void OnPropertyChanged(string property)
    {
        var handler = this.PropertyChanged;
        if (handler == null)
        {
            return;
        }

        handler(this, new PropertyChangedEventArgs(property));
    }
}

Now you bind your checkbox to the IsSelected property. Anytime that the checkbox is selected, the CanExecute method will fire on the class.

Ideally you would use a DelegateCommand class from either MVVMLight or Prism, which have a RaiseCanExecuteChanged() method. This lets you avoid using the CommandManager to requery it.

0
sammy On
 < DataGridTemplateColumn Header="{ Loc CellSettings_Min}" >

     <DataGridTemplateColumn.CellTemplate >

         < DataTemplate >

             < TextBox Width="100" Text="{ Binding Interval }" >

                 <TextBox.Style>

                     < Style TargetType = "TextBox" >

                         < Setter Property="IsEnabled" Value="false" />

                     < Style.Triggers >
          < DataTrigger Binding = "{ Binding AutoScale }" Value= "True" >
             < Setter Property = "IsEnabled" Value = "False" />  
          < /DataTrigger >
      < DataTrigger Binding = "{ Binding AutoScale }" Value="False" >
         < Setter Property = "IsEnabled" Value = "True" />
      </ DataTrigger >
      </ Style.Triggers >
    </ Style >
     </ TextBox.Style >
      </ TextBox >
      </ DataTemplate >
  </ DataGridTemplateColumn.CellTemplate >
  </ DataGridTemplateColumn >