Checkbox in DataGrid

1 Answer 340 Views
CheckBox DataGrid
Pat
Top achievements
Rank 1
Pat asked on 15 Jul 2022, 06:36 PM

I have been building a test app to see if the controls and UI layout we need can work using Telerik .net maui.

I have seen many documents about the RadDataGrid and others about the RadCheckBox BUT, I have not seen any examples that show how to turn one of the returned data columns into a checkbox.

I have tried 2 methods for creating the RadDataGrid;

1) Building the headers individually (this did not produce data rows after fetching)

2) simply creating the RadDataGrid and assigning its ItemSource (this created the columns and produced data rows after fetching)

 

I am using an ObservableCollection to house all my row(s) data which is what I tie the RadDataGrid ItemSource to using the .Add function

 

When I did try the 1st method, I could not bind to the properties of each item in the ObservableCollection which may be either a code issue or a brain (me) issue, not sure because I tried to follow the examples Telerik has put out.

 

I saw a note that if you set the RadDataGrid AutoGenerateColumns value to "True" then Telerik will build the columns according to the underlying data type (ie. if one of the columns shows bool data, it will turn that into a checkbox)....Correct me if I am wrong on that one

I could not get that option to work either, just displayed "True" or "False" in the UI.

I am currently not using the MVVM pattern... All work is done in Code-Behind for simplicity...

Can anyone show me how this can be accomplished or if it cannot be? Also, is there any better documentation than what is found here ( https://docs.telerik.com/devtools/maui/controls/datagrid/overview )

 

If I can ask one follow up...is there a feature for drag and drop?

 

1 Answer, 1 is accepted

Sort by
0
Lance | Manager Technical Support
Telerik team
answered on 15 Jul 2022, 07:57 PM

Hello Pat,

As long as one of the properties on the data item is a bool, you can use the BooleanColumn. The BooleanColumn already uses a CheckBox for the editor by default.

Before I go into what an editor is and the difference between display mode and edit mode, let's address the confusion about column generation.

Setting Columns

As you've seen, there's two ways to get a column... we create it for you, or you manually define it.

Automatic

If you leave AutoGenerateColumns="True" (which is the default), we will automatically create the BooleanColumn for every bool property on your data model.

Manual

If you want to manually define the column (which is what I think you meant by "building the headers"), then you first disable AutoGenerateColumns (set it to false) and then you need to tell that column which property to bind to.

<telerik:RadDataGrid.Columns>
    <telerik:DataGridBooleanColumn PropertyName="MyBoolPropertyName" />
<telerik:RadDataGrid.Columns>

Demo

For example, let's say we have an ObservableCollection of type Car:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace YourProject.DataModels;

public class Car : BindableBase
{
    private string name;
    private bool inStock;

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }

    public bool InStock
    {
        get => inStock;
        set => SetProperty(ref inStock, value);
    }
}

// Helper class that provides PropertyChanged support through 'SetProperty'
public class BindableBase: INotifyPropertyChanged
{
    protected bool SetProperty<T>(
        ref T backingStore, T value,
        [CallerMemberName]string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
            
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }
        
    public event PropertyChangedEventHandler PropertyChanged;
        
    protected void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

*Notice how I am using INotifyPropertyChanged. this is critical when using any kind of databinding. The setters of each property invoke OnPropertychanged (though a cleaner BindableBase helper class). this is what lets the UI know when the value of any property changes.

Since you said you're not using MVVM, then we can just set the DataGrid's ItemsSource directly with a few Cars

using System.Collections.ObjectModel;

namespace DataGridExample;

public partial class MainPage : ContentPage
{
    private readonly ObservableCollection<Car> cars;

	public MainPage()
	{
		InitializeComponent();

        cars = new ObservableCollection<Car>();
        dataGrid.ItemsSource = cars;

        // Note: This can be done after setting ItemsSource because we are using an ObservableCollection.
        MakeSampleCars();
    }
    
    private void MakeSampleCars()
    {
        if (cars == null) throw new NullReferenceException("cars cannot be null if you want to add a car to it");

        for (int i = 1; i < 51; i++)
        {
            cars.Add(new Car
            {
                Name = $"Car {i}",
                InStock = i % 3 == 0 //this just makes every 3rd item True
            });
        }
    }
}

Styling

You explicitly mentioned checkboxes. There is something to keep in mind that is very important. A DataGrid cell lives in one of two states:

  • "display mode" - this is when the rows are rendered and the user is scrolling through the items. 
  • "edit mode" - this is when the cell which change form display mode and provide the user with an editor control to change the value of whatever data type that cell is for (e.g. an Entry for text, a checkbox for bool, etc)

The user can put a cell into edit mode by double clicking it, this is only when you will see the checkbox because the entire grid is not in edit mode all the time.

Here's what all of the code we've used so far looks like at runtime:

The reason high performance and UI virtualized components do this is because if we left every cell in edit mode, the performance would be barely usable in a lot of situations. This is due to the sheer number of two-way bindings and complex layouts in editor components.

However, if you did want to show a read-only checkbox instead of the "True" or "False" text, you easily do this with a custom CellContentTemplate for that column and use a Radcheckbox and set Is
Enabled=False.

<telerik:RadDataGrid x:Name="dataGrid" AutoGenerateColumns="False">
    <telerik:RadDataGrid.Columns>
        <!-- A standard textColumn for the name property -->
        <telerik:DataGridTextColumn PropertyName="Name" />

        <!-- A BooleanColumn for the InStock property -->
        <telerik:DataGridBooleanColumn PropertyName="InStock">
            <!-- If you wanted to show a checkbox while is in display mode, you can set the CellContentTemplate
            Important: Make sure you have set IsEnabled=False so the user cannot change the value when the cell is not in edit mode 
            -->
            <telerik:DataGridColumn.CellContentTemplate>
                <DataTemplate>
                    <telerik:RadCheckBox IsChecked="{Binding InStock}" IsEnabled="False"/>
                </DataTemplate>
            </telerik:DataGridColumn.CellContentTemplate>

            <!-- If you wanted to use a different control for edit mode, say a Switch, you can set the CellEditTemplate-->
            <!--<telerik:DataGridColumn.CellEditTemplate>
                <DataTemplate>
                    <Switch IsToggled="{Binding InStock}" VerticalOptions="Center"/>
                </DataTemplate>
            </telerik:DataGridColumn.CellEditTemplate>-->
        </telerik:DataGridBooleanColumn>
    </telerik:RadDataGrid.Columns>
</telerik:RadDataGrid>

Now, here's what that looks like at runtime

I have updated one of our previous demos together so that it applies everything I talked about today, find it attached,

TIP

Whenever you download something from the internet that contains trusted code, you should unblock the ZIP file before unzipping it. Otherwise windows will flag it and prevent any DLLs or EXEs from working unless they're signed. You do that by taking the following steps

