Hide primary key column

1 Answer 157 Views
DataGrid
Rob
Top achievements
Rank 1
Rob asked on 31 Aug 2022, 12:52 PM

I use a telerik datagrid to display my data in UWP. I want to hide the Id column because this is automatically increased field in my SQLite database. I use it however in various methods. the command this.["Name of the grid"].Columns[1].Visability = false produces an exeption,

I tried various other solutions but nothing seems to work.

Regards Rob

I use: Visual studio 2019

Telerik.UI.for.UWP 1.0.2.9

code XAML:

<Page
    x:Class="GolfComp.Views.KlasseListPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls" 
    xmlns:telerikGrid="using:Telerik.UI.Xaml.Controls.Grid"
    mc:Ignorable="d" 
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>

        <RelativePanel>
            <CommandBar x:Name="mainCommandBar" HorizontalAlignment="Stretch" IsEnabled="{x:Bind enableCommandBar}">

                <AppBarButton
                    Click="Insert_new_entry"
                    Icon="Add"
                    Label="New"
                    ToolTipService.ToolTip="New record"/>
                <AppBarButton
                    Click="Delete_Click"
                    Icon="Delete"
                    Label="Delete"
                    ToolTipService.ToolTip="Delete record" />
                <AppBarButton
                    Click="Refresh_Click"
                    Icon="Refresh"
                    Label="Update"
                    ToolTipService.ToolTip="Update record" />
            </CommandBar>
            <StackPanel
                x:Name="newRecordStackPanel"
                Orientation="Horizontal"                
                RelativePanel.Below="mainCommandBar">
                <TextBox
                    Header="Naam Competitie"
                    PlaceholderText="naam"
                    Margin="8,8,16,8"
                    MinWidth="200"
                    x:Name="NaamTextBox" />
                <TextBox
                    Header="Klasse"
                    PlaceholderText="klasse"
                    Margin="8,8,16,8"
                    MinWidth="200"
                    x:Name="klasseTextBox" />
                <AppBarButton x:Name="DeleteNewRecord" Click="DeleteNewRecord_Click" Icon="Cancel"/>
                <AppBarButton x:Name="SaveNewRecord" Click="SaveNewRecord_Click" Icon="Save"/>
            </StackPanel>        
            
            <telerikGrid:RadDataGrid
             x:Name="DataGrid"
             BorderThickness="0"
             ColumnDataOperationsMode="Flyout"
             GridLinesVisibility="None"
             GroupPanelPosition="Left"
             RelativePanel.AlignLeftWithPanel="True"
             RelativePanel.AlignRightWithPanel="True"
             RelativePanel.Below="newRecordStackPanel"                 
             ItemsSource="{x:Bind klasse}"
             UserEditMode="Inline"              
             SelectedItem="{x:Bind SelectedItem, Mode=TwoWay}"/>
            
            
        </RelativePanel>
    </Grid>
    

</Page>   

 

1 Answer, 1 is accepted

Sort by
0
Lance | Manager Technical Support
Telerik team
answered on 31 Aug 2022, 01:12 PM

Hello Rob,

I can give you two options to achieve your goal.

Option 1

The property you want to use is the MyColumn.IsVisible instead of MyColumn.Visibility.

Note: make sure that the DataGrid has had time to render all the columns before you start modifying them.

Option 2

You can to turn off AutoGenerateColumns feature

 <telerikGrid:RadDataGrid
            AutoGenerateColumns="False"
             x:Name="DataGrid"
...

Then define the columns you do want to show, you can learn more about column definitions here => https://docs.telerik.com/devtools/universal-windows-platform/controls/raddatagrid/columns/datagrid-overview 

Regards,
Lance | Manager Technical Support
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Rob
Top achievements
Rank 1
commented on 31 Aug 2022, 02:03 PM

Hi Lance,

 

Thank you for your quick response. Unfortunately I encounter problems with both solutions and I can't figure out what's wrong.

