Mobiele apps programmeren met Xamarin – Entity Framework – CRUD – Xamarin

print

Deze handleiding maakt deel uit van het programmeertraject:


Inhoud


Wat vooraf ging


Inleiding

In deze handleiding brengen we CRUD in de praktijk.

Deze handleiding bouwt verder op de praktijk handleiding over Entity Framework Relaties.

We hebben reeds onderstaande formulieren aangemaakt:

Weet dat App.Databank een publieke variabele is die u vindt in App.xaml.cs: public static DatabaseContext Databank; en die de verbinding maakt met de DatabaseContext.

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TestEF.Services;
using TestEF.Views;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace TestEF
{
    public partial class App : Application
    {
        public static DatabaseContext Databank;
        //public static string LocatieDB;

        public App(string dbPath)
        {
            InitializeComponent();

            Databank = new DatabaseContext(dbPath);
            MainPage = new NavigationPage(new BlogListViewPage());
        }

        protected override void OnStart()
        {
            // Handle when your app starts
        }

        protected override void OnSleep()
        {
            // Handle when your app sleeps
        }

        protected override void OnResume()
        {
            // Handle when your app resumes
        }
    }
}

We gebruiken dan ook App.Databank voor de databankbewerkingen.

Als u de nieuwe, gewijzigde gegevens wilt behouden na het afsluiten (en opnieuw opstarten) van de app moet u Database.EnsureDeleted(); uit DatabaseContext.cs verwijderen.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Threading.Tasks;
using TestEF.Models;

namespace TestEF.Services
{
    public class DatabaseContext : DbContext
    {
        string _dbPath;

        public DatabaseContext(string dbPath)
        {
            _dbPath = dbPath;
            //Database.EnsureDeleted();
            Database.EnsureCreated();
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_dbPath}");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Name
            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Name)
                .HasMaxLength(100);

            //URL
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();

            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .HasMaxLength(500);

            //Title Post
            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .IsRequired();

            modelBuilder.Entity<Post>()
                .Property(p => p.Title)
                .HasMaxLength(100);


            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 1, Name="PCVO Groeipunt", Url = "https://www.pcvogroeipunt.be" });
            modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = 2, Name="ICT-opleidingen", Url = "https://ictopleidingen.azurewebsites.net" });

            modelBuilder.Entity<Post>().HasData(
               new { BlogId = 1, PostId = 1, Title = "Post 1", Content = "Test 1" },
               new { BlogId = 1, PostId = 2, Title = "Post 2", Content = "Test 2" },
               new { BlogId = 2, PostId = 3, Title = "Post 3", Content = "Test 3" },
               new { BlogId = 2, PostId = 4, Title = "Post 4", Content = "Test 4" });
        }

        public async Task<IEnumerable<Blog>> GetBlogItemsAsync()
        {
            var allItems = await Blogs.ToListAsync().ConfigureAwait(false);
            return allItems;
        }

    }
}

Create (toevoegen)

We wensen Blogs toe te voegen.

Views

View BlogListViewPage

  • Pas View BlogListViewPage aan een Toolbar toe te voegen.
<ContentPage.ToolbarItems>
    <ToolbarItem x:Name="BlogToevoegen" Text="Blog toevoegen" Order="Secondary" Clicked="BlogToevoegen_Clicked" />
</ContentPage.ToolbarItems>

