TWhy
TWhy

Reputation: 3

How do I programmatically add event handlers to RadioButtons in a DataGridTemplateColumn that is dynamically created using XamlReader?

I am trying to add event handlers to RadioButtons in a DataGridTemplateColumn which is created through XamlReader. Here is the code I'm using:

string templateColumnStart = @"<DataGridTemplateColumn Header='Svar' Width='200' x:Name='myTemplateColumn'
    xmlns ='http://schemas.microsoft.com/winfx/2006/xaml/presentation' 
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <StackPanel Orientation='Horizontal'>";
string templateColumnEnd = @"</StackPanel>
    </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>";
string radioButtonString = @"<RadioButton Name='rb1' Content='1' IsChecked='false'/>";
string fullXamlString = templateColumnStart + radioButtonString + templateColumnEnd;
MemoryStream stream = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(fullXamlString));
DataGridTemplateColumn templateColumn = (DataGridTemplateColumn)XamlReader.Load(stream);
StackPanel template = (StackPanel)templateColumn.CellTemplate.LoadContent();
RadioButton radiobutton = (RadioButton)template.FindName("rb1");
radiobutton.Checked += new RoutedEventHandler(rb_Checked);
myDataGrid.Columns.Add(templateColumn);

The reason I'm using XamlReader to create the TemplateColumn is that the number of RadioButtons needed in the column will vary, and as such, I need the ability to dynamically change the number of RadioButtons created.

The column is created and added to the DataGrid without a problem, and the RadioButton is displayed correctly. However, the event handler does not seem to have been added, as checking the button does not trigger it.

As a side note, simply adding "Checked='rb_Checked'" to the radioButtonString throws a XamlParserException, as XamlReader is not able to handle event handlers.

Any help with this would be greatly appreciated.

Upvotes: 0

Views: 539

Answers (3)

mm8
mm8

Reputation: 169200

The main issue here is that you are hooking up the event handler to the wrong RadioButton instance. A new one will be created by the WPF runtime when the CellTemplate is applied so calling the LoadContent() method is meaningless here.

Unfortunately the DataGridColumn class doesn't raise any events when the GenerateElement method is invoked so you will have to create your own custom DataGridColumn to be able to get a reference to the StackPanel once the template has actually been applied. Before this there is no StackPanel nor RadioButton. A DataTemplate is as the name implies just a template that is being applied eventually.

Here is what you could do if you are not satisfied with using an implicit Style that gets applied to all RadioButtons.

Create a custom class that inherits from DataGridTemplateColumn and raises an event when the GenerateElement method is called:

namespace WpfApplication2
{
    public class CellElementGeneratedEventArgs : EventArgs
    {
        public FrameworkElement Content { get; set; }
    }

    public delegate void CellElementGeneratedEventHandler(object sender, CellElementGeneratedEventArgs e);

    public class MyDataGridTemplateColumn : DataGridTemplateColumn
    {
        public event CellElementGeneratedEventHandler ElementGenerated;

        protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
        {
            FrameworkElement fe = base.GenerateElement(cell, dataItem);
            if(fe != null)
                fe.Loaded += Fe_Loaded;
            return fe;
        }

        private void Fe_Loaded(object sender, RoutedEventArgs e)
        {
            if (ElementGenerated != null)
                ElementGenerated(this, new CellElementGeneratedEventArgs() { Content = sender as FrameworkElement });
        }
    }
}

And modify your XAML markup and code slightly:

string templateColumnStart = @"<local:MyDataGridTemplateColumn Header='Svar' Width='200' x:Name='myTemplateColumn' xmlns ='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:local='clr-namespace:WpfApplication2;assembly=WpfApplication2'> <DataGridTemplateColumn.CellTemplate><DataTemplate><StackPanel Orientation='Horizontal'>";
string templateColumnEnd = @"</StackPanel></DataTemplate></DataGridTemplateColumn.CellTemplate></local:MyDataGridTemplateColumn>";
string radioButtonString = @"<RadioButton Name='rb1' Content='1' IsChecked='false'/>";
string fullXamlString = templateColumnStart + radioButtonString + templateColumnEnd;
using (MemoryStream stream = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(fullXamlString)))
{
    MyDataGridTemplateColumn templateColumn = (MyDataGridTemplateColumn)XamlReader.Load(stream);
    templateColumn.ElementGenerated += (ss, ee) => 
    {
        ContentPresenter cc = ee.Content as ContentPresenter;
        if(cc != null)
        {
            StackPanel sp = VisualTreeHelper.GetChild(cc, 0) as StackPanel;
            if(sp != null)
            {
                RadioButton rb = sp.FindName("rb1") as RadioButton;
                if (rb != null)
                    rb.Checked += rb_Checked;
            }
        }
    };
    myDataGrid.Columns.Add(templateColumn);
}

Note that the you will have to change "assembly=WpfApplication2" to the name of the assembly where you define your MyDataGridTemplateColumn class. The same applies to the namespace.

Upvotes: 1

Datastream
Datastream

Reputation: 50

If I remember correctly, you can add Checked="CheckHandlerFunction" in the XAML itself. That may work.

This may not apply in your situation, but have you looked into templates and binding controls to DataContext? You can automate a lot of control creation in WPF using them.

Upvotes: 0

Nemanja Banda
Nemanja Banda

Reputation: 861

You can add EventSetter to your window like this:

<Window.Resources>
    <Style TargetType="{x:Type RadioButton}">
        <EventSetter Event="Checked" Handler="RadioButton_Checked"/>
    </Style>
</Window.Resources>

Event handler method:

void RadioButton_Checked(object sender, RoutedEventArgs e)
{
   // Handle the event...
}

This will call the handler for every RadioButton in your DataGrid.

Upvotes: 0

Related Questions