2

I have a WINUI3 project in which I need to add an element, CheckBox inside a

CheckBox->DataTemplate->DataGridTemplateColumn.CellTemplate->DataGrid

While my DataGrid is defined in Xaml, I need to add the CheckBox inside DataTemplate progmatically using C#. For reasons unknown to me, I dont understand why its so complicated while its easily done in Xaml,in C# there seems to be no way.

private void CreateDataTableForBowlers()
{
    allBowlers.Columns.Clear();
    DataGridTextColumn column = new DataGridTextColumn() { Header = "Name", Binding = new Binding() { Path = new PropertyPath("Name") } };
    column.FontSize = 10;

    DataGridTemplateColumn dataGridTemplateColumn = new();
    dataGridTemplateColumn.Header = "Select Two";

    DataTemplate dataTemplate = new DataTemplate();
    dataGridTemplateColumn.CellTemplate = dataTemplate;

    CheckBox checkBox = new CheckBox();
    checkBox.SetBinding(CheckBox.IsCheckedProperty, new Binding() { Path = new PropertyPath("isBowling") });

    /*
     * Need to add checkBox to dataTemplate here
     * 
     */

    allBowlers.Columns.Add(dataGridTemplateColumn);
    allBowlers.Columns.Add(column);          
}

The Corresponding Xaml that I have is (For reference)

<controls:DataGridTemplateColumn Header="Select Two">
    <controls:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding GetBowling ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Checked="CheckBox_Checked" />
        </DataTemplate>
    </controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>

I even tried XamlReader.Load, for above XAML part which always ends in crashing. Anyone what am I missing in doing it in C#?

UPDATE

I had used XamlReader.Load earlier and also as suggested by @Andrew KeepCoding but didnt work. I cant use string literals like """ as I am using Win10. So my code for XamlReader.Load looks like this

   private void CreateDataTableForBowlers()
 {

allBowlers.Columns.Clear();
   DataGridTextColumn column = new DataGridTextColumn() { Header = "Name", Binding = new Binding() { Path = new PropertyPath("Name") } };
   column.FontSize = 10;
   DataGridTemplateColumn dataGridTemplateColumn = new();
   dataGridTemplateColumn.Header = "Select Two";        
   

   //CheckBox checkBox = new CheckBox();
   //checkBox.SetBinding(CheckBox.IsCheckedProperty, new Binding() { Path = new PropertyPath("isBowling") });

   string xaml = "<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'> <CheckBox IsChecked='{Binding IsBowling, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' /></DataTemplate>";

   DataTemplate dataTemplate = new DataTemplate();
   dataTemplate = XamlReader.Load(GetStream(xaml)) as DataTemplate;

   dataGridTemplateColumn.CellTemplate = dataTemplate;

   allBowlers.Columns.Add(dataGridTemplateColumn);
   allBowlers.Columns.Add(column); 
 }

   private Stream GetStream(string input)
   {
       byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(input);
       return new MemoryStream(byteArray);
   }
Mustafa Özçetin
  • 1,893
  • 1
  • 14
  • 16
  • It's complicated to do this in code because doing UI stuff in code it's not the spirit of these XAML-oriented technologies (WPF, UWP, WinUI3), they are focused on data binding. What you should do instead is for example add an element to the data template and bind its Visibility property to something in the bound model. – Simon Mourier Aug 21 '23 at 05:59
  • 1
    I agree, but due to projetc being overly complex now, it is the only way out for me. –  Aug 21 '23 at 11:25
  • @Martin Did not find any such Definition under DataTemplate as "dataTemplate.Template" –  Aug 21 '23 at 11:44
  • 1
    You asked "I dont understand why its so complicated", I told you why. – Simon Mourier Aug 21 '23 at 11:54

1 Answers1

0

This should work:

private void CreateDataTableForBowlers()
{
    DataGridControl.Columns.Clear();
        
    DataGridTextColumn textColumn = new()
    {
        Header = "Name",
        Binding = new Binding
        {
            Path = new PropertyPath("Name"),
        }
    };

    DataGridControl.Columns.Add(textColumn);

    DataTemplate? checkBoxTemplate = XamlReader.Load(
        @"
        <DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>
            <CheckBox
                Content='Is Bowling'
                Checked='CheckBox_Checked'
                IsChecked='{Binding IsBowling, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}'
                IsEnabled='{Binding IsEnabled}' />
        </DataTemplate>
        ") as DataTemplate;

    DataGridTemplateColumn templateColumn = new()
    {
        Header = "Is Bowling",
        CellTemplate = checkBoxTemplate,
    };

    DataGridControl.Columns.Add(templateColumn);
}

And if you need to subscribe to Checked event:

public MainPage()
{
    this.InitializeComponent();
    this.DataGridControl.LoadingRow += DataGridControl_LoadingRow;
}

private void DataGridControl_LoadingRow(object? sender, DataGridRowEventArgs e)
{
    e.Row.Loaded += Row_Loaded;

}

private void Row_Loaded(object sender, RoutedEventArgs e)
{
    if (sender is not DataGridRow row)
    {
        return;
    }

    if (row.FindDescendants()
        .OfType<DataGridCell>()
        .Select(x => x.Content)
        .OfType<CheckBox>()
        .FirstOrDefault() is CheckBox checkBox)
    {
        checkBox.Checked -= CheckBox_Checked;
        checkBox.Checked += CheckBox_Checked;
    }
}

BTW, the FindDescendants() extension method is from the CommunityToolkit.WinUI.UI NuGet package.

Andrew KeepCoding
  • 7,040
  • 2
  • 14
  • 21
  • Hi @Andrew KeepCoding, thank you for detailed answer, but since I am using Win10, cannot use String literals like """. I had already tried XamlReader.Load but didnt work. Plz check the updated question –  Aug 21 '23 at 11:20
  • I guess you mean C#10. You just need to update to .NET 7. Check this [link](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#:~:text=C%23%2011%20is,NET%207%20SDK.). Anyway, I updated my answer. – Andrew KeepCoding Aug 21 '23 at 11:46
  • Thanks again, however with String literals or using @, I always get syntax error saying Cannot convert from string to System.IO.Stream, so thats why I am using the method above as GetStream. But when i use that method I also get -> System.TypeInitializationException: 'The type initializer for 'Missing template. Cannot initialize CommunityToolkit.WinUI.UI.Controls.DataGridTemplateColumn.' threw an exception.' when adding DataGridTemplateColumn to allBowlers i.e. DataGrid –  Aug 21 '23 at 16:10
  • Are you sure this is a WinUI 3/WinAppSDK app? That seems to be WPF's [System.Windows.Markup.XamlReader](https://learn.microsoft.com/en-us/dotnet/api/system.windows.markup.xamlreader.load?view=windowsdesktop-7.0), where you should be using [Microsoft.UI.Xaml.Markup.XamlReader](https://learn.microsoft.com/en-us/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.markup.xamlreader?view=windows-app-sdk-1.3) for WinUI3/WinAppSDK apps. – Andrew KeepCoding Aug 21 '23 at 23:31