Door te kiezen voor Order="Secondary" wordt rechtsboven een menu voorzien.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TestEF.Views.BlogListViewPage" Title="Mijn Blogs">
    <ContentPage.ToolbarItems>
        <ToolbarItem x:Name="BlogToevoegen" Text="Blog toevoegen" Order="Secondary" Clicked="BlogToevoegen_Clicked" />
    </ContentPage.ToolbarItems>
    <ContentPage.Content>
        <ListView x:Name="MyListView" HasUnevenRows="True" ItemTapped="Handle_ItemTapped" CachingStrategy="RecycleElement" SeparatorVisibility="Default" Margin="5" VerticalOptions="FillAndExpand">
            <ListView.Header>
                <Grid BackgroundColor="LightGray">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="2*" />
                        <ColumnDefinition Width="3*" />
                    </Grid.ColumnDefinitions>

                    <Label Grid.Column="0" Text="ID" FontAttributes="Bold" />
                    <Label Grid.Column="1" Text="Naam" FontAttributes="Bold" />
                    <Label Grid.Column="2" Text="Website" FontAttributes="Bold" />
                </Grid>
            </ListView.Header>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid BackgroundColor="White">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="2*" />
                                <ColumnDefinition Width="3*" />
                            </Grid.ColumnDefinitions>

                            <Label Grid.Column="0" Text="{Binding BlogId}" FontSize="Small" />
                            <Label Grid.Column="1" Text="{Binding Name}" FontSize="Small" />
                            <Label Grid.Column="2" Text="{Binding Url}" FontSize="Small" />
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>        
    </ContentPage.Content>
</ContentPage>

View BlogToevoegenViewPage

Om een Blog toe te voegen heb ik een nieuwe View BlogToevoegenViewPage aangemaakt.

  • Klik rechts op de folder Views.
  • Kies AddNew Item…Xamarin.FormsContent Page en geef het een passende naam BlogToevoegenViewPage.cs.
  • Maak onderstaande XAML aan.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TestEF.Views.BlogToevoegenViewPage" Title="Blog Toevoegen">
    <ContentPage.Content>
        <StackLayout Margin="10">
            <Label Text="Blog toevoegen" FontSize="Large" VerticalOptions="Center" HorizontalOptions="CenterAndExpand" />
            <Entry x:Name="BlogNaam" Placeholder="Naam" />
            <Entry x:Name="BlogURL" Placeholder="URL" />
            <Button x:Name="BlogBewaren" Text="Bewaren" Clicked="BlogBewaren_Clicked" />
            <Button x:Name="Annuleren" Text="Annuleren" Clicked="Annuleren_Clicked" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Ik verkies om deze View Modal te openen (zodat u het eerst moet afsluiten voor u verder kunt).

  • Ga naar de code-behind van BlogListViewPage.XAML.cs en voeg onderstaande code toe aan de BlogToevoegen_Clicked-Event.
using System.Collections.ObjectModel;
using TestEF.Models;
using TestEF.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BlogListViewPage : ContentPage
    {

        BlogListViewModel viewModel;

        public BlogListViewPage()
        {
            InitializeComponent();

            viewModel = new BlogListViewModel();
            MyListView.ItemsSource = viewModel.BlogItems;
        }

        async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item != null)
            {
                Blog blog = (Blog)e.Item;
                await Navigation.PushAsync(new BlogViewPage(blog));
            }

            //Deselect Item
            ((ListView)sender).SelectedItem = null;
        }

        private async void BlogToevoegen_Clicked(object sender, System.EventArgs e)
        {
            await Navigation.PushModalAsync(new BlogToevoegenViewPage());
        }
    }
}
  • Merk op dat de View als een Model wordt asynchroon “gepusht”: await Navigation.PushModalAsync(new BlogToevoegenViewPage());.
  • Zorg ervoor dat u de correcte usings toevoegt.

Bewaren

Het eigenlijke Bewaren gebeurt in de BlogBewaren_Clicked-Event in de View BlogToevoegenViewPage.

Dit gebeurt als volgt:

Blog blog = new Blog { Name = BlogNaam.Text, Url = BlogURL.Text};
App.Databank.Blogs.Add(blog);
App.Databank.SaveChanges();               
  • Er wordt een nieuwe blog aangemaakt en deze krijgt de waarden die we halen uit de tekstvakken: var blog = new Blog { Name = BlogNaam.Text, Url = BlogURL.Text };.
  • Deze nieuwe Blog wordt toegevoegd aan (de entiteit/tabel) Blogs: App.Databank.Blogs.Add(blog);.
  • De DbContext/databank wordt bewaard: App.Databank.SaveChanges();