My description of the Mycolumn.Visability was a typo. I used MyColumn.isVisable but it still throws the exeption.  below you find a piece of code where I implemented it.

I tried you second solution as well. I can't define the columns using <telerikGrid:RadDataGrid.Columns></telerikGrid:RadDataGrid.Columns> because intellisense tells me "The attachable property 'Columns' was not found in type RadDataGrid."

I am a relative novice to C# and XAML and I do make many mistakes but this one is not to be found on the internet. 

What am I missing?

namespace GolfComp.Views
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class KlasseListPage : Page, INotifyPropertyChanged
    {
        ObservableCollection<KlasseModel> klasse = new ObservableCollection<KlasseModel>();
        bool enableCommandBar = true;        
        public KlasseListPage()
        {
            this.InitializeComponent();
            IntializeKlasseList();
            

            newRecordStackPanel.Visibility = Visibility.Collapsed;
            DataGrid.Columns[1].IsVisible = false; // I tried [0], [1] It doesn't work
        } 
        private void IntializeKlasseList()
        {
            string sql = "select * from Klasse";
            var klasselijst = SqliteDataAccess.LoadData<KlasseModel>(sql, new Dictionary<string, object>());

            klasselijst.ForEach(x => klasse.Add(x));
        }
        private void Add_Click(object sender, RoutedEventArgs e)
        {

            throw new NotImplementedException();

        }

        private void Delete_Click(object sender, RoutedEventArgs e)
        {
            string sql = "delete from Klasse where Id = @Id";

            Dictionary<string, object> parameters = new Dictionary<string, object>
            {
                { "@Id", SelectedItem.Id }
            };
            SqliteDataAccess.DeleteData(sql, parameters);
            klasse.Clear();
            IntializeKlasseList();
        }

        private void Refresh_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }
        private void Insert_new_entry(object sender, RoutedEventArgs e)
        {
            enableCommandBar = false;
            newRecordStackPanel.Visibility = Visibility.Visible;
        }

        private (bool isValid, KlasseModel model) ValidateForm()
        {
            bool isValid = true;
            KlasseModel model = new KlasseModel();

            try
            {
                model.Naam = NaamTextBox.Text;
                model.Klasse = klasseTextBox.Text;
            }
            catch
            {
                isValid = false;
            }
            return (isValid, model);
        }

        private void DeleteNewRecord_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();

        }
        private void SaveNewRecord_Click(object sender, RoutedEventArgs e)
        {
            KlasseModel model = new KlasseModel();

            string sql = "insert into Klasse (Naam, Klasse) values (@Naam, @Klasse)";

            var form = ValidateForm();
            if(form.isValid == false)
            {
                MessageDialog messagedialog = new MessageDialog("foutieve invoer. Probeer het opnieuw");

                _= messagedialog.ShowAsync();

                return;
            }

            if(NaamTextBox.Text == "" || klasseTextBox.Text == "")
            {
                MessageDialog messagedialog = new MessageDialog("Vul alle velden in");

                _ = messagedialog.ShowAsync();

                return;
            }


            Dictionary<string, object> parameters = new Dictionary<string, object>
            {
                {"@Naam", form.model.Naam },
                {"@Klasse", form.model.Klasse }
            };
            SqliteDataAccess.SaveData(sql, parameters);
            klasse.Add(form.model);

            NaamTextBox.Text = "";
            klasseTextBox.Text = "";

            enableCommandBar = true;
            newRecordStackPanel.Visibility = Visibility.Collapsed;

            klasse.Clear();
            IntializeKlasseList();

            MessageDialog dialog = new MessageDialog("Database update gelukt");
            _ = dialog.ShowAsync();

        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

        private KlasseModel _selectedItem;

        public KlasseModel SelectedItem
        {
            get => _selectedItem;
            set
            {
                if (_selectedItem != value)
                {
                    _selectedItem = value;
                    OnPropertyChanged();
                }
            }
        }

        private void SelectedRecord()
        {
            throw new NotImplementedException();

        }
    }
}

 

 

 

 

