Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Important
Dit document is alleen geldig voor WPF in .NET Framework
In dit document worden gegevensbinding voor WPF in .NET Framework beschreven. Voor nieuwe .NET-projecten raden we u aan EF Core te gebruiken in plaats van Entity Framework 6. De documentatie voor gegevensbinding in EF Core is hier: Aan de slag met WPF.
In dit stapsgewijze overzicht ziet u hoe u POCO-typen koppelt aan WPF-besturingselementen in een master-detailformulier. De toepassing gebruikt de Entity Framework-API's om objecten te vullen met gegevens uit de database, wijzigingen bij te houden en gegevens in de database te behouden.
Het model definieert twee typen die deelnemen aan een-op-veel-relatie: Categorie (principal\master) en Product (afhankelijk\detail). Vervolgens worden de Visual Studio-hulpprogramma's gebruikt om de typen die in het model zijn gedefinieerd, te binden aan de WPF-besturingselementen. Met het WPF-gegevensbindingsframework kunt u navigeren tussen gerelateerde objecten: als u rijen selecteert in de hoofdweergave, wordt de detailweergave bijgewerkt met de bijbehorende onderliggende gegevens.
De schermafbeeldingen en codevermeldingen in dit scenario zijn afkomstig van Visual Studio 2013, maar u kunt dit scenario voltooien met Visual Studio 2012 of Visual Studio 2010.
Gebruik de optie Object voor het maken van WPF-gegevensbronnen
Met de vorige versie van Entity Framework is het raadzaam om de optie Database te gebruiken bij het maken van een nieuwe gegevensbron op basis van een model dat is gemaakt met EF Designer. Dit komt doordat de ontwerper een context genereert die is afgeleid van ObjectContext en entiteitsklassen die zijn afgeleid van EntityObject. Met behulp van de optie Database kunt u de beste code schrijven voor interactie met dit API-oppervlak.
De EF-ontwerpers voor Visual Studio 2012 en Visual Studio 2013 genereren een context die is afgeleid van DbContext samen met eenvoudige POCO-entiteitsklassen. Met Visual Studio 2010 raden we u aan om over te schakelen naar een sjabloon voor het genereren van code die dbContext gebruikt, zoals verderop in dit scenario wordt beschreven.
Wanneer u het DbContext-API-oppervlak gebruikt, moet u de optie Object gebruiken bij het maken van een nieuwe gegevensbron, zoals wordt weergegeven in dit scenario.
Indien nodig kunt u terugkeren naar op ObjectContext gebaseerde codegeneratie voor modellen die zijn gemaakt met de EF Designer.
Pre-Requisites
U moet Visual Studio 2013, Visual Studio 2012 of Visual Studio 2010 hebben geïnstalleerd om dit scenario te voltooien.
Als u Visual Studio 2010 gebruikt, moet u ook NuGet installeren. Zie NuGet installeren voor meer informatie.
De toepassing maken
- Visual Studio openen
- Bestand -> Nieuw -> Project....
- Selecteer Windows in het linkerdeelvenster en WPFApplication in het rechterdeelvenster
- Voer WPFwithEFSample in als de naam
- Kies OK.
Het NuGet-pakket Entity Framework installeren
- Klik in Solution Explorer met de rechtermuisknop op het Project WinFormswithEFSample
- Selecteer NuGet-pakketten beheren...
- Selecteer in het dialoogvenster NuGet-pakketten beheren het tabblad Online en kies het EntityFramework-pakket
- Klik op Installeren
Note
Naast de EntityFramework-assembly wordt ook een verwijzing naar System.ComponentModel.DataAnnotations toegevoegd. Als het project een verwijzing naar System.Data.Entity heeft, wordt het verwijderd wanneer het EntityFramework-pakket is geïnstalleerd. De Assembly System.Data.Entity wordt niet meer gebruikt voor Entity Framework 6-toepassingen.
Een model definiëren
In deze procedure kunt u ervoor kiezen om een model te implementeren met behulp van Code First of de EF Designer. Voer een van de twee volgende secties uit.
Optie 1: Een model definiëren met code eerst
In deze sectie ziet u hoe u een model en de bijbehorende database maakt met behulp van Code First. Ga naar de volgende sectie (optie 2: Een model definiëren met Database First) als u Database First liever gebruikt om uw model vanuit een database reverse-engineeren met behulp van de EF-ontwerpfunctie
Wanneer u Code First-ontwikkeling gebruikt, begint u meestal met het schrijven van .NET Framework-klassen die uw conceptuele model (domein) definiëren.
- Voeg een nieuwe klasse toe aan de WPFwithEFSample:
- Klik met de rechtermuisknop op de projectnaam
- Selecteer Toevoegen en vervolgens Nieuw item
- Selecteer Klasse en voer Product in voor de klassenaam
- Vervang de productklassedefinitie door de volgende code:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
- Voeg een categorieklasse toe met de volgende definitie:
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Product> Products { get; private set; }
}
}
De eigenschap Producten op de eigenschap Categorie en Categorie in de klasse Product zijn navigatie-eigenschappen. In Entity Framework bieden navigatie-eigenschappen een manier om door een relatie tussen twee entiteitstypen te navigeren.
Naast het definiëren van entiteiten moet u een klasse definiëren die is afgeleid van DbContext en dbSet<TEntity-eigenschappen> beschikbaar maakt. De eigenschappen dbSet<TEntity> laten de context weten welke typen u wilt opnemen in het model.
Een exemplaar van het afgeleide dbContext-type beheert de entiteitsobjecten tijdens runtime, waaronder het invullen van objecten met gegevens uit een database, het bijhouden van wijzigingen en het persistent maken van gegevens in de database.
- Voeg een nieuwe ProductContext-klasse toe aan het project met de volgende definitie:
using System.Data.Entity;
namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}
Compileer het project.
Optie 2: Een model definiëren met Database First
In deze sectie wordt beschreven hoe u Database First gebruikt om uw model te reverse-engineeren vanuit een database met behulp van de EF-ontwerpfunctie. Als u de vorige sectie hebt voltooid (optie 1: Een model definiëren met code eerst), slaat u deze sectie over en gaat u rechtstreeks naar de sectie Luie laden .
Een bestaande database maken
Wanneer u zich richt op een bestaande database, wordt deze doorgaans al gemaakt, maar voor dit scenario moeten we een database maken voor toegang.
De databaseserver die met Visual Studio is geïnstalleerd, is afhankelijk van de versie van Visual Studio die u hebt geïnstalleerd:
- Als u Visual Studio 2010 gebruikt, maakt u een SQL Express-database.
- Als u Visual Studio 2012 gebruikt, maakt u een LocalDB-database .
We gaan verder met het genereren van de database.
Weergave -> Server Explorer
Klik met de rechtermuisknop op gegevensverbindingen -> Verbinding toevoegen...
Als u nog geen verbinding hebt gemaakt met een database vanuit Server Explorer voordat u Microsoft SQL Server als gegevensbron moet selecteren
Maak verbinding met LocalDB of SQL Express, afhankelijk van welke u hebt geïnstalleerd en voer Producten in als databasenaam
Selecteer OK en u wordt gevraagd of u een nieuwe database wilt maken. Selecteer Ja
De nieuwe database wordt nu weergegeven in Server Explorer, klik er met de rechtermuisknop op en selecteer Nieuwe query
Kopieer de volgende SQL naar de nieuwe query en klik met de rechtermuisknop op de query en selecteer Uitvoeren
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
Model reverse-engineeren
We gaan gebruikmaken van Entity Framework Designer, dat deel uitmaakt van Visual Studio, om ons model te maken.
Project -> Nieuw item toevoegen...
Selecteer Gegevens in het linkermenu en ADO.NET Entiteitsgegevensmodel
Voer ProductModel in als de naam en klik op OK
Hiermee wordt de Entiteitsgegevensmodel-wizard gestart
Selecteer Genereren uit database en klik op Volgende
Selecteer de verbinding met de database die u in de eerste sectie hebt gemaakt, voer ProductContext in als de naam van de verbindingsreeks en klik op Volgende
Klik op het selectievakje naast Tabellen om alle tabellen te importeren en klik op Voltooien
Zodra het reverse-engineerproces is voltooid, wordt het nieuwe model toegevoegd aan uw project en geopend zodat u het kunt bekijken in entity framework designer. Er is ook een App.config-bestand aan uw project toegevoegd met de verbindingsgegevens voor de database.
Aanvullende stappen in Visual Studio 2010
Als u in Visual Studio 2010 werkt, moet u de EF-ontwerper bijwerken voor het gebruik van EF6-codegeneratie.
- Klik met de rechtermuisknop op een lege plek van uw model in de EF Designer en selecteer Item voor het genereren van code toevoegen...
- Selecteer Onlinesjablonen in het linkermenu en zoek naar DbContext
- Selecteer de EF 6.x DbContext Generator voor C#, voer ProductsModel in als de naam en klik op Toevoegen
Het genereren van code voor gegevensbinding bijwerken
EF genereert code van uw model met behulp van T4-sjablonen. De sjablonen die worden geleverd met Visual Studio of die zijn gedownload uit de Visual Studio-galerie, zijn bedoeld voor algemeen gebruik. Dit betekent dat de entiteiten die zijn gegenereerd op basis van deze sjablonen eenvoudige ICollection<T-eigenschappen> hebben. Wanneer u echter gegevensbinding uitvoert met WPF, is het wenselijk om ObservableCollection te gebruiken voor verzamelingseigenschappen, zodat WPF wijzigingen in de verzamelingen kan bijhouden. Daarom wijzigen we de sjablonen voor het gebruik van ObservableCollection.
Open Solution Explorer en zoek het bestand ProductModel.edmx
Zoek het ProductModel.tt-bestand dat wordt genest onder het bestand ProductModel.edmx
Dubbelklik op het ProductModel.tt-bestand om het te openen in de Visual Studio-editor
Zoek en vervang de twee exemplaren van 'ICollection' door 'ObservableCollection'. Deze bevinden zich ongeveer op regel 296 en 484.
Zoek en vervang het eerste exemplaar van 'HashSet' door 'ObservableCollection'. Deze gebeurtenis bevindt zich ongeveer op regel 50. Vervang de tweede instantie van HashSet die later in de code voorkomt niet.
Zoek en vervang de enige keer dat System.Collections.Generic voorkomt door System.Collections.ObjectModel. Dit bevindt zich ongeveer op regel 424.
Sla het ProductModel.tt bestand op. Hierdoor moet de code voor entiteiten opnieuw worden gegenereerd. Als de code niet automatisch opnieuw wordt gegenereerd, klikt u met de rechtermuisknop op ProductModel.tt en kiest u 'Aangepast hulpprogramma uitvoeren'.
Als u nu het Category.cs-bestand opent (dat is genest onder ProductModel.tt), ziet u dat de verzameling Producten het type ObservableCollection<Product> heeft.
Compileer het project.
Luie laden
De eigenschap Producten op de eigenschap Categorie en Categorie in de klasse Product zijn navigatie-eigenschappen. In Entity Framework bieden navigatie-eigenschappen een manier om door een relatie tussen twee entiteitstypen te navigeren.
EF biedt u de mogelijkheid om gerelateerde entiteiten uit de database automatisch te laden wanneer u de navigatie-eigenschap voor het eerst opent. Houd er rekening mee dat bij dit type laden (lui laden), de eerste keer dat u elke navigatie-eigenschap benadert, er een afzonderlijke query tegen de database wordt uitgevoerd indien de inhoud nog niet in de context aanwezig is.
Wanneer u POCO entiteitstypen gebruikt, bereikt EF lazy loading door tijdens uitvoering exemplaren van afgeleide proxytypen te maken en vervolgens virtuele eigenschappen in uw klassen te overschrijven om de laadhook toe te voegen. Als u lui laden van gerelateerde objecten wilt, moet u getters voor navigatie-eigenschappen declareren als openbaar en virtueel (overschrijfbaar in Visual Basic), en uw klasse mag niet verzegeld zijn (Niet overschrijfbaar in Visual Basic). Wanneer u Database-First technieken gebruikt, worden navigatie-eigenschappen automatisch virtueel gemaakt om lazy loading mogelijk te maken. In de sectie Code First hebben we ervoor gekozen om de navigatie-eigenschappen om dezelfde reden virtueel te maken.
Object koppelen aan besturingselementen
Voeg de klassen toe die in het model zijn gedefinieerd als gegevensbronnen voor deze WPF-toepassing.
Dubbelklik op MainWindow.xaml in Solution Explorer om het hoofdformulier te openen
Selecteer In het hoofdmenu Project -> Nieuwe gegevensbron toevoegen ... (in Visual Studio 2010 moet u Gegevens selecteren -> Nieuwe gegevensbron toevoegen...)
Selecteer object in het venster Een gegevensbrontype kiezen en klik op Volgende
Vouw in het dialoogvenster Gegevensobjecten selecteren WPFwithEFSample twee keer uit en kies Categorie
U hoeft de gegevensbron Product niet te selecteren, omdat we er via de eigenschap Product in de categoriegegevensbron bij komen
Klik op Voltooien.
Het venster Gegevensbronnen wordt geopend naast het venster MainWindow.xaml Als het venster Gegevensbronnen niet wordt weergegeven, selecteert u Weergave -> Overige Windows-gegevensbronnen>
Druk op het speldpictogram, zodat het venster Gegevensbronnen niet automatisch wordt verborgen. Mogelijk moet u op de knop Vernieuwen drukken als het venster al zichtbaar was.
Selecteer de gegevensbron Categorie en sleep deze in het formulier.
Het volgende is gebeurd wanneer we deze bron hebben gesleept:
- De resource categoryViewSource en het besturingselement categoryDataGrid zijn toegevoegd aan XAML
- De eigenschap DataContext op het bovenliggende rasterelement is ingesteld op {StaticResource categoryViewSource }. De categoryViewSource-resource fungeert als een bindingsbron voor het outer\parent Grid-element. De binnenste rasterelementen nemen vervolgens de DataContext-waarde over van het bovenliggende raster (de eigenschap categoryDataGrid ItemsSource is ingesteld op {Binding})
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Een detailraster toevoegen
Nu we een raster hebben om categorieën weer te geven, gaan we een detailraster toevoegen om de bijbehorende producten weer te geven.
- Selecteer de eigenschap Producten onder de gegevensbron Categorie en sleep deze op het formulier.
- De categoryProductsViewSource-resource en productDataGrid-raster worden aan XAML toegevoegd.
- Het bindingspad voor deze resource is ingesteld op Producten
- WPF-framework voor gegevensbinding zorgt ervoor dat alleen producten met betrekking tot de geselecteerde categorie worden weergegeven in productDataGrid
- Sleep de Button vanuit de toolbox op het formulier. Stel de eigenschap Name in op buttonSave en de eigenschap Inhoud op Opslaan.
Het formulier moet er ongeveer als volgt uitzien:
Code toevoegen die gegevensinteractie verwerkt
Het is tijd om enkele gebeurtenis-handlers toe te voegen aan het hoofdvenster.
Klik in het XAML-venster op het <Window element om het hoofdvenster te selecteren.
Kies in het venster Eigenschappenrechtsboven gebeurtenissen en dubbelklik op het tekstvak rechts van het geladen label
Voeg ook de gebeurtenis Klik voor de knop Opslaan toe door te dubbelklikken op de knop Opslaan in de ontwerpfunctie.
Hiermee gaat u naar de code achter het formulier. We gaan nu de code bewerken om productcontext te gebruiken om gegevenstoegang uit te voeren. Werk de code voor MainWindow bij zoals hieronder wordt weergegeven.
De code declareert een langlopend exemplaar van ProductContext. Het Object ProductContext wordt gebruikt om gegevens op te vragen en op te slaan in de database. De Dispose()-methode van de ProductContext-instantie wordt vervolgens aangeroepen vanuit de overschreven OnClosing-methode. De codeopmerkingen bevatten details over wat de code doet.
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoryViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));
// Load is an extension method on IQueryable,
// defined in the System.Data.Entity namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.Categories.Load();
// After the data is loaded call the DbSet<T>.Local property
// to use the DbSet<T> as a binding source.
categoryViewSource.Source = _context.Categories.Local;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.Products.Local.ToList())
{
if (product.Category == null)
{
_context.Products.Remove(product);
}
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
}
WPF-toepassing testen
Compileer de toepassing en voer deze uit. Als u Code First hebt gebruikt, ziet u dat er een WPFwithEFSample.ProductContext-database voor u is gemaakt.
Voer een categorienaam in het bovenste raster en productnamen in het onderste raster in, voer niets in id-kolommen in, omdat de primaire sleutel wordt gegenereerd door de database
Druk op de knop Opslaan om de gegevens op te slaan in de database
Na de aanroep van SaveChanges() van DbContext worden de id's gevuld met de door de database gegenereerde waarden. Omdat we Refresh() hebben aangeroepen nadat SaveChanges() de DataGrid-besturingselementen ook worden bijgewerkt met de nieuwe waarden.
Aanvullende bronnen
Zie dit onderwerp in de WPF-documentatie voor meer informatie over gegevensbindingen met behulp van WPF.