Er mag enkel maar worden toegevoegd indien zowel de Naam als de URL ingevuld is. We hebben immers tijdens de aanmaak van het Model bepaald dat deze verplicht (Required) zijn. Geven we nu dus geen waarde mee, dan zal een fout gegenereerd worden.

We controleren dit met behulp van onderstaande if-statement.

if(!string.IsNullOrEmpty(BlogNaam.Text) && !string.IsNullOrEmpty(BlogURL.Text))
{
    Blog blog = new Blog { Name = BlogNaam.Text, Url = BlogURL.Text};
    App.Databank.Blogs.Add(blog);
    App.Databank.SaveChanges();  
}
else
{
    await DisplayAlert("Fout", "De naam en URL moeten ingevuld zijn!", "OK");
}            
  • Ga naar de code-behind van BlogToevoegenViewPage.XAML.cs en voeg onderstaande code toe aan de BlogBewaren_Clicked-Event.
  • Voeg ook meteen de code toe om de View af te sluiten in de Annuleren_Clicked-event: await Navigation.PopModalAsync();.
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TestEF.Models;
using System.Linq;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class BlogToevoegenViewPage : ContentPage
	{
		public BlogToevoegenViewPage ()
		{
			InitializeComponent ();
		}

        private async void BlogBewaren_Clicked(object sender, EventArgs e)
        {

            if(!string.IsNullOrEmpty(BlogNaam.Text) && !string.IsNullOrEmpty(BlogURL.Text))
            {
                Blog blog = new Blog { Name = BlogNaam.Text, Url = BlogURL.Text};
                App.Databank.Blogs.Add(blog);
                App.Databank.SaveChanges();  
                await Navigation.PopModalAsync();
            }
            else
            {
                await DisplayAlert("Fout", "De naam en URL moeten ingevuld zijn!", "OK");
            }           
        }

        private async void Annuleren_Clicked(object sender, EventArgs e)
        {
            await Navigation.PopModalAsync();

        }
    }
}

Probleem, de lijst is na het bewaren niet aangepast!

De vorige code werkt, maar niet meteen.

Als u na het bewaren terugkeert naar de lijst, dan ziet u de net toegevoegde blog niet staan. Sluit u de applicatie en start u opnieuw op, dan staat de blog er plots wel (tenminste als u Database.EnsureDeleted(); uit DatabaseContext.cs hebt verwijderd (zie inleiding)).

De reden is dat we terugkeren naar de View die nog in het geheugen staat en die niet automatisch geüpdatet is.

De oplossing bestaat erin om de code, die de lijst aanroept, niet in de constructor te plaatsen maar bij OnAppearing() die overschreven moet worden.

  • Ga naar de code-behind van BlogListViewPage.XAML.cs en voeg onderstaande code toe aan de BlogToevoegen_Clicked-Event.
using System.Collections.ObjectModel;
using TestEF.Models;
using TestEF.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BlogListViewPage : ContentPage
    {

        BlogListViewModel viewModel;

        public BlogListViewPage()
        {
            InitializeComponent();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            viewModel = new BlogListViewModel();
            MyListView.ItemsSource = viewModel.BlogItems;
        }

        async void Handle_ItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item != null)
            {
                Blog blog = (Blog)e.Item;
                await Navigation.PushAsync(new BlogViewPage(blog));
            }

            //Deselect Item
            ((ListView)sender).SelectedItem = null;
        }

        private async void BlogToevoegen_Clicked(object sender, System.EventArgs e)
        {
            await Navigation.PushModalAsync(new BlogToevoegenViewPage());
        }
    }
}             

MVVM alternatief

Bovenstaande oplossing was een “klassieke” benadering. Hieronder bespreek ik een volledige MVVM alternatief.

ViewModel

Eerst maken we een ViewModel aan.

  • Klik rechts op de folder ViewModels.
  • Kies AddNew Item…Class en geef de Class de naam BlogToevoegenViewModel.cs.
  • Maak onderstaande Class aan.