  1. Right-click on the newly downloaded ZIP file and select "Properties"
  2. Check the "Unblock" checkbox and click OK (if you don't see that checkbox, then that file is not flagged)
  3. Now you can unzip it and use it in VS without concern.

Further Resources

Have you had a chance to see our demos? I've noticed our past couple actions could have been answers much faster if you knew where these resources were.

  1. Go to C:\Program Files (x86)\Progress\Telerik UI for .NET MAUI [version]\Examples on your PC
  2. You will see three, complete, runnable projects each with a unique purpose. Please go to the following documentation to learn what each does and instructions on how to run them => Telerik .NET MAUI Sample Applications | Telerik UI for .NET MAUI.

The first one I recommend is our SdkBrowser project. It shows you how to use all the features of each control, it's basically a real-world use case of what you see in the documentations. This should be your go-to for any questions on how to use a feature that you see in the docs.

Regards,
Lance | Manager Technical Support
Progress Telerik

The Premier Dev Conference is back! 

Coming to you live from Progress360 in-person or on your own time, DevReach for all. Register Today.


Pat
Top achievements
Rank 1
commented on 20 Jul 2022, 06:26 PM

Thank you very much for this info Lance. This is the type of info I could not find in the samples and demo apps. 

One further question if you don't mind answering in this post.

I now have my demo app setup with MVVM.

my permit class is a model in the model folder

 public class Permit
    {
        public bool Permit_Active { get; set; }
        public object Auto_ID { get; set; }
        public object Permit_Nbr { get; set; }
        public object Description { get; set; }
        public object House_Nbr { get; set; }
        public object Street_Name { get; set; }
        public object Unit { get; set; }
        public object Location { get; set; }
        public object Lot { get; set; }
        public object Block { get; set; }
        public object PID { get; set; }

}

 

I have my view and that views datatype is tied to my viewmodel

in the viewmodel:

private Permit permit;

public ObservableCollection<Permit> Permits { get; set; } = new();

 

When I manually set the columns in the view for the dataGrid I can set the datagrid itemsource to Permits which is great. But I am not able to access the properties for each permit to assign to each column (aka Auto_ID, PPermit_Nbr, Description....etc.).

 

Here is my View:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"
             xmlns:viewModel="clr-namespace:MAUI_Prototype"
             x:Class="MAUI_Prototype.MainPage"             
             x:DataType="viewModel:MainPageViewModel">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Entry" x:Key="EntryStyle">
                <Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
                <Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Black}}"/>
            </Style>
            <Style TargetType="Label" x:Key="LabelStyle">
                <Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}"/>
                <Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0,0,0" 
            VerticalOptions="Start">
            <Grid 
                ColumnDefinitions=".25*,.25*,*"
                RowDefinitions="Auto, Auto,*"
                IsEnabled="True">
                <Label 
                    Style="{StaticResource LabelStyle}" 
                    Text="Permit_ID:" 
                    HeightRequest="30"
                    Grid.Column="0" 
                    Grid.Row="0" 
                    Margin="0,0,10,0" 
                    VerticalTextAlignment="Center" 
                    IsEnabled="True" 
                    IsVisible="True"/>
                <Entry 
                    Style="{StaticResource EntryStyle}" 
                    x:Name="PID" 
                    Text="{Binding PIDEntry}"
                    Placeholder="Enter Permit ID" 
                    HeightRequest="30"
                    Grid.Column="1" 
                    Grid.Row="0"/>
                <Button
                    x:Name="FindBtn"
                    Text="Find"
                    HeightRequest="50"
                    Command="{Binding FindCommand}"
                    CommandParameter="{Binding PIDEntry}"
                    SemanticProperties.Hint="Finds the Permit ID from the PIMS DB" 
                    MaximumWidthRequest="105"
                    Grid.Column="0"
                    Grid.Row="1"
                    Margin="0,0,10,0"
                    VerticalOptions="Start"
                    HorizontalOptions="Start"
                />              
                <telerik:RadDataGrid
                        x:Name="DataGrid" 
                        Grid.Column="2"
                        Grid.Row="1"
                        HeightRequest="300"                         
                        UserFilterMode="Disabled"
                        AutoGenerateColumns="True"
                    ItemsSource="{Binding Permits}">
                    <telerik:RadDataGrid.AlternateRowBackgroundStyle>
                        <AppThemeBinding>
                            <AppThemeBinding.Light>
                                <telerik:DataGridBorderStyle BackgroundColor="LightBlue" 
                                         BorderThickness="1"
                                         BorderColor="BlanchedAlmond"/>
                            </AppThemeBinding.Light>
                            <AppThemeBinding.Dark>
                                <telerik:DataGridBorderStyle BackgroundColor="{StaticResource Primary}" 
                                         BorderThickness="1"
                                         BorderColor="{StaticResource Gray300}"/>
                            </AppThemeBinding.Dark>
                        </AppThemeBinding>
                    </telerik:RadDataGrid.AlternateRowBackgroundStyle>
                    <telerik:RadDataGrid.MouseHoverStyle>
                        <AppThemeBinding>
                            <AppThemeBinding.Light>
                                <telerik:DataGridBorderStyle 
                                        BackgroundColor="{StaticResource Gray300}"                                                                 
                                        BorderColor="{StaticResource Primary}" 
                                        BorderThickness="2"/>
                            </AppThemeBinding.Light>
                            <AppThemeBinding.Dark>
                                <telerik:DataGridBorderStyle 
                                        BackgroundColor="white" 
                                        BorderColor="{StaticResource Primary}" 
                                        BorderThickness="2"/>
                            </AppThemeBinding.Dark>
                        </AppThemeBinding>
                    </telerik:RadDataGrid.MouseHoverStyle>
                </telerik:RadDataGrid>

 

 

 

 

 

                    <telerik:RadDataGrid x:Name="DataGridManual"
                             UserFilterMode="Disabled"
                             ItemsSource="{Binding Permits}"                                             
                             UserEditMode="None"
                             SelectionMode="None"
                             UserSortMode="None"
                             AutoGenerateColumns="False"
                             GridLinesThickness="1"
                             GridLinesVisibility="Both"
                             GridLinesColor="#DFDFDF"
                             Grid.Column="2"
                             Grid.Row="2">
                        <telerik:RadDataGrid.Columns>
                            <telerik:DataGridTextColumn HeaderText="Auto ID"
                                                            PropertyName="Auto_ID">
                                <telerik:DataGridTextColumn.CellContentTemplate>
                                    <DataTemplate>
                                        <telerik:RadTextInput BindingContext="{Binding Auto_ID}"
                                    </DataTemplate>
                                </telerik:DataGridTextColumn.CellContentTemplate>
                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Permit Number" 
                                                            PropertyName="Permit_Nbr" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Description"
                                                            PropertyName="Description" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="House #"
                                                            PropertyName="House_Nbr" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Street Name" 
                                                            PropertyName="Street_Name" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Unit"
                                                            PropertyName="Unit" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Location"
                                                            PropertyName="Location" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Lot" 
                                                            PropertyName="Lot" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Block"
                                                            PropertyName="Block" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Owner Name"
                                                            PropertyName="Owner_Name" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Owner Address 1" 
                                                            PropertyName="Owner_Address1" >

                            </telerik:DataGridTextColumn>
                            <telerik:DataGridTextColumn HeaderText="Owner Address 2"
                                                            PropertyName="Owner_Address2" >

                            </telerik:DataGridTextColumn>
                        </telerik:RadDataGrid.Columns>
                    </telerik:RadDataGrid>
            </Grid>            
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

 

 

 

 

