Forum Xamarin.Forms

Can you solve this(iteration problem)?

AlexandruFilipescuAlexandruFilipescu Member ✭✭
edited January 11 in Xamarin.Forms

This is my first post, after a day that I could not solve the problem I have...

So, I have in Grades.xaml a grid with 2 labels and Some Frames with Labels.

  • In Grades.xaml.cs I generate 100 Rows for the 5 columns I have with 2 for loops
  • Those 2 for loops Generate Frames that have a Content that is of type Entry, also each Frame is added to the Grid as a children
    I am not able to gain access to the value of the entry like the text or the style Id that is Generated for them in the For loops, it throws as it is empty or does not exist.



So I have problems with the casts I guess... Thank you!

Best Answers

  • igorkr_10igorkr_10 Member ✭✭✭✭
    edited January 12 Accepted Answer

    First, for better reading use pattern matching (is operator)

    Instead of this:

    foreach (View v in grid.Children)
                {
                    if (v.GetType() == typeof(Frame))
                    {
                        Frame frame = (Frame)v;
                        var entry = frame.Content as Entry;
                        Console.WriteLine("**** Entry StyleId" + entry.StyleId);                    
                    }
                }
    

    Use this:

    foreach (View gridChild in grid.Children)
                {
                    if (gridChild is Frame frame)
                    {
                        if (frame.Content is Entry entry)
                        {
                            Console.WriteLine($"**** Entry StyleId {entry.StyleId}");
                        }
                    }
                }
    

    Second, use ListView or ColletionView. These controls use virtualization to improve performance.

    <Grid RowDefinitions="Auto, *">
                <Grid Grid.Row="0" ColumnDefinitions="*, *, *, *, *">
                    <Frame Padding="0" Grid.Column="0">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Discipline &#10; de învățat"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="1">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Felul probei"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="2">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Calificativul/&#10;nota"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="3">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Data&#10;examinării"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="4">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Semnătura&#10;examinatorului"/>
                    </Frame>
                </Grid>
                <ListView Grid.Row="1"
                          ItemsSource="{Binding Items, Mode=OneWay}"
                          CachingStrategy="RecycleElement"
                          HasUnevenRows="True">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid RowDefinitions="Auto" ColumnDefinitions="*, *, *, *, *">
                                    <Entry Grid.Column="0" Text="{Binding Discipline}"/>
                                    <Entry Grid.Column="1" Text="{Binding Felul}"/>
                                    <Entry Grid.Column="2" Text="{Binding Calificativul}"/>
                                    <Entry Grid.Column="3" Text="{Binding Data}"/>
                                    <Entry Grid.Column="4" Text="{Binding Semnatura}"/>
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Grid>
    

    Third, in loops operate with data, not controls.

    public class Grade
        {
            public string Discipline { get; set; }
            public string Felul { get; set; }
            public string Calificativul { get; set; }
            public string Data { get; set; }
            public string Semnatura { get; set; }
        }
    
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class Grades : ContentPage
        {
            public ObservableCollection<Grade> Items { get; }
    
            public Grades()
            {
                InitializeComponent();
                this.Items = new ObservableCollection<Grade>();
                this.BindingContext = this;
            }
    
            protected override void OnAppearing()
            {
                base.OnAppearing();
                this.FillItems();
            }
    
    //In normal case you will have here request to database or server to get data and populate them your collection
            private void FillItems()
            {
                for (int i = 0; i < 100; i++)
                {
                    this.Items.Add(new Grade() { Calificativul = $"Calificativul{i}", Data = $"Data{i}", Discipline = $"Discipline{i}", Felul = $"Felul{i}", Semnatura = $"Semnatura{i}" });
                }
            }
        }
    

    Fourth, if you need to store data don't use Preferences. Preferences is not database. Use it for user settings only. To store a big count of same data use database.
    F. e. use Entity Framework Core
    EF Core is ORM which gives you access to database.

    And property StyleId is not the best thing. Your data must know nothing about your controls.

  • igorkr_10igorkr_10 Member ✭✭✭✭
    Accepted Answer

    @AlexandruFilipescu said:
    @igorkr_10 Thank you for your extremely detailed reply! I did not image someone could give the exact solution for the other person asking the question on a forum! This was exactly what I was looking for, thank you Mr Igor. You saved me as the next day I will present the project as it is but I will certainly apply ListViews to my code because as you saw it lags a bit now when you click on "Note". Thank you, you where the reply that I was waiting for from when I posted. Do you work at a company that uses Xamarin? How can I find you on Facebook or Instagram?

    To prevent from that lag try to put some delay in OnAppearing to start load data when side menu is closed. This is a bug in Shell https://github.com/xamarin/Xamarin.Forms/issues/5216 you will find some workarounds there.

    private bool _isLoading;
            public bool IsLoading
            {
                get { return _isLoading; }
                set
                {
                    if (_isLoading != value)
                    {
                        _isLoading = value;
                        this.OnPropertyChanged(nameof(IsLoading));
                    }
                }
            }
    
    protected override async void OnAppearing()
            {
                base.OnAppearing();
                //Give a time for side menu to close
                await Task.Delay(300);
                await this.LoadData();
            }
    
            private async Task LoadData()
            {
                try
                {
                    this.IsLoading = true;
                    this.Items.Clear();
                    //Simulating request
                    await Task.Delay(500);
                    for (int i = 0; i < 100; i++)
                    {
                        this.Items.Add(new Grade() { Calificativul = $"Calificativul{i}", Data = $"Data{i}", Discipline = $"Discipline{i}", Felul = $"Felul{i}", Semnatura = $"Semnatura{i}" });
                    }
                }
                catch(Exception ex)
                {
                    await this.DisplayAlert("Error", ex.Message, "OK");
                }
                finally
                {
                    this.IsLoading = false;
                }
            }
    
    <ListView Grid.Row="1"
                          ItemsSource="{Binding Items, Mode=OneWay}"
                          CachingStrategy="RecycleElement"
                          IsRefreshing="{Binding IsLoading, Mode=OneWay}"
                          HasUnevenRows="True">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid RowDefinitions="Auto" ColumnDefinitions="*, *, *, *, *">
                                    <Entry Grid.Column="0" Text="{Binding Discipline}"/>
                                    <Entry Grid.Column="1" Text="{Binding Felul}"/>
                                    <Entry Grid.Column="2" Text="{Binding Calificativul}"/>
                                    <Entry Grid.Column="3" Text="{Binding Data}"/>
                                    <Entry Grid.Column="4" Text="{Binding Semnatura}"/>
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
    

    You can send a letter to me on this forum.