using System.Windows.Input;
using TestEF.Models;
using Xamarin.Forms;

namespace TestEF.ViewModels
{
    public class BlogToevoegenViewModel : BaseViewModel
    {
        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged();
            }
        }

        private string url;
        public string Url
        {
            get { return url; }
            set
            {
                url = value;
                OnPropertyChanged();
            }
        }

        private string melding;
        public string Melding
        {
            get { return melding; }
            set
            {
                melding = value;
                OnPropertyChanged();
            }
        }

        public ICommand SaveCommand { get; private set; }
        public ICommand AnnuleerCommand { get; private set; }

        public BlogToevoegenViewModel()
        {
            SaveCommand = new Command(SaveBlog);
            AnnuleerCommand = new Command(AnnuleerSaveBlog);

            Melding = "";
        }

        async void SaveBlog()
        {
            if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Url))
            {
                Blog blog = new Blog { Name = Name, Url = Url };
                App.Databank.Blogs.Add(blog);
                App.Databank.SaveChanges();

                await Application.Current.MainPage.Navigation.PopModalAsync();
            }
            else
            {
                melding = "De naam en URL moeten ingevuld zijn!";
                OnPropertyChanged("Melding");
            }           
        }

        async void AnnuleerSaveBlog()
        {
            await Application.Current.MainPage.Navigation.PopModalAsync();
        }
    }
}     

Laten we dit eens van naderbij bekijken.

De velden (eigenschappen) die verbonden moeten worden met de View worden als properties aangemaakt.

private string name;
public string Name
{
    get { return name; }
    set
    {
        name = value;
        OnPropertyChanged();
    }
}

private string url;
public string Url
{
    get { return url; }
    set
    {
        url = value;
        OnPropertyChanged();
    }
}

private string melding;
public string Melding
{
    get { return melding; }
    set
    {
        melding = value;
        OnPropertyChanged();
    }
}     

Belangrijk is hier dat OnPropertyChanged(); wordt ingesteld. Dit zorgt ervoor dat er een notificatie wordt “verstuurd” als de betreffende eigenschap, via de Binding wijzigt zodat de rest van de applicatie hiervan op de hoogte is.

Melding is een bijkomende eigenschap die gebruikt wordt om een melding mee te geven over het al dan niet bewaard zijn, een Displayalert() werkt immers niet via een ViewModel.

Commands

In plaats van te werken met events werken we met Commands.

Commands worden gebruikt om interactie met de interface, door te gebruiker, op te vangen en er een commando, programmeercode, aan te koppelen. Ze worden dan ook meestal gebruikt met Buttons of Toolbars. Net zoals een event reageren ze op bv. het klikken op een knop.

Voor we zelf Commands gaan implementeren kunt u even deze video’s bekijken als introductie.

Deze Commands worden eerst gedeclareerd.

public ICommand SaveCommand { get; private set; }
public ICommand AnnuleerCommand { get; private set; }     

Nadien worden ze, in de constructor toegewezen aan een functie/methode.

public BlogToevoegenViewModel()
{
    SaveCommand = new Command(SaveBlog);
    AnnuleerCommand = new Command(AnnuleerSaveBlog);

    Melding = "";
}   

De code van deze methoden verschilt weinig van de “klassieke” code.

async void SaveBlog()
{
    if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Url))
    {
        Blog blog = new Blog { Name = Name, Url = Url };
        App.Databank.Blogs.Add(blog);
        App.Databank.SaveChanges();

        await Application.Current.MainPage.Navigation.PopModalAsync();
    }
    else
    {
        melding = "De naam en URL moeten ingevuld zijn!";
        OnPropertyChanged("Melding");
    }           
}

async void AnnuleerSaveBlog()
{
    await Application.Current.MainPage.Navigation.PopModalAsync();
}   

Merk nog even de code op die de verandering van de eigenschap Melding duidt OnPropertyChanged("Melding");.

View