What am I missing?

I would be more than happy to do a remote session if that would be more helpful too..

Lance | Manager Technical Support
Telerik team
commented on 20 Jul 2022, 08:36 PM

Hi Pat,

We do have Remote Web Assistance as a feature of Ultimate Support, it's not available for trial support at this time. However, if if you did have Ultimate support, I don't think this would qualify because the problem you're having isn't a problem with the DataGrid (see the scope of support article).

That's actually a bit of good news because it looks like it's just a matter of reversing something you did in the DataGrid's XAML... it has edit mode disabled UserEditMode="None".

I would recommend skimming the DataGrid docs. You don't need to thoroughly read everything, but just explore so that you can familiarize yourself where everything is. A few of the hiccups you've had so far were little things that you would have caught earlier.

I also highly recommend visiting the demos, we also show you editing there, too (see the link in my last reply)

 

Pat
Top achievements
Rank 1
commented on 21 Jul 2022, 01:00 PM

Lance,

again.....thank you for your assistance!...

The issue was not actually the UserEditMode but rather that I didn't set a heightRequest for the manual datagrid. This made it appear as though the data was not connecting, but it was just not showing because there was no free space for it to show.

After setting the HeightRequest, I used both UserEditMode="Cell" and UserEditMode="None"  and the manually built datagrid functioned properly both ways :)

My only suggestion/request would be to set a default height if the developer doesn't set it when setting the columns up manually. It wouldn't need to be anything specific, just enough that you can see a line or two of data...this way we can see our data bindings are working and would quickly know why we can't see more data. This was actually the cause of most of this confusion I had.

 

Lance | Manager Technical Support
Telerik team
commented on 21 Jul 2022, 01:11 PM

Hi Pat,

That sounds like you're using the DataGrid in a parent that doesn't work for children who use UI virtualization. Please visit this article to understand this situation => Controls Are Not Appearing.

It's not specific to our controls, but any component that uses UI virtualization. In the future, if you use DataGrid, ListView, ItemsControl, TreeView, PdfViewer, etc. Anything that renders virtualized items or has an internal canvas or ScrollView, this must be considered.

Pat
Top achievements
Rank 1
commented on 21 Jul 2022, 01:14 PM

They are appearing sir... it was just a matter of not having the height request not set so the viewable datagrid bounds cut off right below the column headers. Once I set the height request to something larger, the data showed.
Lance | Manager Technical Support
Telerik team
commented on 21 Jul 2022, 01:24 PM

The title of the article is a bit misleading, but is still relevant to that situation. You do not have to set a HeightRequest to the DataGrid.. unless it is inside a parent container that doesn't support UI virtualization based children.

For example, if you put the DataGrid is a StackLayout/VerticalStackLayout, it "squeezes" the child element to the minimum amount of space. which is a few pixels taller than the headers.

The same is true if the DataGrid is in a Grid RowDefinition Height=Auto. that Auto means to squeeze the child to the bare minimum amount of space it needs. Which results in the same condition as a stackLayout.

The best way to use the DataGrid is inside a Grid RowDefinition Height="*". This way, that star-size row stretches out to fill the remaining amount of vertical space and lets it's children stretch to fill that space as well. Once gotcha here is that if you place that Grid inside a StackLayout, the entire thing is going to get squeezed and the star-row has no remaining space to use.

That being said, there are many situations that you must put a UI virtualized control inside a parent that doesn't stretch.. in which case you must set a HeightRequest so that the parent provides the child with a minimum amount of space to use.

Tags
CheckBox DataGrid
Asked by
Pat
Top achievements
Rank 1
Answers by
Lance | Manager Technical Support
Telerik team
Share this question
or