Lance | Manager Technical Support
Telerik team
commented on 31 Aug 2022, 03:28 PM

Hi Rob,

Don't worry, you'll quickly get accustomed to the lifecycle and nuances of XAML and C#. It's very powerful and fun to use, but occasionally there are things you'll want to keep in mind when building your application.

Before I go into the question-at-hand, you've accidentally posted an "Answer" instead of replying to me in a Comment. Not a problem, I manually converted your Answer to a Comment.

Okay, let's address the problem.

In short, to answer your issue is you didn't follow my advice in the italics. the code is attempting to access something that just doesn't exist yet. It did not give the DataGrid any time to create any columns yet. Here's a screenshot to better explain:

So, to fix this, you need to think about the lifecycle of your page and ask yourself some questions:

  • When does it actually get the data?
  • When does it assign that data to the RadDataGrid?
  • When does the DataGrid render to the UI and generate columns?

Yes, you do call IntializeKlasseList() in the CTOR before the DataGrid code... but the CTOR is the worst place to do this because it is executed synchronously before the page has loaded and any downstream things (like auto column generation) only happens after the constructor. Therefore, I strongly discourage you from using the CTOR for anything that is dynamic... just new-up the page's required objects there.

My suggestion would be to make your page a little more asynchronous and responsive (remember, the page will take a very long time to appear if you have a lot of data in the database, because the CTOR is synchronous, it will hang the UI thread until it is done with the work). The best option is to use the OnNavigatedTo to load your data and show a busy animation, this give the user the experience of a very fast and snappy experience.

Finally, to handle the column problem, my first piece of advice is to never use an indexer (myList[x]) unless you are absolutely positive that index is available, because it will crash the application due to an IndexOutOfRangeException.

Instead, listen to the columns collection changing and then hide the Id column if it is there.

public sealed partial class KlasseListPage : Page, INotifyPropertyChanged
{
    private ObservableCollection<KlasseModel> klasse = new ObservableCollection<KlasseModel>();
    private bool enableCommandBar = true;

    public KlasseListPage()
    {
        InitializeComponent();

        newRecordStackPanel.Visibility = Visibility.Collapsed;

        // Because you're using AutoGenerateColumns=True, you can listen for changes to that collection
        DataGrid.Columns.CollectionChanged += Columns_CollectionChanged;
    }

    private void Columns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        // Make sure that you only execute your logic ONLY when there were columns added and there are more than 1
        if (e.NewItems != null && e.NewItems.Count > 0)
        {
            // Look for the column with the "Id" header
            var possibleIdColumn = DataGrid.Columns.FirstOrDefault(c => c.Header.ToString() == "Id");

            // If that column is found, you can hide it
            if (possibleIdColumn != null)
            {
                possibleIdColumn.IsVisible = false;
            }
        }
    }

    // PERFORMANCE & UX TIP 
    // Avoid loading data in a constructor, it can dramatically slow down your application
    // Instead, use the OnNavigatedTo method and 
    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        // UWP navigation caches pages for back navigation. Check to see if we've already loaded the data
        if(klasse.Any()) return;

        // Show the busy indicator
        BusyIndicator.IsActive = true;

        // Get the data
        IntializeKlasseList();

        // Hide the busy indicator
        BusyIndicator.IsActive = false;
    }
...
}

Rob
Top achievements
Rank 1
commented on 31 Aug 2022, 06:13 PM

Hi Lance,

 

Thanx again for the response.

You make some excellent points and I am eager to implement these. 

You suggest to listen for a change in Column collection? There will be no change in the number of columns. It a fixed table. I insert a breakpoint for the debugging and it showed null so it never comes to executing the if statements:

if (e.NewItems != null && e.NewItems.Count > 0)
        {
            // Look for the column with the "Id" header
            var possibleIdColumn = DataGrid.Columns.FirstOrDefault(c => c.Header.ToString() == "Id");

            // If that column is found, you can hide it
            if (possibleIdColumn != null)
            {
                possibleIdColumn.IsVisible = false;
            }
        }

There will be few changes in records but the column layout will never change.  That goes for all tables in this database. All tables have an Integer Id field as first column and it is the primary key with auto incrementation. All models use the Id column for joins and queries and I don't want the users to see and/or modify this id column. The column is always there otherwise the whole program wouldn't work. Maybe it's not best practice but this UWP project is part of an exam and not for real use.

I hope I explained my problem a little better. I looking forward to your answer.

Cheers Rob.

Lance | Manager Technical Support
Telerik team
commented on 31 Aug 2022, 08:19 PM

I think you're forgetting about the lifecycle of the DataGrid. It always starts with 0 columns, but will add columns when you set the DataGrid's ItemsSource property. This is why the Columns.CollectionChanged event is useful for you.

Because you chose to use AutoGenerateColumns=True (this is the default), it will create the appropriate column based on the properties of your object when you set the DataGrid.ItemsSource.

If you want to keep using AutoGenerateColumns=True, maybe a small change in approaches will help clarify things... instead of using x:Bind, we'll set it in the code-behind. This might help you understand when things happen.

In the following code, remove the red part from your code

<telerikGrid:RadDataGrid
             x:Name="DataGrid"
             BorderThickness="0"
             ColumnDataOperationsMode="Flyout"
             GridLinesVisibility="None"
             GroupPanelPosition="Left"
             RelativePanel.AlignLeftWithPanel="True"
             RelativePanel.AlignRightWithPanel="True"
             RelativePanel.Below="newRecordStackPanel"                 
             ItemsSource="{x:Bind klasse}"
             UserEditMode="Inline"              
             SelectedItem="{x:Bind SelectedItem, Mode=TwoWay}"/>

 

Now, in the code-behind, set the DataGrid.ItemsSource after you get the data for the first time

protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); // UWP navigation caches pages for back navigation. Check to see if we've already loaded the dataif(klasse.Any()) return; // Show the busy indicator BusyIndicator.IsActive = true; // Get the data IntializeKlasseList(); // Set the ItemsSource of the DataGrid if(DataGrid.ItemsSource == null) DataGrid.ItemsSource = this.klasse;// Hide the busy indicator BusyIndicator.IsActive = false; }

 

Alternate Option

If you do not want to have to manage this, then I highly recommend you turn off auto-generation and manually set only the columns you want the DataGrid to have

<telerikGrid:RadDataGrid
             AutoGenerateColumns="False"
             x:Name="DataGrid"
             BorderThickness="0"
             ColumnDataOperationsMode="Flyout"
             GridLinesVisibility="None"
             GroupPanelPosition="Left"
             RelativePanel.AlignLeftWithPanel="True"
             RelativePanel.AlignRightWithPanel="True"
             RelativePanel.Below="newRecordStackPanel"                 
             ItemsSource="{x:Bind klasse}"
             UserEditMode="Inline"              
             SelectedItem="{x:Bind SelectedItem, Mode=TwoWay}">
    <telerikGrid:RadDataGrid.Columns>
        <telerikGrid:DataGridTextColumn PropertyName="Naam" Header="Naam" />
        <telerikGrid:DataGridTextColumn PropertyName="Klasse" Header="Klasse" />
    </telerikGrid:RadDataGrid.Columns>
</telerikGrid:RadDataGrid>

Rob
Top achievements
Rank 1
commented on 01 Sep 2022, 10:35 AM

You were right. I realized later the datagrid is always empty until filled. 

I tried your first solution in the "code behind" but it was still missing the "Id" column and stopped before all if's. I mover the var possibleIdColumn before the if statement to see if it contained anything. It was null. I didn't want to figure this out and tried your alternative method in XAML. This works so problem solved!

Thank you for your patience and solutions.

Regards Rob.

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