De View zal nu via Binding verbonden worden met het ViewModel.

  • Pas de View BlogToevoegenViewPage als volgt aan. Merk de Bindings op en de extra Label om de Melding weer te geven.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TestEF.Views.BlogToevoegenViewPage" Title="Blog Toevoegen">
    <ContentPage.Content>
        <StackLayout Margin="10">
            <Label Text="Blog toevoegen" FontSize="Large" VerticalOptions="Center" HorizontalOptions="CenterAndExpand" />
            <Entry x:Name="BlogNaam" Placeholder="Naam" Text="{Binding Name}" />
            <Entry x:Name="BlogURL" Placeholder="URL" Text="{Binding Url}" />
            <Button x:Name="BlogBewaren" Text="Bewaren" Command="{Binding SaveCommand}"/>
            <Button x:Name="Annuleren" Text="Annuleren" Command="{Binding AnnuleerCommand}"/>
            <Label x:Name="Foutmelding" Text="{Binding Melding}" TextColor="Red" HorizontalOptions="CenterAndExpand" FontAttributes="Bold"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

De code-behind BlogToevoegenViewPage.xaml.cs is nu wel heel eenvoudig, ze verbindt gewoon de View met het ViewModel.

  • Pas de code-behind BlogToevoegenViewPage.xaml.cs als volgt aan:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using TestEF.ViewModels;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class BlogToevoegenViewPage : ContentPage
	{
		public BlogToevoegen ()
		{
			InitializeComponent ();
			BindingContext = new BlogToevoegenViewModel();
		}
	}
}

Gerelateerde entiteiten toevoegen

Bij het toevoegen van een gerelateerde entiteit is het belangrijk dat de eigenschap die de relatie verzorgt (de Foreign key) mee bewaard wordt. U moet deze waarde dus meegeven aan het ViewModel.

Hieronder ziet u het ViewModel PostToevoegenViewModel die een Post, gerelateerd aan een Blog, toevoegt.

ViewModel

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using TestEF.Models;
using TestEF.ViewModels;
using Xamarin.Forms;

namespace TestEF.Views
{
    class PostToevoegenViewModel : BaseViewModel
    {
        private string title;
        public string Title
        {
            get { return title; }
            set
            {
                title = value;
                OnPropertyChanged();
            }
        }

        private string content;
        public string Content
        {
            get { return content; }
            set
            {
                content = value;
                OnPropertyChanged();
            }
        }

        private string melding;
        public string Melding
        {
            get { return melding; }
            set
            {
                melding = value;
                OnPropertyChanged();
            }
        }

        public ICommand SaveCommand { get; private set; }
        public ICommand AnnuleerCommand { get; private set; }

        int BlogId;

        public PostToevoegenViewModel(int blogId)
        {
            SaveCommand = new Command(SaveBlog);
            AnnuleerCommand = new Command(AnnuleerSaveBlog);

            Melding = "";
            BlogId = blogId;
        }

        async void SaveBlog()
        {
            if (!string.IsNullOrEmpty(Title))
            {
                Post post = new Post { BlogId = BlogId, Title = Title, Content = Content };
                App.Databank.Posts.Add(post);
                App.Databank.SaveChanges();

                await Application.Current.MainPage.Navigation.PopModalAsync();
            }
            else
            {
                melding = "De titel moet ingevuld zijn!";
                OnPropertyChanged("Melding");
            }
        }

        async void AnnuleerSaveBlog()
        {
            await Application.Current.MainPage.Navigation.PopModalAsync();
        }
    }
}

View