Answers

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    If you post a repo on github I can try to take a look, but I think you should use a listview (or a contentview) for this scenario, and databinding to accessa data.

  • AlexandruFilipescuAlexandruFilipescu Member ✭✭
    edited January 12

    First of all thank you for your reply @AlessandroCaliaro ! I posted on Google Drive as the file was too big for Github....

    h ttps://drive.google.com/file/d/1O_CEztnCwNqfaFUs1AiAGQLKuau7f0E4/view?usp=sharing

    This is my first project with Xamarin, I just wanted to save all the data from those Entries with Xamarin.Essentials Preferences... But I cant even manage to access them, grazie!

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭
                    if(v.GetType() == typeof(Frame))
                    {
                    Frame frame = (Frame)v;
                    if (frame.Content != null && frame.Content is Entry)
                    {
                        var entry = frame.Content as Entry;
    
                        Console.WriteLine("**** Entry StyleId" + entry.StyleId);
    
                    }
                    //Preferences.Set(entry.StyleId, entry.Text);
                    }
    

    With this I have no error:

    **** Entry StyleIdEntry21
    **** Entry StyleIdEntry22
    01-12 10:27:22.564 I/mono-stdout(16768): **** Entry StyleIdEntry21
    **** Entry StyleIdEntry23
    **** Entry StyleIdEntry24
    **** Entry StyleIdEntry30
    **** Entry StyleIdEntry31
    **** Entry StyleIdEntry32
    **** Entry StyleIdEntry33
    01-12 10:27:22.565 I/mono-stdout(16768): **** Entry StyleIdEntry22
    **** Entry StyleIdEntry34
    **** Entry StyleIdEntry40
    **** Entry StyleIdEntry41
    **** Entry StyleIdEntry42
    01-12 10:27:22.565 I/mono-stdout(16768): **** Entry StyleIdEntry23
    **** Entry StyleIdEntry43
    **** Entry StyleIdEntry44

    And I see a lot of this.

    But I suggest to take a look to CollectionView and DataBinding...

  • AlexandruFilipescuAlexandruFilipescu Member ✭✭
    edited January 12

    @AlessandroCaliaro I forgot to tell you that the error happens when you click on the "Salvare" Button on the "Note Page", after you click the hamburger menu... I will take a look!

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @AlexandruFilipescu said:
    @AlessandroCaliaro I forgot to tell you that the error happens when you click on the "Salvare" Button on the "Note Page", after you click the hamburger menu... I will take a look!

    Yes, I see

  • igorkr_10igorkr_10 Member ✭✭✭✭
    edited January 12 Accepted Answer

    First, for better reading use pattern matching (is operator)

    Instead of this:

    foreach (View v in grid.Children)
                {
                    if (v.GetType() == typeof(Frame))
                    {
                        Frame frame = (Frame)v;
                        var entry = frame.Content as Entry;
                        Console.WriteLine("**** Entry StyleId" + entry.StyleId);                    
                    }
                }
    

    Use this:

    foreach (View gridChild in grid.Children)
                {
                    if (gridChild is Frame frame)
                    {
                        if (frame.Content is Entry entry)
                        {
                            Console.WriteLine($"**** Entry StyleId {entry.StyleId}");
                        }
                    }
                }
    

    Second, use ListView or ColletionView. These controls use virtualization to improve performance.

    <Grid RowDefinitions="Auto, *">
                <Grid Grid.Row="0" ColumnDefinitions="*, *, *, *, *">
                    <Frame Padding="0" Grid.Column="0">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Discipline &#10; de învățat"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="1">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Felul probei"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="2">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Calificativul/&#10;nota"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="3">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Data&#10;examinării"/>
                    </Frame>
                    <Frame Padding="0" Grid.Column="4">
                        <Label HorizontalTextAlignment="Center" VerticalTextAlignment="Center" Text="Semnătura&#10;examinatorului"/>
                    </Frame>
                </Grid>
                <ListView Grid.Row="1"
                          ItemsSource="{Binding Items, Mode=OneWay}"
                          CachingStrategy="RecycleElement"
                          HasUnevenRows="True">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid RowDefinitions="Auto" ColumnDefinitions="*, *, *, *, *">
                                    <Entry Grid.Column="0" Text="{Binding Discipline}"/>
                                    <Entry Grid.Column="1" Text="{Binding Felul}"/>
                                    <Entry Grid.Column="2" Text="{Binding Calificativul}"/>
                                    <Entry Grid.Column="3" Text="{Binding Data}"/>
                                    <Entry Grid.Column="4" Text="{Binding Semnatura}"/>
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </Grid>
    

    Third, in loops operate with data, not controls.

    public class Grade
        {
            public string Discipline { get; set; }
            public string Felul { get; set; }
            public string Calificativul { get; set; }
            public string Data { get; set; }
            public string Semnatura { get; set; }
        }
    
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class Grades : ContentPage
        {
            public ObservableCollection<Grade> Items { get; }
    
            public Grades()
            {
                InitializeComponent();
                this.Items = new ObservableCollection<Grade>();
                this.BindingContext = this;
            }
    
            protected override void OnAppearing()
            {
                base.OnAppearing();
                this.FillItems();
            }
    
    //In normal case you will have here request to database or server to get data and populate them your collection
            private void FillItems()
            {
                for (int i = 0; i < 100; i++)
                {
                    this.Items.Add(new Grade() { Calificativul = $"Calificativul{i}", Data = $"Data{i}", Discipline = $"Discipline{i}", Felul = $"Felul{i}", Semnatura = $"Semnatura{i}" });
                }
            }
        }
    

    Fourth, if you need to store data don't use Preferences. Preferences is not database. Use it for user settings only. To store a big count of same data use database.
    F. e. use Entity Framework Core
    EF Core is ORM which gives you access to database.

    And property StyleId is not the best thing. Your data must know nothing about your controls.

  • AlexandruFilipescuAlexandruFilipescu Member ✭✭
    edited January 12

    @igorkr_10 Thank you for your extremely detailed reply! I did not image someone could give the exact solution for the other person asking the question on a forum! This was exactly what I was looking for, thank you Mr Igor. You saved me as the next day I will present the project as it is but I will certainly apply ListViews to my code because as you saw it lags a bit now when you click on "Note". Thank you, you where the reply that I was waiting for from when I posted. Do you work at a company that uses Xamarin? How can I find you on Facebook or Instagram?

  • igorkr_10igorkr_10 Member ✭✭✭✭
    Accepted Answer

    @AlexandruFilipescu said:
    @igorkr_10 Thank you for your extremely detailed reply! I did not image someone could give the exact solution for the other person asking the question on a forum! This was exactly what I was looking for, thank you Mr Igor. You saved me as the next day I will present the project as it is but I will certainly apply ListViews to my code because as you saw it lags a bit now when you click on "Note". Thank you, you where the reply that I was waiting for from when I posted. Do you work at a company that uses Xamarin? How can I find you on Facebook or Instagram?

    To prevent from that lag try to put some delay in OnAppearing to start load data when side menu is closed. This is a bug in Shell https://github.com/xamarin/Xamarin.Forms/issues/5216 you will find some workarounds there.

    private bool _isLoading;
            public bool IsLoading
            {
                get { return _isLoading; }
                set
                {
                    if (_isLoading != value)
                    {
                        _isLoading = value;
                        this.OnPropertyChanged(nameof(IsLoading));
                    }
                }
            }
    
    protected override async void OnAppearing()
            {
                base.OnAppearing();
                //Give a time for side menu to close
                await Task.Delay(300);
                await this.LoadData();
            }
    
            private async Task LoadData()
            {
                try
                {
                    this.IsLoading = true;
                    this.Items.Clear();
                    //Simulating request
                    await Task.Delay(500);
                    for (int i = 0; i < 100; i++)
                    {
                        this.Items.Add(new Grade() { Calificativul = $"Calificativul{i}", Data = $"Data{i}", Discipline = $"Discipline{i}", Felul = $"Felul{i}", Semnatura = $"Semnatura{i}" });
                    }
                }
                catch(Exception ex)
                {
                    await this.DisplayAlert("Error", ex.Message, "OK");
                }
                finally
                {
                    this.IsLoading = false;
                }
            }
    
    <ListView Grid.Row="1"
                          ItemsSource="{Binding Items, Mode=OneWay}"
                          CachingStrategy="RecycleElement"
                          IsRefreshing="{Binding IsLoading, Mode=OneWay}"
                          HasUnevenRows="True">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <Grid RowDefinitions="Auto" ColumnDefinitions="*, *, *, *, *">
                                    <Entry Grid.Column="0" Text="{Binding Discipline}"/>
                                    <Entry Grid.Column="1" Text="{Binding Felul}"/>
                                    <Entry Grid.Column="2" Text="{Binding Calificativul}"/>
                                    <Entry Grid.Column="3" Text="{Binding Data}"/>
                                    <Entry Grid.Column="4" Text="{Binding Semnatura}"/>
                                </Grid>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
    

    You can send a letter to me on this forum.

  • AlexandruFilipescuAlexandruFilipescu Member ✭✭
    edited January 13

    @igorkr_10 Awesome I implemented some of the concepts you taught me, also Entity Framework would not make the program over complicated? Is not enough Preferences for this one? Thank you in advance!

Sign In or Register to comment.