Reputation: 3
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
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
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
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