Een bijhorende View kan er als volgt uitzien.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TestEF.Views.PostToevoegenViewPage">
    <ContentPage.Content>
        <StackLayout Margin="10">
            <Label Text="Post toevoegen" FontSize="Large" VerticalOptions="Center" HorizontalOptions="CenterAndExpand" />
            <Entry x:Name="PostTitle" Placeholder="Naam" Text="{Binding Title}" />
            <Editor x:Name="PostContent" Text="{Binding Content}" />
            <Button x:Name="PostBewaren" Text="Bewaren" Command="{Binding SaveCommand}"/>
            <Button x:Name="Annuleren" Text="Annuleren" Command="{Binding AnnuleerCommand}"/>
            <Label x:Name="Foutmelding" Text="{Binding Melding}" TextColor="Red" HorizontalOptions="CenterAndExpand" FontAttributes="Bold"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Met als Code-behind:

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class PostToevoegenViewPage : ContentPage
	{
		public PostToevoegenViewPage (int blogId)
		{
			InitializeComponent ();
            BindingContext = new PostToevoegenViewModel(blogId);
        }
	}
}

Opdracht

Integreer het toevoegen van een Post binnen het lopende project.


Update (wijzigen) en Delete (verwijderen)

Updaten (wijzigen) en deleten (verwijderen) ga ik samen behandelen omdat ze allebei plaatsvinden in de BlogViewPage. Omdat er reeds een ViewModel aanwezig ik werk ik meteen in MVVM.

View

Dit was de View aan het einde van de vorige handleiding.

We gaan de View wat aanpassen.

Zoek de 7 verschillen!

Nou ja, het zijn er misschien geen 7 maar toch een paar:

  • Er is een Toolbar toegevoegd.
  • De BlogId staat niet langer in een Entry maar in een Label, het mag immers niet gewijzigd worden.
  • Er is een marge voorzien, dit was in de “oude” versie “vergeten”.

De aangepaste code staat hieronder. Merk de Commands op binnen de Toolbar.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="TestEF.Views.BlogViewPage" Title="{Binding Name}">
    <ContentPage.ToolbarItems>
        <ToolbarItem x:Name="BlogUpdaten" Text="Blog updaten" Order="Secondary" Command="{Binding UpdateBlogCommand}" />
        <ToolbarItem x:Name="BlogVerwijderen" Text="Blog verwijderen" Order="Secondary" Command="{Binding DeleteBlogCommand}" />
        <ToolbarItem x:Name="PostToevoegen" Text="Post toevoegen" Order="Secondary" Clicked="PostToevoegen_Clicked" />
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <StackLayout Margin="10">
            <Label Text="{Binding BlogId}" />
            <Entry Text="{Binding Name}" />
            <Entry Text="{Binding Url}" />

            <ListView x:Name="MyListView" HasUnevenRows="True" SeparatorVisibility="Default" Margin="5">
                <ListView.Header>
                    <Grid BackgroundColor="LightGray">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="2*" />
                            <ColumnDefinition Width="3*" />
                        </Grid.ColumnDefinitions>

                        <Label Grid.Column="0" Text="ID" FontAttributes="Bold" />
                        <Label Grid.Column="1" Text="Titel" FontAttributes="Bold" />
                        <Label Grid.Column="2" Text="Inhoud" FontAttributes="Bold" />
                    </Grid>
                </ListView.Header>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid BackgroundColor="White">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="2*" />
                                    <ColumnDefinition Width="3*" />
                                </Grid.ColumnDefinitions>

                                <Label Grid.Column="0" Text="{Binding PostId}" FontSize="Small" />
                                <Label Grid.Column="1" Text="{Binding Title}" FontSize="Small" />
                                <Label Grid.Column="2" Text="{Binding Content}" FontSize="Small" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            <ListView x:Name="MyListView2" HasUnevenRows="True" SeparatorVisibility="Default" Margin="5">
                <ListView.Header>
                    <Grid BackgroundColor="LightGray">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>

                        <Label Grid.Column="0" Text="Blog" FontAttributes="Bold" />
                        <Label Grid.Column="1" Text="Titel" FontAttributes="Bold" />
                        <Label Grid.Column="2" Text="Inhoud" FontAttributes="Bold" />
                    </Grid>
                </ListView.Header>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid BackgroundColor="White">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>

                                <Label Grid.Column="0" Text="{Binding Naam}" FontSize="Small" />
                                <Label Grid.Column="1" Text="{Binding Titel}" FontSize="Small" />
                                <Label Grid.Column="2" Text="{Binding Inhoud}" FontSize="Small" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

