4

How can I add a Button column to a Datagrid programmatically? I want to do this through code in the code-behind file.

Also i want to selectively enable or disable this button based on record (If status is Open then Enable it else disable this button). Here Status is a Column in the DataSource.

Thanks, Abhi

Connell.O'Donnell
  • 3,603
  • 11
  • 27
  • 61
Abhijeet Kumar
  • 171
  • 2
  • 2
  • 4
  • http://stackoverflow.com/questions/1754337/how-to-add-button-dynamically-in-datagrid-in-wpf – Peter Nov 18 '09 at 10:54
  • try this: http://stackoverflow.com/questions/1754608/what-is-the-code-behind-for-datagridtemplatecolumn-and-how-to-use-it/1755556#1755556 – viky Nov 18 '09 at 13:20

3 Answers3

6

The answer linked by viky summarizes the idea of adding the button column from source. I've put together a short example that also shows the second part of your question, which is how to enable/disable the button based on the data in the grid. I used the DataGrid in 4.0, but the toolkit with 3.5 should be fine as well.

First, I add one Name column from XAML. This isn't necessary, but just shows that you can do a combination of XAML and C# additions to the grid. Note that I name the grid here so that I have a way to refer to it in the C# partial class.

<Window x:Class="WpfApplication2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Data Grid Populated in XAML and C#">
    <Grid>
        <DataGrid x:Name="_gridControl" IsReadOnly="True" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Width="*" Binding="{Binding Name}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

Now, I do two things in the code behind. Aside from adding the button column and the data elements (which have a Name and a Status), I create a Converter class that will return true if the value is "Open" and false otherwise. Then, when setting the Binding (as opposed to value), I also add my converter so that when the ItemsControl generates and binds containers for my items, the Button displayed has its IsEnabled state linked to the Status field of my item.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var buttonTemplate = new FrameworkElementFactory(typeof(Button));
        buttonTemplate.SetBinding(Button.ContentProperty, new Binding("Name"));
        buttonTemplate.SetBinding(Button.IsEnabledProperty, new Binding("Status")
        {
            Converter = new StatusToEnabledConverter()
        });
        buttonTemplate.AddHandler(
            Button.ClickEvent, 
            new RoutedEventHandler((o, e) => MessageBox.Show("hi"))
        );
        this._gridControl.Columns.Add(
            new DataGridTemplateColumn()
            {
                Header = "Close Button",
                CellTemplate = new DataTemplate() { VisualTree = buttonTemplate }
            }
        );

        this._gridControl.ItemsSource = new object[]
        {
            new { Name = "First Item", Status = "Open" },
            new { Name = "Second Item", Status = "Open" },
            new { Name = "Third Item", Status = "Closed" },
            new { Name = "Fourth Item", Status = "Closed" },
            new { Name = "Fifth Item", Status = "Open" }
        };
    }
}

public class StatusToEnabledConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        return "Open".Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        CultureInfo culture)
    {
        throw new System.NotImplementedException();
    }
}

One final important note is that I only modify the DataGrid from C# after InitializeComponent is called. For a WPF content control that has a XAML and C# part, this is important, as the object construction done via XAML needs to have completed before you start modifying the objects defined in XAML.

Hope this helps!

timmyl
  • 1,115
  • 9
  • 15
  • Nice example. However it makes unecessary use of the var keyword. There's no reason to use implicit typing for ButtonTemplate. It could simply be: FrameworkElementFactory buttonTemplate = new FrameworkElementFactory(typeof(Button)); – xr280xr May 17 '11 at 22:50
  • 6
    What does the extra 20 characters buy me? – timmyl Jun 07 '11 at 18:41
  • I suppose since the compiler still treats the variable as strong typed, it's just a pet peeve. Sorry, I take it back. – xr280xr Jun 08 '11 at 22:19
2

Xaml :

<DataGridTemplateColumn>
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <Button Click="Details">Details</Button>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>

Code Behind

       private IEnumerable<DataGridRow> GetDataGridRowsForButtons(DataGrid grid)
    { //IQueryable 
        var itemsSource = grid.ItemsSource as IEnumerable;
        if (null == itemsSource) yield return null;
        foreach (var item in itemsSource)
        {
            var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
            if (null != row & row.IsSelected) yield return row;
        }
    }

    void Details(object sender, RoutedEventArgs e)
    {

        for (var vis = sender as Visual; vis != null; vis = VisualTreeHelper.GetParent(vis) as Visual)
            if (vis is DataGridRow)
            {
               // var row = (DataGrid)vis;

                var rows = GetDataGridRowsForButtons(dgv_Students);
                string id;
                foreach (DataGridRow dr in rows)
                {
                    id = (dr.Item as tbl_student).Identification_code;
                    MessageBox.Show(id);
                }
                break;
            }
    }

After clicking on the Button, the ID of that row is returned to you and you can use it for your Button name.

ehsan A
  • 119
  • 1
  • 2
0

XAML:

  <DataGrid.Columns>
                <DataGridTemplateColumn Header="Check">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox  x:Name="chkName" IsChecked="{Binding IsChecked ,  UpdateSourceTrigger=PropertyChanged}" Click="chkName_Click">
                            </CheckBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                
                <DataGridTemplateColumn Header="as">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button Click="OnAddQuantityClicked">hello</Button>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>

Code Behind:

private async void OnAddQuantityClicked(object sender, RoutedEventArgs e)
        {
            if (sender is Button)
            {
                var temps = (Models.TempV)mainDatagrid.SelectedItem;
                //Button button = (Button)e.OriginalSource;
                if (temps.IsChecked is true)
                {
                    MessageBox.Show("Do something when it is True");
                }
                else
                {
                    MessageBox.Show("Do something when it is False");
                }
            }
        }
  • (A pity a *golden shovel* isn't awarded for resurrecting a question more than a dozen years old. Then again, a [silver one](https://stackoverflow.com/help/badges/17/necromancer) may be even more valuable faced with zombie questions.) Given that the question lacks a verb: How *did* you fill that gap? – greybeard Aug 24 '20 at 01:53