ViewModel

De ViewModel BlogViewModel op het einde van de vorige handleiding was:

using System.Collections.ObjectModel;
using System.Linq;
using TestEF.Models;

namespace TestEF.ViewModels
{
    class BlogViewModel : BaseViewModel
    {
        public Blog BlogItem { get; set; }
        public ObservableCollection<Post> PostLijst;
        public IQueryable BlogPostLijst;

        public BlogViewModel(Blog item = null)
        {
            BlogItem = item;
            var Query = App.Databank.Posts.Where(p => p.BlogId == BlogItem.BlogId);
            PostLijst = new ObservableCollection<Post>(Query);

            var Query2 = from b in App.Databank.Blogs
                            join p in App.Databank.Posts
                                on b.BlogId equals p.BlogId
                            where b.BlogId == BlogItem.BlogId

                            select new
                            {
                                Naam = b.Name,
                                Titel = p.Title,
                                Inhoud = p.Content
                            };
            BlogPostLijst = Query2;
        }
    }
}

We gaan deze ViewModel BlogViewModel nu deftige uitbreiden:

using System.Collections.ObjectModel;
using System.Linq;
using TestEF.Models;
using System.Windows.Input;
using TestEF.Views;
using Xamarin.Forms;

namespace TestEF.ViewModels
{
    class BlogViewModel : BaseViewModel
    {
        public Blog BlogItem { get; set; }
        public ObservableCollection<Post> PostLijst;
        public IQueryable BlogPostLijst;

        private int blogid;
        public int BlogId
        {
            get { return blogid; }
            set
            {
                blogid = value;
                OnPropertyChanged();
            }
        }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged();
            }
        }

        private string url;
        public string Url
        {
            get { return url; }
            set
            {
                url = value;
                OnPropertyChanged();
            }
        }

        public ICommand UpdateBlogCommand { get; private set; }
        public ICommand DeleteBlogCommand { get; private set; }

        public BlogViewModel(Blog item = null)
        {
            BlogItem = item;

            BlogId = BlogItem.BlogId;
            Name = BlogItem.Name;
            Url = BlogItem.Url;

            var Query = App.Databank.Posts.Where(p => p.BlogId == BlogItem.BlogId);
            PostLijst = new ObservableCollection<Post>(Query);

            var Query2 = from b in App.Databank.Blogs
                            join p in App.Databank.Posts
                                on b.BlogId equals p.BlogId
                            where b.BlogId == BlogItem.BlogId
                            orderby p.Title descending, p.Content
                            select new
                            {
                                Naam = b.Name,
                                Titel = p.Title,
                                Inhoud = p.Content
                            };

            Query2 = Query2.OrderByDescending(s => s.Titel).ThenBy(s => s.Inhoud);
            BlogPostLijst = Query2;

            UpdateBlogCommand = new Command(UpdateBlog);
            DeleteBlogCommand = new Command(DeleteBlog);

        }

        void UpdateBlog()
        {
            if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Url))
            {
                BlogItem.Name = Name;
                BlogItem.Url = Url;
                App.Databank.SaveChanges();
            }
        }

        async void DeleteBlog()
        {
            App.Databank.Blogs.Remove(BlogItem);
            App.Databank.SaveChanges();
            await Application.Current.MainPage.Navigation.PushAsync(new BlogListViewPage());
        }

    }
}

Laten we de toevoegingen eens bekijken.

Vergeet niet de nodige usings toe te voegen.

using System.Windows.Input;
using TestEF.Views;
using Xamarin.Forms;

De eigenschappen die verbonden worden met de View worden voorzien als properties. Als de waarde van de eigenschap wijzigt OnPropertyChanged(); wordt dit doorgegeven via notificatie. de nodige Commands worden gedeclareerd.

public int BlogId
{
    get { return blogid; }
    set
    {
        blogid = value;
        OnPropertyChanged();
    }
}

private string name;
public string Name
{
    get { return name; }
    set
    {
        name = value;
        OnPropertyChanged();
    }
}

private string url;
public string Url
{
    get { return url; }
    set
    {
        url = value;
        OnPropertyChanged();
    }
}

public ICommand UpdateBlogCommand { get; private set; }
public ICommand DeleteBlogCommand { get; private set; }

Binnen de constructor worden de waarden toegekend aan de properties (dit was niet nodig bij het toevoegen omdat daar de waarden nog leeg waren bij het “construeren”).

BlogId = BlogItem.BlogId;
Name = BlogItem.Name;
Url = BlogItem.Url;

Binnen de constructor worden de Commands gekoppeld aan de gebruikte methode/functie.

UpdateBlogCommand = new Command(UpdateBlog);
DeleteBlogCommand = new Command(DeleteBlog);

De methode UpdateBlog:

void UpdateBlog()
{
    if (!string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Url))
    {
        BlogItem.Name = Name;
        BlogItem.Url = Url;
        App.Databank.SaveChanges();
    }
}
  • Er wordt eerst gecontroleerd of de nodige velden niet leeg zijn.
  • De gebonden properties worden toegekend aan de attributen (velden) van BlogItem BlogItem.Name = Name; en BlogItem.Url = Url;.
  • De databank wordt bewaard App.Databank.SaveChanges();.

De methode DeleteBlog

async void DeleteBlog()
{
    App.Databank.Blogs.Remove(BlogItem);
    App.Databank.SaveChanges();
    await Application.Current.MainPage.Navigation.PushAsync(new BlogListViewPage());
}
  • De blog wordt verwijderd uit de databank App.Databank.Blogs.Remove(BlogItem);.
  • De databank wordt bewaard App.Databank.SaveChanges();.
  • Omdat de blog niet meer bestaat keren we terug, navigeren we, naar naar de View BlogListViewPage.

Omdat we niet meer enkel een blog moeten verbinden maar ook de Commands moet de verbinding tussen de View en het ViewModel herbekeken worden.

Dit gebeurt in de code-behind van de View BlogViewPage.xaml.cs.

De huidige code is:

using TestEF.Models;
using TestEF.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BlogViewPage : ContentPage
    {

        public BlogViewPage(Blog BlogDetail)
        {
            InitializeComponent();

            BlogViewModel viewModel = new BlogViewModel(BlogDetail);
            BindingContext = viewModel.BlogItem;
            MyListView.ItemsSource = viewModel.PostLijst;
            MyListView2.ItemsSource = viewModel.BlogPostLijst;
        }
    }
}

Volgende wijzigingen dringen zich op:

  • Merk op dat de BindingContext van de View verbonden is met de Blog zelf BindingContext = viewModel.BlogItem;. Om ook toegang te hebben tot de Commands dient de verbinding te gebeuren met het volledige ViewModel.

Pas de code-behind als volgt aan:

using TestEF.Models;
using TestEF.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestEF.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class BlogViewPage : ContentPage
    {

        public BlogViewPage(Blog BlogDetail)
        {
            InitializeComponent();
            BlogViewModel viewModel = new BlogViewModel(blog);
            BindingContext = viewModel;
            MyListView.ItemsSource = viewModel.PostLijst;
            MyListView2.ItemsSource = viewModel.BlogPostLijst;

        }

    }
}

Hiermee zijn we aan het einde van een reeks handleidingen gekomen, als opdracht kunt u het wijzigen en verwijderen van een Post uitwerken.

Onderstaande video van Xamarin University toont een licht andere werkwijze maar kan als afronding van mijn reeks Entity Framework met Xamarin gezien worden.

Geef een reactie

Deze website gebruikt Akismet om spam te verminderen. Bekijk hoe je reactie-gegevens worden verwerkt.

  • Abonneer je op deze website d.m.v. e-mail

    Voer je e-mailadres in om je in te schrijven op deze website en e-mailmeldingen te ontvangen van nieuwe berichten.