66
ADO.NET CONNECTED SQL Server Management Studio: Server Name: localhost (idem SQL-lessen) Authentication: Windows Authentication User Name: idem SQL-lessen Connect Tabellen importeren: Persoonlijke database selecteren New query Contact.sql plakken Execute Project Contactpersonen: Visual Studio 2010 File > Open > Project > Contactpersonen.sln Connecteren vanuit Visual Studio: View > Server Explorer > Data Connections > Rechts > Add Connection > Microsoft SQL Server > Continue Add Connection: Server Name: localhost > Windows Authentication > Select database name > Test Connection > OK Connection: Een verbinding met een database tot stand brengen d.m.v. een connectiestring Connectiestring uit App.config halen Connectie openen met Open()-methode Connectie sluiten met Close()-methode Command: Een SQL-query uitvoeren: ExecuteNonQuery: geen records (update, insert, delete, create table …) ExecuteScalar: 1 waarde (select sum(…) …) ExecuteReader: 0, 1 of meerdere records (select * from …) DataReader: Resultset (resultaat van ExecuteReader) opvangen in DataReader Forward-only read-only cursor Read()-methode om naar volgende rij te gaan True = rij gevonden False = geen rijen meer App.config: Add > New Item > Application Configuration File Connectionstrings: Lokale SQL Server met Windows Authentication: Server = localhost; Database = …; Integrated Security = SSPI; Lokale SQL Server met SQL Authentication: Data Source = codd; Initial Catalog = …; User Id = s5061854; Password = …; Connectiestring in App.config: <?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name = "local" connectionString = "Server = localhost; Database = …; Integrated Security = SSPI;"/> <add name="sqlserver" connectionString="Data Source = codd; Initial Catalog = …; User Id = s5061854; Password = …;"/> </connectionStrings> </configuration> ConnectionString makkelijk opvullien via: Server Explorer > Data Connections > Database.dbo > Rechts > Properties Connectiestring ophalen: Project > Rechts > Add Reference > System.Configuration // connectiestring uit App.config using System.Configuration; // SQL Server Data Provider gebruiken using System.Data.SqlClient;

KHK 2Ti C# Samenvatting

Embed Size (px)

DESCRIPTION

KHK 2Ti C# Samenvatting

Citation preview

Page 1: KHK 2Ti C# Samenvatting

ADO.NET CONNECTED

– SQL Server Management Studio: – Server Name: localhost (idem SQL-lessen)– Authentication: Windows Authentication– User Name: idem SQL-lessen– Connect

– Tabellen importeren:– Persoonlijke database selecteren– New query– Contact.sql plakken– Execute

– Project Contactpersonen:– Visual Studio 2010– File > Open > Project > Contactpersonen.sln

– Connecteren vanuit Visual Studio:View > Server Explorer > Data Connections > Rechts > Add Connection > Microsoft SQL Server > Continue

– Add Connection: Server Name: localhost > Windows Authentication > Select database name > Test Connection > OK

– Connection:– Een verbinding met een database tot stand brengen d.m.v. een connectiestring– Connectiestring uit App.config halen– Connectie openen met Open()-methode– Connectie sluiten met Close()-methode

– Command:– Een SQL-query uitvoeren:

– ExecuteNonQuery: geen records (update, insert, delete, create table …)– ExecuteScalar: 1 waarde (select sum(…) …)– ExecuteReader: 0, 1 of meerdere records (select * from …)

– DataReader:– Resultset (resultaat van ExecuteReader) opvangen in DataReader– Forward-only read-only cursor– Read()-methode om naar volgende rij te gaan

– True = rij gevonden– False = geen rijen meer

– App.config:– Add > New Item > Application Configuration File

– Connectionstrings:– Lokale SQL Server met Windows Authentication:

– Server = localhost; Database = …; Integrated Security = SSPI; – Lokale SQL Server met SQL Authentication:

– Data Source = codd; Initial Catalog = …; User Id = s5061854; Password = …;

– Connectiestring in App.config:

<?xml version="1.0" encoding="utf-8" ?><configuration> <connectionStrings> <add name = "local" connectionString = "Server = localhost; Database = …; Integrated Security = SSPI;"/> <add name="sqlserver" connectionString="Data Source = codd; Initial Catalog = …; User Id = s5061854; Password = …;"/> </connectionStrings></configuration>

– ConnectionString makkelijk opvullien via: Server Explorer > Data Connections > Database.dbo > Rechts > Properties

– Connectiestring ophalen: Project > Rechts > Add Reference > System.Configuration

// connectiestring uit App.configusing System.Configuration;// SQL Server Data Provider gebruikenusing System.Data.SqlClient;…

Page 2: KHK 2Ti C# Samenvatting

// form-variabele connectionString uit App.Configstring _connectionString = ConfigurationManager.ConnectionStrings["local"].ConnectionString;// variabele id bevat de id van de geselecteerde contactpersoonint _id;

– Form-Load:

private void FormContactpersonen_Load(…) { LeesAantalContactpersonen(); LeesContactpersonen();}

– Eén waarde inlezen:

private void LeesAantalContactpersonen() { // een connectie-object maken SqlConnection connection = new SqlConnection(_connectionString); // connectie openen connection.Open(); // sql-statement uitvoeren met command-object SqlCommand command = new SqlCommand("Select count(*) from Contact", connection); // slechts 1 waarde als resultaat, dus ExecuteScalar int aantal = (int)(command.ExecuteScalar()); labelAantal.Text = aantal.ToString() + " records"; // connectie sluiten connection.Close();}

– Lay-out van de listview aanpassen (later meer):

private void InitializeComponentExtra() { listViewContactpersonen.BeginUpdate();

listViewContactpersonen.View = View.Details; listViewContactpersonen.FullRowSelect = true; listViewContactpersonen.MultiSelect = false;

// drie kolommen in ListView listViewContactpersonen.Columns.Add("ID", 35, HorizontalAlignment.Right); listViewContactpersonen.Columns.Add("Naam", 110, HorizontalAlignment.Left); listViewContactpersonen.Columns.Add("Email", 160, HorizontalAlignment.Left);

listViewContactpersonen.EndUpdate();}

– Meerdere records inlezen:

private void LeesContactpersonen() { listViewContactpersonen.BeginUpdate();

// connectie-object maken en connectie openen SqlConnection connection = new SqlConnection(_connectionString); connection.Open();

// SQL select-statement uitvoeren SqlCommand command = new SqlCommand("Select * from Contact order by Naam", connection);

// DataReader want Select geeft een resultset: ExecuteReader SqlDataReader dataReader = command.ExecuteReader();

// zolang er contactpersonen zijn while (dataReader.Read()) { // nieuwe ListItems toevoegen ListViewItem item = new ListViewItem(dataReader["ID"].ToString()); item.SubItems.Add(dataReader["Naam"].ToString()); item.SubItems.Add(dataReader["Email"].ToString()); item.ImageIndex = 0; // index van de afbeelding, zie later listViewContactpersonen.Items.Add(item); }

// datareader en connectie sluiten dataReader.Close(); connection.Close(); listViewContactpersonen.EndUpdate();}

– Contactpersoon selecteren:

Page 3: KHK 2Ti C# Samenvatting

private void listViewContactpersonen_SelectedIndexChanged(…) { // controleren of er een contactpersoon is geselecteerd if (listViewContactpersonen.SelectedItems.Count != 0) { // id ophalen van geselecteerde contactpersoon _id = Int32.Parse(listViewContactpersonen.SelectedItems[0].Text); LeesContactpersoon(); } else { textBoxNaam.Clear(); textBoxEmail.Clear(); }}// I.p.v. Int32.Parse kan je ook het volgende gebruiken:// _id = Convert.ToInt32(listViewContactpersonen.SelectedItems[0].Text);

– 0 of 1 record inlezen:

private void LeesContactpersoon() { // connectie-object maken en connectie openen SqlConnection connection = new SqlConnection(_connectionString); connection.Open(); SqlCommand command = new SqlCommand("Select * from Contact where id = @id", connection);

// parameters doorgeven met Add()-methode van Parameterscollectie command.Parameters.Add(new SqlParameter("@id", id));

// DataReader want Select geeft resultset met 0 of 1 record SqlDataReader dataReader = command.ExecuteReader();

// 1 keer Read() om te positioneren op eerste record (als die er is) if (dataReader.Read()) { textBoxNaam.Text = dataReader["Naam"].ToString(); textBoxEmail.Text = dataReader["Email"].ToString(); }

// datareader en connectie sluiten dataReader.Close(); connection.Close();}

– Contactpersoon wijzigen:

private void buttonWijzigen_Click(…) { SchrijfContactpersoon(); // in "echte" applicatie beter alleen de aangepaste gegevens wijzigen // i.p.v. alles terug in te lezen listViewContactpersonen.Items.Clear(); LeesContactpersonen();}

– Geen records als resultaat:

private void SchrijfContactpersoon() { // connectie-object maken en connectie openen SqlConnection connection = new SqlConnection(_connectionString); connection.Open();

// update-statement met command-object uitvoeren SqlCommand command = new SqlCommand( "Update Contact set naam = @naam, email = @email where id = @id", connection);

// Parameters doorgeven met Add()-methode van Parameters-collectie // Let op: dezelfde volgorde gebruiken als voorkomen in SQL-statement command.Parameters.Add(new SqlParameter("@naam", textBoxNaam.Text)); command.Parameters.Add(new SqlParameter("@email", textBoxEmail.Text)); command.Parameters.Add(new SqlParameter("@id", _id));

// update-statement uitvoeren command.ExecuteNonQuery(); connection.Close();}

– Pop-up menu: contextMenuStripListContact (vinkje voor menu item, CheckedState)

– Icons voor ListView:– imageListLarge

– ImageSize = 32 x 32

Page 4: KHK 2Ti C# Samenvatting

– Choose Images > Klant3232.bmp– imageListSmall

– ImageSize 16 x 16– Choose Images > Klant1616.bmp

– listViewContactpersonen– LargeImageList = imageListLarge– SmallImageList = imageListSmall– ContextMenuStrip = contextMenuStripListContact

– View enumeration:– In Tag-eigenschap van menu-items: LargeIcon, SmallIcon, List, Details, Tile

– Eén eventhandler:

private void InitializeComponentExtra() { … // één eventhandler voor de click van alle menu-items this.detailsToolStripMenuItem.Click += new System.EventHandler(this.Schikken_Click); this.lijstToolStripMenuItem.Click += new System.EventHandler(this.Schikken_Click); this.tegelsToolStripMenuItem.Click += new System.EventHandler(this.Schikken_Click); this.grotePictogrammenToolStripMenuItem.Click += new System.EventHandler(this.Schikken_Click); this.kleinePictogrammenToolStripMenuItem.Click += new System.EventHandler(this.Schikken_Click);}

private void Schikken_Click(object sender, EventArgs e) { // voorbeeld: tekstwaarde "LargeIcon" uit Tag-eigenschap omgezet worden in opsomming // (enumeration) // View.LargeIcon // true = geen rekening houden met hoofd/kleine letters View view = (View)Enum.Parse( typeOf(View), ((ToolStripMenuItem)sender).Tag.ToString(), True); listViewContactpersonen.View = view; // alle vinkjes wegdoen DoeVinkjesWeg(((ToolStripMenuItem)sender).GetCurrentParent()); // vinkje bij huidige menu-item plaatsen ((ToolStripMenuItem)sender).Checked = true;}

private void DoeVinkjesWeg(Toolstrip toolStrip) { // alle items van het contextmenu aflopen en vinkjes wegdoen foreach (ToolStripMenuItem item in toolStrip.Items) { item.Checked = false; }}

Page 5: KHK 2Ti C# Samenvatting

DRAG AND DROP

– Voorbeeld Drag and Drop:– Visual Studio 2010 > File > Open > Solution > DragAndDrop.sln– DragAndDrop > Rechts > Properties > Resources tab > Add Resource > Existing File > Paars.png

– Te doorlopen stappen bij Drag and Drop:– Drag-actie starten– Bepalen waar men kan droppen– Drag-operatie bevestigen– Droppen

– Drag and Drop van verfpotten naar listbox:

– Stap 1: Drag-actie starten

private void pictureboxPaars_MouseMove(…) { if (e.Button == MouseButtons.Left) { // beginnen met slepen, als data kleur meegeven pictureboxPaars.DoDragDrop("Paars", DragDropEffects.Copy); }}// Beter met één eventhandler voor alle verfpotten, zie later bij Control Arrays

– Stap 2: Bepalen waar droppen

listBoxKleuren: AllowDrop = true (via properties of via code in InitializeComponentExtra)

– Stap 3: Drag-operatie bevestigen

private void listBoxKleuren_DragEnter(…) { // muisaanwijzer aanpassen e.Effect = DragDropEffects.Copy;}

– Stap 4: Droppen

private void listBoxKleuren_DragDrop(…) { // ophalen data (bevat de kleur) string kleur = (string)e.Data.GetData(DataFormats.Text); listBoxKleuren.Items.Add(kleur);}

– Drag and Drop van listbox naar textbox:

– Stap 1: Drag-actie starten

private void listBoxKleuren_MouseMove(…) { // slepen bij bewegen van muis, linkermuisknop en item geselecteerd if (e.Button == MouseButtons.Left && ((ListBox)sender).SelectedIndex != -1) { // beginnen met slepen, als data naam van de control meegeven listBoxKleuren.DoDragDrop("listBoxKleuren", DragDropEffects.Copy); }}

– Stap 2: Bepalen waar droppen

textBoxKleur: AllowDrop = true (via properties of via code in InitializeComponentExtra)

– Stap 3: Drag-operatie bevestigen

private void textBoxKleur_DragEnter(…) { // muisaanwijzer aanpassen e.Effect = DragDropEffects.Copy;}

– Stap 4: Droppen

private void textBoxKleur_DragDrop(…) { // ophalen data (bevat naam van de control of kleur van de verfpot) string sourceOfKleur = (string)e.Data.GetData(DataFormats.Text); if (sourceOfKleur == "listBoxKleuren") { textBoxKleur.Text = listBoxKleuren.SelectedItem.ToString(); } else { // source is verpot textBoxKleur.Text = sourceOfKleur; }

Page 6: KHK 2Ti C# Samenvatting

}

private void listBoxKleuren_DragDrop(…) { // ophalen data string sourceOfKleur = (string)e.Data.GetData(DataFormats.Text); if (sourceOfKleur != "listBoxKleuren") { listBoxKleuren.Items.Add(sourceOfKleur); }}

– Listbox eigenschappen en methodes:

// items toevoegenlistBoxKleuren.Items.Add("Geel");

// alle items wissen listBoxKleuren.Items.Clear();

// is er een item geselecteerd? if (listBoxKleuren.SelectedIndex != -1) { MessageBox.Show(listBoxKleuren.SelectedItem.ToString()); }

// geselecteerde item verwijderen if (listBoxKleuren.SelectedIndex != -1) { listBoxKleuren.Items.RemoveAt(listBoxKleuren.SelectedIndex);}

// een item schrappen listBoxKleuren.Items.Remove("Geel");

// alle items in de keuzelijst weergeven: uit SelectedItems-collectiefor (int i = 0; i < listBoxKleuren.Items.Count; i++) { MessageBox.Show(listBoxKleuren.Items[i].ToString()); }

// alle items in de keuzelijst weergeven: met foreachforeach (string kleur in listBoxKleuren.SelectedItems) { MessageBox.Show(kleur);}

Page 7: KHK 2Ti C# Samenvatting

CONTROL ARRAYS

– Twee methodes:– Zelf een array definiëren en hierin controls stoppen

– Alleen bij een beperkt en vast aantal controls– Controls-collectie van het formulier gebruiken

– Ook bij een groot en variabel aantal controls – Iets moeilijker, maar altijd te gebruiken

– Voorbeeld Control Arrays:– Visual Studio 2010 > File > New > Project > Visual C# > Windows Form Application >

ControlArrays– Create Directory niet aanvinken

– FormEerste:– textBoxDisplay: RightToLeft = Yes– button1, button2 … button10– Alle buttons werken hetzelfde, nl. cijfer toevoegen aan het display-venster

public partial class FormEerste : Form { // array van buttons Button[] _knoppenArray = new Button[10];

public FormEerste() { InitializeComponent(); InitializeComponentExtra(); }

public void InitializeComponentExtra() { // buttons in array stoppen _knoppenArray[0] = button1; … _knoppenArray[9] = button10;

// vanaf nu array en index gebruiken for (int i = 0; i < _knoppenArray.Length; i++) { _knoppenArray[i].Text = i.ToString(); // één eventhandler voor alle knoppen _knoppenArray[i].Click += new System.EventHandler(this.knoppen_Click); } }

// Let op! Eventhandlers hebben altijd de parameters sender en e private void knoppen_Click (object sender, EventArgs e) { // object casten naar Button textBoxDisplay.Text += ((Button)sender).Text; }}

– FormTweede:

– Nieuwe formulier FormTweede opstarten (Program.cs)– textBoxLeeftijd, buttonToon, buttonWis– Project > Rechts > Properties > Resources > Add Resource > Add Existing File > Kaarsaan en

Kaarsuit.jpg

public partial class FormTweede : Form { // breedte en hoogte van de figuren const int _BREEDTE = 86; const int _HOOGTE = 111;

public FormTweede { InitializeComponent(); }}

– Controls-collectie:

private void buttonToon_Click(…) { // positie van de eerste afbeelding Point positie = new Point(0, 50);

int leeftijd = int.Parse(textBoxLeeftijd.Text); for (int i = 0; i < leeftijd; i++) { // nieuwe PictureBox maken PictureBox pictureBoxKaars = new PictureBox(); …

Page 8: KHK 2Ti C# Samenvatting

// control toevoegen aan controls-collectie van het formulier this.Controls.Add(pictureBoxKaars); positie.X += _BREEDTE; } }

– PictureBox-eigenschappen:

pictureBoxKaars.Image = Properties.Resources.KaarsAan;pictureBoxKaars.Location = positie;pictureBoxKaars.Size = new Size(_BREEDTE, _HOOGTE);

// alle controls hebben een tag-eigenschap waarin je zelf extra info mag stoppenpictureBoxKaars.Tag = i + 1;

// eventhandlers voor klikken en bewegen muis pictureBoxKaars.Click += new System.EventHandler(this.pictureBoxKaars_Click);pictureBoxKaars.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBoxKaars_MouseMove);

– Eventhandlers:

private void pictureBoxKaars_Click(object sender, EventArgs e) { ((PictureBox)sender).Image = Properties.Resources.KaarsUit;}

private void pictureBoxKaars_MouseMove(object sender, MouseEventArgs e) { this.Text = "Kaarsje " + ((PictureBox)sender).Tag;}

– Controls-collectie:

private void buttonWis_Click(object sender, EventArgs e) { // controls-collectie aflopen for (int i = 0; i < this.Controls.Count; i++) { // is control op plaats i een PictureBox? if (this.Controls[i].GetType() == typeof(PictureBox)) { this.Controls.RemoveAt(i); i--; } }}

– Controls-collectie alternatieve oplossing:

// controls-collectie aflopenfor (int i = this.Controls.Count - 1; i >= 0; i--) { // is control op plaats i een PictureBox? if (this.Controls[i] is PictureBox) { this.Controls.RemoveAt(i); i--; }}

Page 9: KHK 2Ti C# Samenvatting

ADO.NET OBJECTGEORIËNTEERD

– Voorbeeld Klanten: – Visual Studio 2010 > Open > Project > Klanten.sln– Database, EntityClasses, DataAccessClasses, Front End (FormLogin, FormKlanten)

– Database: – Met SQL Server Management Studio connecteren met persoonlijke database– Klanten.sql uitvoeren– Met Visual Studio 2010 connecteren met database – ConnectieString in App.Config:

<? xml version="1.0" encoding="utf-8" ?><configuration> <connectionStrings> <add name = "school" connectionString = "Server = codd; Database = …; Integrated Security=SSPI;"/> </connectionStrings></configuration>

– EntityClasses:

using System;using System.Collections.Generic;using System.Linq;using System.Text;

namespace Klanten.EntityClasses { class Klant { private int id; private string naam; private string adres; private int gemeenteID; private decimal saldo; private string login; private string wachtwoord;

public int ID { get { return id; } set { id = value; } };

public string Naam { get { return naam; } set { naam = value; } };

public Klant(int id, string naam, string adres, int gemeenteID, decimal saldo, string login, string wachtwoord) { this.ID = id; this.Naam = naam; this.Adres = adres; this.GemeenteID = gemeenteID; this.Saldo = saldo; this.Login = login; this.Wachtwoord = wachtwoord; }

public override string ToString() { return this.Naam; } }}

// Idem voor klasse Gemeente

– Base Class:

using System.Configuration;using System.Data.SqlClient;

namespace Klanten.DataAccessClasses { class DABaseClass { string connectionString =

Page 10: KHK 2Ti C# Samenvatting

ConfigurationManager.ConnectionStrings["school"].ConnectionString; protected SqlConnection connection;

public DABaseClass() { connection = new SqlConnection(connectionString); } }}

– Data Access Klant:

using System.Configuration;using System.Data.SqlClient;using Klanten.EntityClasses;

namespace Klanten.DataAccessClasses { // DAKlant erft (:) van DABaseClass class DAKlant : DABaseClass { // constructor van base-class uitvoeren public DAKlant() : base() { }

public Klant GetKlantById(int id) { Klant klant = null; connection.Open();

SqlCommand command = new SqlCommand( "Select * from Klant where id = @id", connection); command.Parameters.Add(new SqlParameter("@id", id));

SqlDataReader dataReader = command.ExecuteReader(); if (dataReader.Read()) { klant = new Klant( (int)dataReader["id"], dataReader["Naam"].ToString(), dataReader["Adres"].ToString(), (int)dataReader["GemeenteID"], (decimal)dataReader["Saldo"], dataReader["Login"].ToString(), dataReader["Wachtwoord"].ToString() ); }

dataReader.Close(); connection.Close(); return klant; }

public List<Klant> GetKlantenByGemeente(int gemeenteID) { List<Klant> lijst = new List<Klant>(); connection.Open(); SqlCommand command = new SqlCommand( "Select * from Klant where GemeenteID = @gemeenteID order by Naam", connection); command.Parameters.Add(new SqlParameter("@gemeenteID", gemeenteID));

SqlDataReader dataReader = command.ExecuteReader(); while (dataReader.Read()) { lijst.Add(new Klant( (int)dataReader["id"], dataReader["Naam"].ToString(), dataReader["Adres"].ToString(), (int)dataReader["GemeenteID"], (decimal)dataReader["Saldo"], dataReader["Login"].ToString(), dataReader["Wachtwoord"].ToString()) ); } dataReader.Close(); connection.Close(); return lijst; }

public Klant GetKlantByLogin(string login) { Klant klant = null;

Page 11: KHK 2Ti C# Samenvatting

connection.Open();

SqlCommand command = new SqlCommand( "Select * from Klant where login = @login", connection); command.Parameters.Add(new SqlParameter("@login", login));

SqlDataReader dataReader = command.ExecuteReader(); if (dataReader.Read()) { klant = new Klant( (int)dataReader["id"], dataReader["Naam"].ToString(), … ); }

dataReader.Close(); connection.Close(); return klant; }

public void InsertKlant(Klant klant) { // extra business-logica: alle klant hebben een unieke login // (later in aparte klasse) if (this.GetKlantByLogin(klant.Login) == null) { connection.Open();

SqlCommand command = new SqlCommand( "Insert into Klant (Naam, Adres, GemeenteID, Saldo, Login, Wachtwoord) values(@Naam, @Adres, @GemeenteID, @Saldo, @Login, @Wachtwoord)", connection); command.Parameters.Add(new SqlParameter("@Naam", klant.Naam)); command.Parameters.Add(new SqlParameter("@Adres", klant.Adres)); command.Parameters.Add(new SqlParameter("@GemeenteID", klant.GemeenteID)); command.Parameters.Add(new SqlParameter("@Saldo", klant.Saldo)); command.Parameters.Add(new SqlParameter("@Login", klant.Login)); command.Parameters.Add(new SqlParameter("@Wachtwoord", klant.Wachtwoord));

command.ExecuteNonQuery(); connection.Close(); } }

public bool IsKlantMetKorting(int id) { // extra business-logica: klanten met een saldo >= 10000 hebben korting bool korting = false;

Klant klant = this.GetKlantById(id); if (klant != null) { if (klant.Saldo >= 10000) { korting = true; } } return korting; }

public bool IsLoginOk(string login, string wachtwoord) { // klanten met een geldige login en wachtwoord mogen inloggen bool toegang = false;

Klant klant = this.GetKlantByLogin(login); if (klant != null) { if (klant.Wachtwoord == wachtwoord) { toegang = true; } }

return toegang; } }}

– FormLogin:

using Klanten.EntityClasses;using Klanten.DataAccessClasses;…

Page 12: KHK 2Ti C# Samenvatting

private void buttonAanmelden_Click(…) { DAKlant daKlant = new DAKlant(); if (daKlant.IsLoginOk(textBoxLogin.Text, textBoxWachtwoord.Text)) { FormKlanten form = new FormKlanten(); form.ShowDialog(); }}

– FormKlanten:

using Klanten.EntityClasses;using Klanten.DataAccessClasses;…

private void FormKlanten_Load(…) { DAGemeente daGemeente = new DAGemeente(); List<Gemeente> gemeenten = daGemeente.GetGemeenten();

foreach (Gemeente gemeente in gemeenten) { comboBoxGemeenten.Items.Add(gemeente); }}

private void comboBoxGemeenten_SelectedIndexChanged(…) { Gemeente gemeente = (Gemeente)comboBoxGemeenten.SelectedItem;

DAKlant daKlant = new DAKlant(); List<Klant> klanten = daKlant.GetKlantenByGemeente(gemeente.ID);

checkedListBoxKlanten.Items.Clear(); foreach (Klant klant in klanten) { checkedListBoxKlanten.Items.Add(klant); if (daKlant.IsKlantMetKorting(klant.ID)) { checkedListBoxKlanten.SetItemChecked(checkedListBoxKlanten.Items.Count-1, true); } }}

Page 13: KHK 2Ti C# Samenvatting

TREEVIEW

– Voorbeeld: – Visual Studio 2010 > Open > Project > Treeview.sln– imageListIcons: land.bmp, select.bmp, stad.bmp, wereld.bmp– imageList = imageListIcons

– Afbeeldingen voor Nodes [√] in TreeView komen uit ImageList– SelectedImageIndex = 1

– Afbeelding voor geselecteerde Node heeft index 1

– TreeView opvullen:

private void FormTreeView_Load(object sender, EventArgs e) { // TreeView niet hertekenen tijdens opvullen treeViewLanden.BeginUpdate();

// Europa toevoegen aan Nodes-collectie van treeview // Parameters: key of name (best uniek), tekst, afbeeldingIndex TreeNode rootNode = treeViewLanden.Nodes.Add("E", "Europa", 3);

// landKnoop België toevoegen aan Nodes-collectie van rootKnoop Europa TreeNode landNode = new TreeNode(); landNode.Name = "L1"; landNode.Text = "België"; landNode.ImageIndex = 0; rootNode.Nodes.Add(landNode);

// steden toevoegen aan landKnoop België landNode.Nodes.Add("L1S1", "Antwerpen", 2); landNode.Nodes.Add("L1S2", "Brussel", 2); landNode.Nodes.Add("L1S3", "Gent", 2); TreeNode geelNode = landNode.Nodes.Add("L1S4", "Geel", 2);

geelNode.Nodes.Add("Holven"); geelNode.Nodes.Add("Elsum"); geelNode.Nodes.Add("Bel"); geelNode.Nodes.Add("Larum");

landNode = rootNode.Nodes.Add("L2", "Frankrijk", 0); landNode.Nodes.Add("L2S1", "Parijs", 2); landNode.Nodes.Add("L2S2", "Lille", 2); landNode.Nodes.Add("L2S3", "Bordeaux", 2);

// TreeView hertekenen treeViewLanden.EndUpdate();}

– Geselecteerde knoop:

private void treeViewLanden_AfterSelect(object sender, TreeViewEventArgs e) { // tekst van geselecteerde knoop opvragen labelSelected.Text = treeViewLanden.SelectedNode.Text;

// of: labelSelected.Text = e.Node.Text; // volledig pad: FullPath}

// een knoop (hier eerste = Europa) openklappentreeViewLanden.Nodes[0].Expand();

– Zoeken in nodes:

// in Nodes-collectie zoeken naar knoop met key L1// true = ook kindknopen doorzoeken// key is uniek, dus slechts 1 knoop wordt gevonden // → collectie omzetten naar 1 element met Single()TreeNode nodeBelgie = treeViewLanden.Nodes.Find("L1", true).Single();

// Parent is bovenliggende knoopstring werelddeel = nodeBelgie.Parent.Text

// Nodes bevat alle kindknopenforeach (TreeNode stadNode in nodeBelgie.Nodes) { … }

– Alle nodes uitklappen:

private void buttonExpand_Click(…) {

Page 14: KHK 2Ti C# Samenvatting

KlapAllesUit();}

private void KlapAllesUit() { for (int i = 0; i < treeViewLanden.Nodes.Count; i++) { treeViewLanden.Nodes[i].Expand(); }}

private void buttonExpand_Click(…) { // rootNode (Europa) als parameter KlapUit(treeViewLanden.Nodes[0]);}

private void KlapUit(TreeNode node) { // eerst huidige knoop openklappen node.Expand(); // daarna verder met alle kinderen van huidige knoop foreach (TreeNode childNode in node.Nodes) { // recursie KlapUit(childNode); }}

Page 15: KHK 2Ti C# Samenvatting

ADO.NET DISCONNECTED

– DataSet:– Disconnected → alleen tijdens opvullen en updaten van de DataSet een geopende connectie – Bevat één of meerdere DataTable objecten (Tables-property)– Tussen die DataTable objecten kunnen relaties bestaan (Relations-property)– DataTable bestaat uit DataRow en DataColumn objecten (Rows- en Columns-property)

– DataAdapter:– Brug tussen Tabel in DataBase en DataTable in DataSet– Fill()-methode

– Opvullen van de DataTable met gegevens uit de DataBase – Update()-methode

– Wijzigingen in de DataTable wegschrijven naar DataBase – Juiste SQL-statement uitvoeren a.h.v. RowState van elke DataRow

– Unchanged, Deleted, Added, Modified

– Voorbeeld:– Visual Studio 2010 > Open > Project > Disconnected.sln– DataSet (met DataTables) wordt vanuit code opgebouwd (zowel structuur als gegevens)– Volgende les: DataSet opvullen met gegevens uit een DataBase m.b.v. Fill()

en wijzigingen wegschrijven m.b.v. Update()

– Een DataSet maken:

using System.Data;

public partial class FormNoDatabase : Form { DataSet _dataset; …}

private void maakDataSet() { _dataset = new DataSet(); …}

– Een DataTable in een DataSet maken:

// nieuwe datatable werknemerDataTable tableWerknemer = new DataTable("werknemer");…

// datatable werknemer toevoegen aan dataset_dataset.Tables.Add(tableWerknemer);

// tweede datatable afdelingDataTable tableAfdeling = new DataTable("afdeling");…

// datatable afdeling aan dataset toevoegen_dataset.Tables.Add(tableAfdeling);

– Kolommen in een DataTable maken:

DataTable tableWerknemer = new DataTable("werknemer");

DataColumn kolom;// eerste kolom id, geen nulls, uniekkolom = new DataColumn("id", typeof(Int32));kolom.AllowDBNull = false;kolom.Unique = true;tableWerknemer.Columns.Add(kolom);

// 2de manier, korter maar minder optiestableWerknemer.Columns.Add(new DataColumn("naam", typeof(String)));tableWerknemer.Columns.Add(new DataColumn("geboortedatum", typeof(DateTime)));tableWerknemer.Columns.Add(new DataColumn("afdelingId", typeof(Int32)));

– Primaire sleutel definiëren:

tableWerknemer.PrimaryKey = new DataColumn[] { tableWerknemer.Columns["id"] };

– Relaties leggen:

// dankzij relaties kan je navigeren van parent-DataTable naar child-DataTable en omgekeerd

Page 16: KHK 2Ti C# Samenvatting

// relatie van id in afdeling naar afdelingId in werknemer // (true betekent foreign key constraint toevoegen, dus geen werknemers toevoegen met // onbestaande afdelingId) _dataset.Relations.Add(new DataRelation( "fkAfdelingWerknemer", tableAfdeling.Columns["id"], tableWerknemer.Columns["afdelingId"], true));

– Rijen toevoegen:

// nieuwe rijen toevoegen DataRow rij;

rij = tableWerknemer.NewRow();rij["id"] = 1;rij["naam"] = "Jef Verboven";rij["geboortedatum"] = "22/11/1967";rij["afdelingId"] = 100;tableWerknemer.Rows.Add(rij);

– Gegevens tonen m.b.v. Rows-collectie:

// alle afdelingen in Rows-collectie van DataTabel aflopenforeach (DataRow afdeling in _dataset.Tables["afdeling"].Rows) { // toevoegen aan treeview // key = afdelingId TreeNode nodeAfdeling = treeViewPersoneel.Nodes.Add( afdeling["id"].ToString(), afdeling["naam"].ToString() );}

– Of met index en Field()-methode:

for (int i = 0; i < _dataset.Tables["afdeling"].Rows.Count; i++) { DoeIets(_dataset.Tables["afdeling"].Rows[i].Field<String>("naam"));}

– Navigeren m.b.v. een relatie:

foreach (DataRow afdeling in _dataset.Tables["afdeling"].Rows) { TreeNode nodeAfdeling = treeViewPersoneel.Nodes.Add( afdeling["id"].ToString(), afdeling["naam"].ToString() );

foreach (DataRow werknemer in afdeling.GetChildRows("fkAfdelingWerknemer")) { nodeAfdeling.Nodes.Add( werknemer["id"].ToString(), werknemer["naam"].ToString() ); }}

– Kolommen in een DataTable:

// alle kollommen van werknemer-datatable aflopenforeach (DataColumn kolom in _dataset.Tables["werknemer"].Columns) { DoeIets(kolom.ColumnName);}

– Zoeken op primaire sleutel:

private void treeViewPersoneel_AfterSelect(…) { if (treeViewPersoneel.SelectedNode.Level == 1) { // Id van geselecteerde Node ophalen _werknemerId = Int32.Parse(treeViewPersoneel.SelectedNode.Name);

// zoeken op primaire sleutel DataRow werknemer = _dataset.Tables["werknemer"].Rows.Find(werknemerId); textBoxNaam.Text = werknemer["naam"].ToString(); // DateTime omzetten naar gewenste formaat textBoxGeboortedatum.Text = ((DateTime)(werknemer["geboortedatum"]))

Page 17: KHK 2Ti C# Samenvatting

.ToString("dd/MM/yyyy"); }}

– Rijen wijzigen:

private void buttonAanpassen_Click(…) { DataRow werknemer = _dataset.Tables["werknemer"].Rows.Find(_werknemerId);

werknemer.BeginEdit(); werknemer["naam"] = textBoxNaam.Text; werknemer["geboortedatum"] = textBoxGeboortedatum.Text; werknemer.EndEdit();

treeViewPersoneel.SelectedNode.Text = textBoxNaam.Text;}

– Rijen schrappen:

private void buttonSchrappen_Click(…) { DataRow werknemer = _dataset.Tables["werknemer"].Rows.Find(_werknemerId);

// een rij verwijderen (Remove <> Delete, zie later) _dataset.Tables["werknemer"].Rows.Remove(werknemer); // SelectedNode verwijderen uit Nodes-collectie van Parent treeViewPersoneel.SelectedNode.Parent.Nodes.Remove(treeViewPersoneel.SelectedNode);

}

Page 18: KHK 2Ti C# Samenvatting

ADO.NET DATASETS EN DATABASES

– SQL Server Management Studio:– UCI.sql uitvoeren– Ploeg en renner

– Project DataAdapter (UCI):– textBoxNaam, comboBoxPloeg, labelRowState– buttonToevoegen, buttonWijzigen, buttonSchrappen– buttonEerste, buttonVorige, buttonVolgende, buttonLaatste

– ConnectieString in App.Config:

<xml version="1.0" encoding="utf-8" ?><configuration> <connectionString> <add name="school" connectionString = "…" /> </connectionString></configuration>

– Namespaces en Module variabelen:

// Connectiestring uit App.configusing System.Configuration;// SQL Server Data Provider gebruikenusing System.Data.SqlClient;

namespace DataAdapter { public partial class FormUCI : Form { DataSet _dataset; // DataAdapter om renners op te halen en later terug weg te schrijven SqlDataAdapter _rennerAdapter; // index van huidige renner int _index;

… }}

– Connectie:

private void LeesGegevens() { _dataset = new DataSet();

string connectionString = ConfigurationManager.ConnectionStrings["school"].ConnectionString; SqlConnection connection = new SqlConnection(connectionString);

// connectie moet niet geopend worden, DataAdapter doet zelf connectie open en dicht …}

– Gegevens ophalen met DataAdapter:

// Command-object maken met juiste Select-statement// eventueel parameters gebruiken i/h SQL-statementSqlCommand ploegCommand = new SqlCommand("Select * From Ploeg", connection);

// DataAdapter om DataSet op te vullen (gebruikt Command-object)// mag lokaal gedefinieerd want later niet meer nodig om aanpassingen terug te sturenSqlDataAdapter ploegAdapter = new SqlDataAdapter(ploegCommand);

// Fill-methode om dataset op te vullen// naam DataTable is PloegploegAdapter.Fill(_dataset, "Ploeg");

– Commandbuilder:

// aparte SqlCommand gebruiken voor 2de tabel !SqlCommand rennerCommand = new SqlCommand("Select * From Renner", connection);

// aparte DataAdapter om renners op te halen! // = Module-variabele (DataAdapter straks ook gebruikt om wijzigingen terug te schrijven)_rennerAdapter = new SqlDataAdapter(rennerCommand);

// maak de insert, update en delete-commands automatisch aan

Page 19: KHK 2Ti C# Samenvatting

SqlCommandBuilder commandBuilder = new SqlCommandBuilder(_rennerAdapter);

// tweede tabel ophalen, 2de DataTable met naam Renner_rennerAdapter.Fill(_dataset, "Renner");

– Gegenereerde SQL-statement:

Console.WriteLine(commandBuilder.GetUpdateCommand().CommandText);Console.WriteLine(commandBuilder.GetInsertCommand().CommandText);Console.WriteLine(commandBuilder.GetDeleteCommand().CommandText);

Update command Generated by the Command Builder : ==================================================UPDATE [Renner] SET [Naam] = @p1, [PloegID] = @p2 WHERE (([ID] = @p3) AND ((@p4 = 1 AND [Naam] IS NULL) OR ([Naam] = @p5)) AND ((@p6 = 1 AND [PloegID] IS NULL) OR ([PloegID] = @p7)))

Insert command Generated by the Command Builder : ==================================================INSERT INTO [Renner] ([Naam], [PloegID]) VALUES (@p1, @p2)

Delete command Generated by the Command Builder : ==================================================DELETE FROM [Renner] WHERE (([ID] = @p1) AND ((@p2 = 1 AND [Naam] IS NULL) OR ([Naam] = @p3)) AND ((@p4 = 1 AND [PloegID] IS NULL) OR ([PloegID] = @p5)))

// niet controleren op gewijzigde informatiecommandBuilder.ConflictOption = ConflictOption.OverwriteChanges;

Update command Generated by the Command Builder : ==================================================UPDATE [Renner] SET [Naam] = @p1, [PloegID] = @p2 WHERE (([ID] = @p3))

Insert command Generated by the Command Builder : ==================================================INSERT INTO [Renner] ([Naam], [PloegID]) VALUES (@p1, @p2)

Delete command Generated by the Command Builder : ==================================================DELETE FROM [Renner] WHERE (([ID] = @p1))

– CommandBuilder:– Default

– Indien dezelfde informatie door een andere gebruiker gewijzigd werd– foutmelding bij bewaren aanpassingen (= Optimistic Locking)– opvangen met try-catch

– Overwrite Changes– Alleen checken op primary key (= Geen locking)

– Zelf– Eigen SQL-statements (stored procedures) opgeven (later)

– DataBinding:

private void ToonPloegen() { // ofwel één voor één in Items-collectie stoppen: // foreach (DataRow rij in dataset.Tables["Ploeg"].Rows) { // comboBoxPloeg.Items.Add(rij.Field<string>("Naam")); // }

// beter DataBinding om gegevens in ComboBox te tonen: comboBoxPloeg.DataSource = _dataset.Tables["Ploeg"]; // veld dat in de combobox getoond wordt comboBoxPloeg.DisplayMember = "Naam"; // veld dat gebruikt wordt als SelectedValue comboBoxPloeg.ValueMember = "ID";}

– RowStates:– Unchanged: geen wijzigen– Deleted: Gemarkeerd als Delete → DeleteCommand bij Update – Added: Nieuwe rij → InserCommand bij Update – Modified: Aangepast → UpdateCommand bij Update

_dataset.Tables["Renner"].Rows[i].RowState

– RowState en Field()-methode:

Page 20: KHK 2Ti C# Samenvatting

private void ToonRenner(int i) { // ook verwijderde renners staan nog in de Rows-collectie // dus alleen gegevens tonen als renner niet verwijderd werd if (_dataset.Tables["Renner"].Rows[i].RowState != DataRowState.Deleted) { textBoxNaam.Text = _dataset.Tables["Renner"].Rows[i].Field<String>("Naam"); comboBoxPloeg.SelectedValue = _dataset.Tables["Renner"].Rows[i].Field<Int32>("PloegID"); } else { textBoxNaam.Text = "###"; comboBoxPloeg.SelectedValue = 0; }

labelRowState.Text = _dataset.Tables["Renner"].Rows[i].RowState.ToString();}

– Rijen toevoegen:

private void buttonToevoegen_Click(object sender, EventArgs e) { DataRow rij = _dataset.Tables["Renner"].NewRow();

rij["Naam"] = textBoxNaam.Text; // opvragen ID (ValueMember) rij["PloegID"] = comboBoxPloeg.SelectedValue;

_dataset.Tables["Renner"].Rows.Add(rij);}

– Rijen wijzigen:

private void buttonAanpassen_Click(object sender, EventArgs e) { DataRow rij = _dataset.Tables["Renner"].Rows[_index]; rij.BeginEdit(); rij["Naam"] = textBoxNaam.Text; rij["PloegID"] = comboBoxPloeg.SelectedValue; rij.EndEdit();}

– Rijen verwijderen:

private void buttonSchrappen_Click(object sender, EventArgs e) { DataRow rij = _dataset.Tables["Renner"].Rows[_index];

// Markeren als Delete, niet Remove! Dan is de rij echt weg uit Rows-collectie // en kan Delete-statement niet naar database gestuurd worden rij.Delete();}

– Aangepaste rijen wegschrijven:

private void Form1_FormClosing(object sender, FormClosingEventArgs e) { // afhankelijk van RowStates juiste SQL-DML-statements // naar database sturen // best met Try-Catch want eventueel concurrency-fouten! (hierover later meer) try { _rennerAdapter.Update(_dataset, "Renner"); } catch (DBConcurrencyException exTransaction) { MessageBox.Show(exTransaction.Message); // Cancel FormClosing e.Cancel = true; }}

Page 21: KHK 2Ti C# Samenvatting

ADO.NET TYPED DATASETS

– 3TIER met Typed DataSets:– Data Tier

– Tabellen, views en stored procedures (zie later)– Logical Tier

– Data Access Layer (DAL)– Methodes voor basisbewerkingen: mbv TableAdapter (ipv zelf te schrijven)– TableAdapter = DataAdapter met build-in connection

TableAdapter bevat automatisch de CRUD-basisoperaties– Gegevens doorgeven aan Business Logic Layer mbv DataTable (ipv List) en

DataRow (ipv entity objecten)– Business Logic Layer (BLL)

– methodes met extra Business Logica: maakt gebruik van DAL– Presentation Tier

– Grafische interface met de gebruiker (windows forms), gebruikt alleen methoden uit BLL

– Typed DataSets:– Gewone Dataset = loosely-typed

– Kolommen van DataTable zijn pas gekend at runtime (na opvullen Dataset) → ook geen intellisense

dataset.Tables["renner"].Rows[i]["naam"]

– Typed Dataset = strongly-typed– Kolommen zijn geïmplementeerd als properties (klassen gegenereerd door Visual

Studio) en gekend tijdens compilatie → wel intellisense– Merk op: Datatables kunnen ook bestaan zonder DataSet

RennerDataTable.Rows[i].Naam

– Voorbeeld OrderToevoegen:– Maak de tabellen aan via Noorderwind.sql– Open solution OrderToevoegen– Connecteer via Server Explorer met je persoonlijke SQL Server Database

– Typed DataSets:– DAL > Rechts > Add > New Item > DataSet > Noorderwind.xsd (niet dezelfde naam als project!)

– KlantenDataTable:– Versleep tabel Klanten uit Server Explorer naar DataSet – ConnectieString wordt automatisch toegevoegd aan App.Config– KlantenDataTable (ipv List) met KlantenRow (ipv Entity Object)– KlantenTableAdapter met basisoperaties en eventueel extra methodes

– KlantenTableAdapter:– Creatie van methode GetAllKlanten()– Selecteer Fill, GetData() > Rechts > Configure

SELECT Klantnummer, Bedrijf, Adres, Plaats, Postcode, Land, TelefoonnummerFROM dbo.KlantenORDER BY Bedrijf

– GetAllKlanten(): – Next > Enkel tweede optie aanvinken (Return DataTable) > Finish > Save All

– Twee mogelijkheden:– Fill: DataTable, die meegegeven wordt als parameter, wordt opgevuld

Noorderwind.KlantenDataTable klantenDataTable = new Noorderwind.KlantenDataTable();adapter.Fill(klantenDataTable);

– Get: DataTable komt terug als resultaat van de functie (wij gebruiken steeds deze variant)

Noorderwind.KlantenDataTable klantenDataTable = adapter.GetAllKlanten();

– DAL: – Solution Explorer > Class view

– BLL: – BLL > Rechts > Add > New Item > Class > BLKlanten.cs

using OrderToevoegen.DAL;

Page 22: KHK 2Ti C# Samenvatting

using OrderToevoegen.DAL.NoorderwindTableAdapters;

namespace OrderToevoegen.BLL { class BLKlanten { private KlantenTableAdapter adapter;

public BLKlanten() { adapter = new KlantenTableAdapter(); }

public Noorderwind.KlantenDataTable GetAllKlanten() { return adapter.GetAllKlanten(); } }

}

– FormOrderToevoegen (Front End):

using OrderToevoegen.BLL;using OrderToevoegen.DAL;

private void FormOrderToevoegen_Load(…) { // Opvullen combobox klantgegevens BLKlanten blKlanten = new BLKlanten(); Noorderwind.KlantenDataTable klanten = blKlanten.GetAllKlanten(); // Data Binding ComboBoxKlant.DataSource = klanten; ComboBoxKlant.DisplayMember = "Bedrijf"; ComboBoxKlant.ValueMember = "Klantnummer";}

– Een methode met een paramete (Verkopers):– Versleep tabel Werknemers uit Server Explorer naar Noorderwind.xsd– Creatie van methode GetWerknemersByFunctie(functie)– Selecteer Fill, GetData() > Rechts > Configure

SELECT WerknemerID, Familienaam, Voornaam, Functie, Beleefdheidstitel, Geboortedatum, InDienst, Familienaam + ' ' + Voornaam as Naam FROM dbo.Werknemers WHERE Functie = @Functie ORDER BY Familienaam

– Next > Enkel Return DataTable aanvinken: GetWerknemersByFunctie > Next > Finish > Save All

– BLWerknemers:– BLL > Rechts > Add > New Item > Class > BLWerknemers.cs

using OrderToevoegen.DAL;using OrderToevoegen.DAL.NoorderwindTableAdapters;

namespace OrderToevoegen.BLL { class BLWerknemers { private WerknemersTableAdapter adapter;

public BLWerknemers() { adapter = new WerknemersTableAdapter(); }

public Noorderwind.WerknemersDataTable GetVerkopers() { return adapter.GetWerknemersByFunctie("Verkoper"); } }}

– FormOrderToevoegen (Front End):

private void FormOrderToevoegen_Load(…) { … // Opvullen combobox verkoper BLWerknemers blWerknemers = new BLWerknemers(); Noorderwind.WerknemersDataTable verkopers = blWerknemers.GetVerkopers(); ComboBoxVerkoper.DataSource = verkopers; ComboBoxVerkoper.DisplayMember = "Naam"; ComboBoxVerkoper.ValueMember = "WerknemerID";}

Page 23: KHK 2Ti C# Samenvatting

– Oefening:– DAL: ProductenTableAdapter: GetAllProducten() gesorteerd op productnaam– BLL: BLProducten: GetAllProducten()

using OrderToevoegen.DAL;using OrderToevoegen.DAL.NoorderwindTableAdapters;namespace OrderToevoegen.BLL { class BLProducten { private ProductenTableAdapter adapter; public BLProducten() { adapter = new ProductenTableAdapter(); } public Noorderwind.ProductenDataTable GetVerkopers() { return adapter.GetAllProducten(); } }}

– Front End: ComboBoxProducten opvullen: display = naam; value = nummer

// Opvullen combobox productenBLProducten blProducten = new BLProducten();Noorderwind.ProductenDataTable producten = blProducten.GetAllProducten();ComboBoxProduct.DataSource = producten;ComboBoxProduct.DisplayMember = producten.NaamColumn.ColumnName;ComboBoxProduct.ValueMember = producten.NummerColumn.ColumnName;

– Orderdatum = Systeemdatum

// Datum invullenTextBoxOrderdatum.Text = DateTime.Today.ToString("dd/MM/yyyy");

– KlantenRow i.p.v. EntityClasses:

private void ComboBoxKlant_SelectedIndexChanged(…) { // klantobject ophalen Noorderwind.KlantenRow klant = (Noorderwind.KlantenRow) ((System.Data.DataRowView)ComboBoxKlant.SelectedItem).Row; // klantobject (KlantenRow) wordt door XSD automatisch gegenereerd LabelBedrijf.Text = klant.Bedrijf; LabelAdres.Text = klant.Adres; LabelPostcode.Text = klant.Postcode; LabelPlaats.Text = klant.Plaats; LabelLand.Text = klant.Land;}

– Gegevens wijzigen m.b.v. TableAdapter:– Batch-update

– Meerdere Rows in de DataTable worden aangepast– Volledige DataTable wordt doorgegeven aan Adapter– Afhankelijk van de RowState van elke rij worden door de Adapter de juiste SQL-

statements naar de database gestuurd– Row-update

– Adapter bevat Insert, Update en Delete-methodes die één rij bewerken– Deze methodes worden onmiddellijk naar de database gestuurd en verwerkt

– Batch-update:

OrderlijnenTableAdapter adapter = new OrderlijnenTableAdapter();Noorderwind.OrderlijnenDataTable orderlijnen = adapter.GetAllOrderlijnen();

// een orderlijn aanpassenorderlijnen[2].Hoeveelheid = 15;

// een orderlijn schrappenorderlijnen[1].Delete();

// een orderlijn toevoegenNoorderwind.OrderlijnenRow orderlijn = orderlijnen.NewOrderlijnenRow();orderlijn.OrderID = 9999;orderlijn.Productnummer = …orderlijnen.Rows.Add(orderlijn);

// Batch updateadapter.Update(orderlijnen);

Page 24: KHK 2Ti C# Samenvatting

– Row-update:

OrdersTableAdapter adapter = new OrdersTableAdapter();

// een order toevoegenadapter.InsertOrder(klantnummer, …);

// een order schrappenadapter.DeleteOrder(orderID);

// een order aanpassenadapter.UpdateOrder(orderID, klantnummer, …);

– Batch-update van Orderlijnen (in DAL)– Versleep tabel Orderlijnen uit Server Explorer naar Noorderwind.xsd– Fill, GetData() > … > GetAllOrderlijnen() – Alleen InserCommand wordt aangemaakt (tabel heeft geen PK, foutje van Microsoft, normaal ook

Update en Delete)

– BLOrderlijnen:– BLL > Rechts > Add > New Item > Class > BLOrderlijnen.cs

using OrderToevoegen.DAL;using OrderToevoegen.DAL.NoorderwindTableAdapters;

namespace OrderToevoegen.BLL { class BLOrderlijnen { private OrderlijnenTableAdapter adapter;

public BLOrderlijnen() { adapter = new OrderlijnenTableAdapter(); }

public void InsertAll(Noorderwind.OrderlijnenDataTable orderlijnen) { adapter.Update(orderlijnen); } }}

– Totaalbedrag van het order in BLL:

public decimal BerekenTotaalOrderBedrag(Noorderwind.OrderlijnenDataTable orderlijnen) { decimal totaal = 0; foreach (Noorderwind.OrderlijnenRow orderlijn in orderlijnen) { totaal += (orderlijn.Eenheidsprijs * orderlijn.Hoeveelheid); } return totaal;}

– Orderlijnen (Front End):

public partial class FormOrderToevoegen : Form { // lege orderlijnen-datatable Noorderwind.OrderlijnenDataTable orderlijnen = new Noorderwind.OrderlijnenDataTable(); … private void FormOrderToevoegen_Load(…) { … // wijzigingen in datatable orderlijnen tonen in datagridview dataGridViewOrderlijnen.DataSource = orderlijnen; } …}

– Orderlijn toevoegen:

private void ButtonToevoegen_Click(…) { Noorderwind.ProductenRow product = (Noorderwind.ProductenRow) ((System.Data.DataRowView)ComboBoxProduct.SelectedItem).Row;

// nieuwe orderlijn toevoegen aan orderlijnen-datatable Noorderwind.OrderlijnenRow orderlijn = orderlijnen.NewOrderlijnenRow(); // voorlopige orderid, later echte waarde invullen orderlijn.OrderID = 9999; orderlijn.Productnummer = product.Nummer; orderlijn.Eenheidsprijs = product.Prijs;

Page 25: KHK 2Ti C# Samenvatting

orderlijn.Hoeveelheid = short.Parse(TextBoxHoeveelheid.Text); orderlijnen.Rows.Add(orderlijn);

BLOrderlijnen blOrderlijnen = new BLOrderlijnen(); TextBoxTotaal.Text = blOrderlijnen.BerekenTotaalOrderBedrag(orderlijnen).ToString();}

– Row-Insert van Orders (in DAL)– Versleep tabel Orders uit Server Explorer naar Noorderwind.xsd– Fill, GetData() > … > GetAllOrders()– Merk op: nu wel InserCommand, UpdateCommand (met optimistic locking) en DeleteCommand– We gebruiken deze commands niet, maar voorzien zelf een InsertOrder– Rechts op adapter > Add Query > Use SQL Statements > Insert

INSERT INTO [dbo].[Orders]([Klantnummer], [WerknemerID], [Orderdatum])VALUES (@Klantnummer, @WerknemerID, @Orderdatum);SELECT SCOPE_IDENTITY()

– Naam = InsertOrder– Via Properties: Execute mode = Scalar (ID van toegevoegde order is returnwaarde)

– Row-Update van Voorraad (in DAL)– Producten– Rechts op adapter > Add query > Use SQL Statements > Update

UPDATE ProductenSET Voorraad = Voorraad - @HoeveelheidWHERE Nummer = @Nummer

– Naam = UpdateVoorraad– Via Properties: Execute mode blijft NonQuery– Parameters: alleen @Nummer en @Hoeveelheid (geen SourceColumn)

– BLProducten:

public void UpdateVoorraad(int nummer, short hoeveelheid) { adapter.UpdateVoorraad(nummer, hoeveelheid);}

– BLOrders:– BLL > Rechts > Add > New Item > Class > BLOrders.cs

using OrderToevoegen.DAL;using OrderToevoegen.DAL.NoorderwindTableAdapters;

namespace OrderToevoegen.BLL { class BLOrders { private OrdersTableAdapter adapter;

public BLOrders() { adapter = new OrdersTableAdapter(); }

public void InsertOrder(Noorderwind.OrdersRow order, Noorderwind.OrderlijnenDataTable orderlijnen) { // 1. order toevoegen, nieuwe ID opvragen int orderid = Convert.ToInt32( adapter.InsertOrder(order.Klantnummer, order.WerknemerID, order.Orderdatum));

// in orderlijnen juiste orderid invullen foreach (Noorderwind.OrderlijnenRow orderlijn in orderlijnen) { orderlijn.OrderID = orderid; }

// 2. batch-update van orderlijnen BLOrderlijnen blOrderlijnen = new BLOrderlijnen(); blOrderlijnen.InsertAll(orderlijnen); // 3. voorraad aanpassen BLProducten blProducten = new BLProducten(); foreach (Noorderwind.OrderlijnenRow orderlijn in orderlijnen) { blProducten.UpdateVoorraad(orderlijn.Productnummer, orderlijn.Hoeveelheid); } } }}

Page 26: KHK 2Ti C# Samenvatting

– Order opslaan (Front end)

private void ButtonOpslaan_Click(…) { // geselecteerde klant en verkoper ophalen Noorderwind.KlantenRow klant = (Noorderwind.KlantenRow) ((System.Data.DataRowView)ComboBoxKlant.SelectedItem).Row; Noorderwind.WerknemersRow werknemer = (Noorderwind.WerknemersRow) ((System.Data.DataRowView)ComboBoxVerkoper.SelectedItem).Row;

// nieuw order-object maken Noorderwind.OrdersDataTable orders = new Noorderwind.OrdersDataTable(); Noorderwind.OrdersRow order = orders.NewOrdersRow();

order.Klantnummer = klant.Klantnummer; order.WerknemerID = werknemer.WerknemerID; order.Orderdatum = DateTime.Parse(TextBoxOrderdatum.Text);

// order toevoegen, orderlijnen toevoegen en voorraad aanpassen BLOrders blOrders = new BLOrders(); blOrders.InsertOrder(order, orderlijnen);}

Page 27: KHK 2Ti C# Samenvatting

MULTIPLE DOCUMENT INTERFACE

– Single Document Interface (SDI)– Slechts één formulier– Bijvoorbeeld: Kladblok

– Multiple Document Interface (MDI) – Verschillende Child Forms in één Parent Window– Bijvoorbeeld: Access

– Tabbed Document Interface (TDI)– Verschillende tabbladen in één Parent Window– Bijvoorbeeld: Firefox

– FormMDIParent:– Open MDIForms.sln– IsMDIContainer = true

– FormMDIChild:– Nieuw formulier FormMDIChild– textBoxDocument

– Multiline = true– Dock = fill

– Applicatie sluiten:

public partial class FormMDIParent : Form { // module variabele (private is default, alleen bereikbaar binnen het formulier) int _aantal = 0;

private void afsluitenToolStripMenuItem_Click(…) { Application.Exit(); }}

– ChildForms openen:

private void nieuwToolStripMenuItem_Click(…) { // Childforms nummeren _aantal++;

// nieuw Childform openen FormMDIChild formMDIChild = new FormMDIChild(); formMDIChild.Text = "Document " + _aantal.ToString();

// Childform aan Parent koppelen formMDIChild.MdiParent = this; formMDIChild.Show();}

– ChildForms schikken:

private void trapsgewijsToolStripMenuItem_Click(…) { this.LayoutMdi(MdiLayout.Cascade);}

private void naastElkaarToolStripMenuItem_Click(…) { this.LayoutMdi(MdiLayout.TileVertical);}

private void onderElkaarToolStripMenuItem_Click(…) { this.LayoutMdi(MdiLayout.TileHorizontal);}

– Actieve Child opvragen:

private void sluitenToolStripMenuItem_Click(…) { // zijn er MDIChildren? if (this.MdiChildren.Length != 0) { this.ActiveMdiChild.Close(); }}

– Childnummer doorgeven via constructor

public partial class FormMDIChild : Form {

Page 28: KHK 2Ti C# Samenvatting

// private variabele int _childNummer;

public FormMDIChild(int childNummer) { InitializeComponent(); this._childNummer = childNummer; this.Text = "Document " + this._childNummer.ToString(); }}

– In MDIParent:

private void nieuwToolStripMenuItem_Click(…) { // MDIChilds nummeren _aantal++;

// nieuw MDIChild FormMDIChild formMDIChild = new FormMDIChild(_aantal);

// Childform aan Parent koppelen formMDIChild.MdiParent = this; formMDIChild.Show();}

// Niet vergeten: formMDIChild.Text = _aantal

– GeselecteerdeTekst Property:

public partial class FormMDIChild : Form { …

public String GeselecteerdeTekst { get { // getter geeft geselecteerde tekst in textbox terug return textBoxDocument.SelectedText; } set { // setter past de tekst aan textBoxDocument.SelectedText = value; } }}

– In MDIParent:

private void knippenToolStripMenuItem_Click(…) { String geselecteerdeTekst = ((FormMDIChild)(this.ActiveMdiChild)).GeselecteerdeTekst; Clipboard.SetText(geselecteerdeTekst); ((FormMDIChild)(this.ActiveMdiChild)).GeselecteerdeTekst = "";}

private void kopiërenToolStripMenuItem_Click(…) { String geselecteerdeTekst = ((FormMDIChild)(this.ActiveMdiChild)).GeselecteerdeTekst; Clipboard.SetText(geselecteerdeTekst);}

private void plakkenToolStripMenuItem_Click(…) { ((FormMDIChild)(this.ActiveMdiChild)).GeselecteerdeTekst = Clipboard.GetText();}

// Merk op: er is geen foutcontrole// Bijvoorbeeld: kopiëren zonder ChildForm → extra programmeerwerk

– ToolStrip:– ToolStrip met 2 buttons: DisplayStyle = ImageAndText; ImageScaling = None– toolStripButtonNieuw: Image = Nieuw.bmp; Text = Nieuw – toolStripButtonSluiten: Image = Sluiten.bmp; Text = Sluiten

– Eventhandlers:– Bij drukken op de knoppen moet dezelfde code uitgevoerd worden als bij keuze in het menu– Nooit zelf eventhandlers oproepen!– Beter: Aparte procedure definiëren en oproepen– Ofwel: Dezelfde eventhandler aan actie hangen:

this. … .Click += new System.EventHandler(…);

Page 29: KHK 2Ti C# Samenvatting

– Eventhandlers met aparte procedure

private void NieuwDocumentOpenen() { // MDIChilds nummeren aantal++;

// nieuw MDIChild FormMDIChild formMDIChild = new FormMDIChild(aantal);

// Childform aan Parent koppelen formMDIChild.MdiParent = this; formMDIChild.Show();}

– Dezelfde eventhandler gebruiken:

public FormMDIParent() { InitializeComponent(); InitializeComponentExtra();}

private void InitializeComponentExtra() { toolStripButtonSluiten.Click += new EventHandler(sluitenToolStripMenuItem_Click);}

Page 30: KHK 2Ti C# Samenvatting

WINDOWS PHONE 7

– Mobiele Applicaties:– Native Apps:

– Draaien op het toestel– Windows toepassing– Draaien maar op één platform– Rijke UI, "snel"– Geen connectie met een server nodig

– Web Apps:– Draaien op een server– Web toepassing– Draaien op alle platformen (m.b.v. browser)– Webapplicatie, "traag"– Wel een connectie nodig

– Platformen:– Google Android:

– Open Source gebaseerd op de Linux-kernel– Java

– Apple iPhone en iPad– iOS– Objetive-C

– Microsoft Windows Phone– Windows Phone 7– C#– YouTube

– Symbian, Blackberry …

– Windows Phone 7 Architecture– Silverlight: Event driven (Windows-)applicatie– XNA: Hight performance 2D- of 3D-game

– Tools:– Downloaden van: http://create.msdn.com

– Visual Studio 2010 Express– Windows Phone Emulator– Silverlight– XNA Game Studio 4.0– Microsoft Expression Blend for Windows Phone– .NET Framework 4

– Voorbeeld: HelloPhone– Visual Studio 2010 > File > New Project > Visual C# > Silverlight for Windows Phone > Windows

Phone Application > HelloPhone

– Silverlight:– Silverlight is het antwoord van Microsoft op Flash– Output van Silverlight is XML (XAML) ↔ Output van Flash is binary– XAML zelf intypen (ofwel Expression Blend gebruiken)– Silverlight controls:

– Grid ≈ <table>– StackPanel– Button – TextBlock ≈ <label>– TextBox

– Voorbeeld Grid:

<Grid x:Name="gridVoorbeeld"> <Grid.RowDefinitions> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition Height="100"></RowDefinition> <RowDefinition Height="*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Button Name="buttonAuto" Content="Auto" Height="150" Grid.Row="0" Grid.Column="0"> </Button> <Button Name="buttonPixel" Content="Pixel" Height="100" Grid.Row="1" Grid.Column="1"> </Button>

Page 31: KHK 2Ti C# Samenvatting

<Button Name="buttonStar" Content="Star" Height="150" Grid.Row="2" Grid.Column="2"> </Button></Grid>

– Voorbeeld StackPanel:

<StackPanel x:Name="stackPanelVoorbeeld"> <Button x:Name="buttonClickMe" Content="Click Me!"></Button> <Button x:Name="buttonAndMe" Content="And Me!"></Button></StackPanel><StackPanel x:Name="stackPanelHorizontaalVoorbeeld" Orientation="Horizontal"> <Button x:Name="buttonClickMe" Content="Click Me!"></Button> <Button x:Name="buttonAndMe" Content="And Me!"></Button></StackPanel>

– Voorbeeld Button:

<Button x:Name="buttonHello" Content="Hello" Margin="20,5,75,0" Click="buttonHello_Click" />// Margin = "left, top, right, bottom"

– Voorbeeld TextBlock:

<StackPanel x:Name="stackPanelTitle" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="textBlockTitleApplication" Text="HELLO PHONE" Style="{StaticResource PhoneTextNormalStyle}" /> <TextBlock x:Name="textBlockTitlePage" Text="main" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" /></StackPanel>

– Voorbeeld TextBox:

<TextBox x:Name="textBoxName" Height="70" Width="150"></TextBox><TextBox x:Name="textBoxNumber" Height="70" Width="100"> <TextBox.InputScope> <InputScope> <InputScopeName NameValue="Number" /> </InputScope> </TextBox.InputScope></TextBox>

– GridContent verder uitwerken:

<Grid x:Name="gridContent" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel x:Name="stackPanelContent" Grid.Row="0" Orientation="Horizontal"> <TextBox x:Name="textBoxName" Height="70" Width="150"></TextBox> <TextBox x:Name="textBoxNumber" Height="70" Width="100"> <TextBox.InputScope> <InputScope> <InputScopeName NameValue="Number"/> </InputScope> </TextBox.InputScope> </TextBox> <Button x:Name="buttonHello" Content="Click Me!" Click=“buttonHello_Click"/> </StackPanel> <TextBlock Grid.Row="1" x:Name="textBlockBanner" TextAlignment="Center" Style="{StaticResource PhoneTextExtraLargeStyle}" FontWeight="Bold"/></Grid>

– buttonHello_Click():

private void buttonHello_Click(object sender, RoutedEventArgs e) { if (textBoxName.Text != "" && textBoxNumber.Text != "") { textBlockBanner.Text = "";

for (int i = 1; i <= int.Parse(textBoxNumber.Text); i++) { textBlockBanner.Text += textBoxName.Text + "\n"; } }}

Page 32: KHK 2Ti C# Samenvatting

SILVERLIGHT

– Silverlight – Expression Blend:– Visual Studio 2010 > File > New Project > Visual C# > Silverlight for Windows Phone > Windows

Phone Application > Silverlight– Applicatie titel = SILVERLIGHT; Pagina titel = main– Drie mogelijkheden om het design aan te passen:

– Met de designer van Visual Studio (toolbox + properties venster)– Rechtstreeks in XAML-code– Met Expression Blend

– Expression Blend:– Een tool waarmee grafisch ontwerpers het design van een Windows Phone Applicatie kunnen

aanpassen (≈ Flash)– Expression Blend en Visual Studio synchroniseren automatisch– Bewaar je project in Visual Studio– Start Expression Blend en open solution Silverlight.sln

– Application Bar (m.b.v. Expression Blend):– Objects en Timeline Panel (links midden) > selecteer PhoneApplicationPage– Properties Tab (rechts boven) > Common Properties > Application Bar > New– Buttons > …– Add another item > Search for ApplicationBar > ApplicationBarIconButton– IconUri > Download– Text = Next– Een tweede knop: Add another item > Search for ApplicationBar > ApplicationBarIconButton– Icon = Delete; Text = Delete – Bewaar in Expression Blend en ga terug naar Visual Studio– Reload

<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar> <shell:ApplicationBarIconButton x:Name="buttonNext" IconUri="/icons/appbar.download.rest.png" Text="Next"/> <shell:ApplicationBarIconButton x:Name="buttonDelete" IconUri="/icons/appbar.delete.rest.png" Text="Delete"/> </shell:ApplicationBar></phone:PhoneApplicationPage.ApplicationBar>

– Een tweede pagina:– Silverlight > Rechts > Add > New Item > Windows Phone Portrait Page > SecondPage.xaml

– SecondPage oproepen:– Cursor in tag van ButtonNext plaatsen

<shell:ApplicationBarIconButton x:Name="buttonNext" IconUri="/icons/appbar.download.rest.png" Text="Next"/>

– Properties venster > buttonNext > Events > Click > dubbelklikken

private void buttonNext_Click(…) { this.NavigationService.Navigate(new Uri("/SecondPage.xaml", UriKind.Relative));}

<shell:ApplicationBarIconButton x:Name="buttonNext" IconUri="/icons/appbar.download.rest.png" Text="Next" Click="buttonNext_Click" />

– Zelf een eventhandler koppelen:

public MainPage() { InitializeComponent(); buttonVerder.Click += buttonNext_Click;}

private void buttonNext_Click(…) { this.NavigationService.Navigate(new Uri("/SecondPage.xaml", UriKind.Relative));}

Page 33: KHK 2Ti C# Samenvatting

DATABINDING

– DataBinding: Gegevens tonen in een Windows Phone Applicatie m.b.v. DataBinding

– Voorbeeld:– Open solution ChampionsLeague.sln

– Ploeg.cs:

public class Ploeg { private int _id; private string _naam; private string _bestand; private string _land;

public int Id { get { return _id; } set { _id = value; } } …

public string Logo { get { return "Logos/" + _bestand; } }

public string Shirt { get { return "Shirts/" + _bestand; } } …

public Ploeg(int id, string naam, string bestand, string land) { Id = id; Naam = naam; Bestand = bestand; Land = land; }}

– Data.cs:

using System.Collections.Generic;using System.Linq;

Public class Data { private List<Ploeg> _ploegen;

public List<Ploeg> Ploegen { get { return _ploegen; } }

public Data() { _ploegen = new List<Ploeg>(); _ploegen.Add(new Ploeg(10, "Arsenal FC", "Arsenal.png", "England")); _ploegen.Add(new Ploeg(52, "FC Barcelona", "Barcelona.png", "Spain")); _ploegen.Add(new Ploeg(54, "FC Bayern München", "Bayern.png", "Germany")); … }

– App.xaml.cs:

public partial class App : Application { // Vanop elke xaml-page kan je App.Current opvragen // We krijgen dan een App-object // Daarin definiëren we een Data-object met de gegevens (in de list) public Data data = new Data(); …}

– MainPage.xaml:

<ListBox x:Name="listBoxPloeg" Grid.Row="0" Height="600"> … </ListBox>

public MainPage() { InitializeComponent(); listBoxPloeg.ItemsSource = ((App)(App.Current)).data.Ploegen;}

Page 34: KHK 2Ti C# Samenvatting

– ItemTemplate:

<ListBox x:Name="listBoxPloeg" Grid.Row="0" Height="600"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Naam}" FontSize="30" TextWrapping="Wrap" VerticalAlignment="Center" Margin="10,0,0,0"/> </DataTemplate> </ListBox.ItemTemplate></ListBox>

– MainPage.xaml:

<ListBox x:Name="listBoxPloeg" Grid.Row="0" Height="600" SelectionChanged="listBoxPloeg_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Logo}" Width="100" Height="100" Margin="10,10,0,10"/> <TextBlock Text="{Binding Naam}" FontSize="30" TextWrapping="Wrap" VerticalAlignment="Center" Margin="10,0,0,0"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate></ListBox>

private void listBoxPloeg_SelectionChanged(…) { if (listBoxPloeg.SelectedIndex != -1) { int id = ((Ploeg)listBoxPloeg.SelectedItem).Id; NavigationService.Navigate(new Uri("/DetailPage.xaml?id=" + id.ToString(), UriKind.Relative)); }}

– DetailPage.xaml:

protected override void OnNavigatedTo(NavigationEventArgs e) { string stringId = ""; int id = 0; if (NavigationContext.QueryString.TryGetValue("id", out stringId)) { id = int.Parse(stringId); } // hier gegevens van ploeg met Id ophalen}

– Data.cs:

public Ploeg GetPloegById(int id) { // zoek in de list de eerste ploeg waarvoor de Id gelijk is aan id Ploeg resultaat = ploegen.First( delegate(Ploeg ploeg) { return ploeg.Id == id; } );return resultaat;}

– DetailPage.xaml:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> … <Image Grid.Row="0" Grid.Column="0" x:Name="imageLogo" Width="125" HorizontalAlignment="Left" VerticalAlignment="Top" Source="{Binding Logo}" /> …</Grid>

protected override void OnNavigatedTo(…) { … // hier gegevens van ploeg met Id ophalen ContentPanel.DataContext = ((App)(App.Current)).data.GetPloegById(id);}

<StackPanel Grid.Row="0" Grid.Column="1"> <TextBlock x:Name="textBlockNaam" FontSize="40" HorizontalAlignment="Center" TextWrapping="Wrap" Text="{Binding Naam}" Margin="0,10,0,0" /> <TextBlock x:Name="textBlockLand" FontSize="25" HorizontalAlignment="Center" TextWrapping="Wrap" Text="{Binding Land}" Margin="0,15,0,0" />

Page 35: KHK 2Ti C# Samenvatting

<Image x:Name="imageShirt" Width="150" HorizontalAlignment="Center" VerticalAlignment="Top" Source="{Binding Shirt}" Margin="0,10,0,0" /></StackPanel>

Page 36: KHK 2Ti C# Samenvatting

LINQ TO XML

– XML Data:– I.p.v. een relationele database gebruiken we een xml-document om onze gegevens te bewaren– Bewaren van dat XML-document in:

– Application File (.xap) → Les 14– Het XMM-bestand wordt dan mee geïnstalleerd als resource bij de installatie

v/d App (alleen lezen van info)– Isolated Storage → Les 15

– Geen rechtstreekse toegang tot mappen, wel voor elke App wat ruimte gereserveerd om te lezen/schrijven (ook schrijven van info)

– Internet → Les 16– XML-data ophalen/wegschrijven via het Internet (OData, webservices, RSS,

Twitter, …)– Gegevens lezen uit/schrijven in zo'n xml-document kan heel eenvoudig met LINQ to XML

– Voorbeeld MobileGuide:– Open solution MobileGuide.sln– Toestellen.xml: Build Action = Content

= bestand mee opnemen in application file (.xap) = mee installeren

– Toestellen.xml (XML for dummies):

<?xml version="1.0" encoding="utf-8" ?> elk xml-document begint hiermee<toestellen> root-element, verplicht, al de rest zit hier in <merken> keuze van alle andere elementen (begin- plus eindtag) is vrij <merk>Nokia</merk> <merk>HTC</merk> <merk>Apple</merk> <merk>Samsung</merk> enige beperking: mooi afsluiten v/d tags </merken> <toestel id="65" merk="HTC"> sommige elementen hebben attributen <naam>HTC Incredible S</naam> <url>http://www.mobileguide.be/nl/index.cfm/…</url> <score>2</score> <prijs>520</prijs> <kort>HTC beschikt over een grote hoeveelheid aan …</kort> </toestel> <toestel id="102" merk="Nokia"> …</toestellen>

– LINQ to XML:

using System.Xml.Linq;

private void LeesMerken() { XDocument xmlDoc = XDocument.Load("Toestellen.xml");

var merken = from element in xmlDoc.Root.Element("merken").Descendants("merk") select element.Value;

listBoxMerk.ItemsSource = merken;}

// Element = 1 niveau lager// Descendants = x niveaus lager

// <toestellen>// <merken>// <merk>Nokia</merk>// <merk>HTC</merk>// <merk>Apple</merk>// <merk>Samsung</merk>// </merken>// …// <toestellen>

– RadioButtonListBox:

<ListBox x:Name="listBoxMerk" Height="230"> <ListBox.ItemTemplate> <DataTemplate> <RadioButton x:Name="radioButtonMerk" Content="{Binding}" GroupName="Merk" Checked="RadioButton_Checked" Margin="20,-20,0,0" /> </DataTemplate>

Page 37: KHK 2Ti C# Samenvatting

</ListBox.ItemTemplate></ListBox>

private void RadioButton_Checked(object sender, RoutedEventArgs e) { string merk = ((RadioButton)sender).Content.ToString(); LeesToestellen(merk);}

– Class Toestel:

public class Toestel { int id; string naam; string merk; …

public Toestel(int id, string naam, string merk, string url, int score, double prijs, string kort) { Id = id; Naam = naam; Merk = merk; Url = url; Score = score; Prijs = prijs; Kort = kort; } …}

– LINQ to XML:

private void LeesToestellen(string merk) { XDocument xmlDoc = XDocument.Load("Toestellen.xml");

var toestellen = from element in xmlDoc.Descendants("toestel") where element.Attribute("merk").Value == merk select new Toestel( int.Parse(element.Attribute("id").Value), element.Element("naam").Value, element.Attribute("merk").Value, element.Element("url").Value, int.Parse(element.Element("score").Value), int.Parse(element.Element("prijs").Value), element.Element("kort").Value );

listBoxToestel.ItemsSource = toestellen;}

// <toestellen>// …// <toestel id="65" merk="HTC">// <naam>HTC Incredible S</naam>// <url>http://www.mobileguide.be/nl/index.cfm/…</url>// <score>2</score>// <prijs>520</prijs>// <kort>HTC beschikt over een grote hoeveelheid aan …</kort>// </toestel>// …// <toestellen>

– ListBox:

<ListBox x:Name="listBoxToestel" selectionChanged="listBoxToestel_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Vertical"> <TextBlock FontSize="30" Text="{Binding Naam}" TextWrapping="Wrap" Margin="0,10,0,0" /> <TextBlock FontSize="20" Text="{Binding Kort}" TextWrapping="Wrap" Margin="10,0,0,0" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate></ListBox>

– LINQ to XML:

Page 38: KHK 2Ti C# Samenvatting

private void LeesToestel(int id) { XDocument xmlDoc = XDocument.Load("Toestellen.xml");

Toestel toestel = (from element in xmlDoc.Descendants("toestel") where element.Attribute("id").Value == id.ToString() select new Toestel(int.Parse(element.Attribute("id").Value), …)).Single<Toestel>();

ContentPanel.DataContext = toestel;}

// <toestel id="65" merk="HTC">// …// </toestel>

Page 39: KHK 2Ti C# Samenvatting

ISOLATED STORAGE

– Isolated Storage:– Er is GEEN toegang vanuit de App naar het bestandssysteem van de smartphone

– Gegevens van een App kunnen niet in om het even welke map op de smartphone bewaard worden

– Isolated storage is een privé bestandssysteem dat wordt beheerd door het .NET Framework– Voor elke toepassing geïsoleerde schijfruimte waarin informatie kan bewaard worden

– Voorbeeld:– Open solution Storage.sln

– Isolated Storage ophalen:

// LINQ to XMLusing System.Xml.Linq;// Isolated Storageusing System.IO.IsolatedStorage;

// altijd eerst Isolated Storage voor deze App ophalenIsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication();

– Inhoud van Isolated Storage:

List<String> files;

private void ReadFiles() { IsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication();

// welke bestanden in Isolated Storage? files = applicationStorage.GetFileNames().ToList<String>();

if (files.Count() == 0) { files.Add("Geen bestanden gevonden…"); }

listboxFiles.ItemsSource = null; listboxFiles.ItemsSource = files;}

– XML-document maken in Isolated Storage:

private void buttonCreate_Click(object sender, EventArgs e) { Random random = new Random();

// vier willekeurige namen uit 10 namen kiezen List<string> namen = new List<string>() { "Jef", "Piet", "Ann", "Sofie", "Bert", "Leen", "Trui", "Els", "Bart", "Sien" }; namen = (namen.OrderBy(x => random.Next()).Take(4)).ToList<string>();

// willekeurige bestandsnaam string filename = random.Next(10000, 90000).ToString() + ".xml";

// eerst Isolated Storage voor deze App ophalen IsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication();

// dan een nieuwe file in ApplicationStorage creëren IsolatedStorageFileStream fileStream = applicationStorage.OpenFile(filename, System.IO.FileMode.Create, System.IO.FileAccess.Write);

// LINQ to XML om xml-document te creëren XDocument xmlDocument = new Xdocument(new XDeclaration("1.0", "utf-8", "yes"), new Xelement("personen", from naam in namen select new Xelement("persoon", new XElement("naam", naam))));

// <?xml version="1.0" encoding="utf-8" ?> // <personen> // <persoon> // <naam>Ann</naam> // </persoon> // … // </personen>

// XML bewaren in filestream xmlDocument.Save(fileStream); fileStream.Close();

Page 40: KHK 2Ti C# Samenvatting

ReadFiles();}

– Bestand verwijderen uit Isolated Storage:

private void buttonDelete_Click(object sender, EventArgs e) { if (listboxFiles.SelectedIndex != -1) { string filename = listboxFiles.SelectedItem.ToString();

IsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication(); applicationStorage.DeleteFile(filename);

ReadFiles(); }}

– Bestand lezen uit Isolated Storage:

private void buttonShow_Click(object sender, EventArgs e) { if (listboxFiles.SelectedIndex != -1) { string filename = listboxFiles.SelectedItem.ToString();

IsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication(); IsolatedStorageFileStream fileStream = applicationStorage.OpenFile(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read);

// streamreader om filestream in te lezen // data is niet gestructureerd -> beter met LINQ to XML StreamReader reader = new StreamReader(fileStream); MessageBox.Show(reader.ReadToEnd().ToString()); reader.Close(); fileStream.Close(); }}

– Lezen op een gestructureerde manier:

// uiteraard beter met LINQ to XML i.p.v. StreamReaderIsolatedStorageFile applicationStorage = IsolatedStorageFile.GetUserStoreForApplication();IsolatedStorageFileStream fileStream = applicationStorage.OpenFile(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read);

XDocument xmlDocument = XDocument.Load(fileStream);

// vanaf hier LINQ to XML (zie les 14)…

Page 41: KHK 2Ti C# Samenvatting

USING A REST SERVICE

– Wat is ReST?– Representational State Transfer– ReST is een lightweight alternatief voor RPC (Remote Procedure Calls) en Webservices (SOAP)

dat toelaat om toepassingen op verschillende machines met mekaar te laten communiceren– Platform-independent (Mac, Unix, Windows Phone, …)– Language-independent (C# can talk to Java, Java can talk to PHP, …)– ReST calls gebruiken HTTP en zijn stateless– Voor beveiliging kunnen user/password tokens gebruikt worden– Voor encryptie gebruik je HTTPS

– HTTP Methods:– In ReST gebruik je de vier HTTP methods (PUT, GET, POST, DELETE) voor de vier CRUD

(Create/Read/Update/Delete) operaties:– GET: to fetch information about an existing resource– POST: to update an existing resource with information– PUT: to create a new resource– DELETE: to delete a resource

– ReST Responses:– XML: Gemakkelijk uit te breiden (extra velden beïnvloeden bestaande applicaties niet)

<users> <user><name>Jef</name><tel>014/563254</tel></user> <user><name>Piet</name><tel>014/326954</tel></user></users>

– JSON: Eenvoudig te parsen door JavaScript clients (en ook door C#, java, …)

[{"name":"Jef","tel":"014/563254"}, {"name":"Piet","tel":"014/326954"}]

– CSV: Compact

Jef,014/563254Piet,014/326954

– Voorbeeld:– Open solution ReST

– GET (fetch):

<ListBox x:Name="BierListBox" Margin="0,0,-12,0"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Image Source="{Binding Afbeelding}" Height="150" … /> <TextBlock Text="{Binding Naam}" FontSize="30" … /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate></ListBox>

// public class Biertje {// int id;// string naam;// string brouwerij;// decimal graden;// string uitleg;// string afbeelding;// string soort;// decimal prijs;// …// }

– GET:

const string URL = "http://…/CI/beershop.php/api/";

private void GetBiertjes() { string resource = "biertjes";

// WebClient-object om ReST-call te doen WebClient webclient = new WebClient();

// Asynchroon, dus functie opgeven die moet uitgevoerd worden als resultaat binnen is

Page 42: KHK 2Ti C# Samenvatting

webclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webclient_DownloadStringCompleted);

// Doe de call (DownloadStringAsync) naar de juiste URL en resource // Method is altijd GET bij DownloadStringAsync webclient.DownloadStringAsync(new Uri(URL + resource));}

private void webclient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { return; }

// XML-resultaat parsen XDocument xmlEntries = XDocument.Parse(e.Result);

// Biertjes eruit halen var biertjes = from itemElement in xmlEntries.Descendants("item") select new Biertje(int.Parse(itemElement.Element("id").Value), … , decimal.Parse(itemElement.Element("prijs").Value));

// Tonen via databinding in BierListBox BierListBox.ItemsSource = biertjes;}

– PUT (create):

private void PutTweet(string account, string message) { string resource = "tweet";

// WebClient-object om ReST-call te doen WebClient webclient = new WebClient();

// Parameters-string met stringbuilder maken StringBuilder postData = new StringBuilder();

// parameternaam=parameterwaarde postData.AppendFormat("{0}={1}", "account", HttpUtility.UrlEncode(account));

// &tweedeparameter=tweedewaarde postData.AppendFormat("&{0}={1}", "message", HttpUtility.UrlEncode(message));

– PUT:

// Juiste HTTP headers instellen webclient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; webclient.Headers[HttpRequestHeader.ContentLength] = postData.Length.ToString();

// Asynchroon, dus functie opgeven die moet uitgevoerd worden als resultaat binnen is webclient.UploadStringCompleted += new UploadStringCompletedEventHandler(webclient_UploadStringCompleted);

// Doe de call (UploadStringAsync) naar de juiste URL en resource // Method is PUT (resource creëren) en parameters webclient.UploadStringAsync(new Uri(URL + resource), "PUT", postData.ToString()); }

Page 43: KHK 2Ti C# Samenvatting

CREATING A REST SERVER

– Creating a ReSTfull API in PHP:– Download de codeigniter-restserver code van GitHub:

https://github.com/philsturgeon/codeigniter-restserver– De zip bevat een volledig CodeIgniter project– Wij gebruiken eruit alleen config/rest.php, libraries/REST_Controller.php en Format.php– Met deze drie bestanden maken we een ReSTfull API om tweets te versturen (we bouwen deze

functionaliteit in een werkende CodeIgniter-applicatie, bijv. Beershop)

– Tweet ReSTfull API:– Voeg toe aan je CodeIgniter project:

– Uit restserver.zip:– config/rest.php– libraries/REST_Controller.php– libraries/Format.php

– JSON:– helpers/JSON.php– helpers/json_helper.php

– Zelf te schrijven:– controllers/restapi.php– controllers/tweet.php– models/tweet_model.php– views/tweet

– Uittesten:– Eerst tabel creëren: tweet.sql– …/beershop.php/tweet/menu– GET:

– …/beershop.php/restapi/tweets– …/beershop.php/restapi/tweets/top/5– …/beershop.php/restapi/tweet/id/4– …/beershop.php/restapi/tweet/id/3/format/xml– …/beershop.php/restapi/tweets/top/5/format/json

– Zelf te schrijven?– Bekijk de code van:

– models/tweet_model.php = gewoon CodeIgniter model– controllers/tweet.php = gewone CodeIgniter controller voor menu, lijst en schrappen– views/tweet = het menu en de lijst van tweets

– Blijft over:– controllers/restapi.php = dit is de ReST API, het enige wat je moet programmeren

– restapi.php:

require (APPATH . '/libraries/REST_Controller.php');

class Restapi extends REST_Controller { public function __construct() { parent::__construct();

$this->load->helper('json'); $this->load->helper('url'); }

function tweets_get() { } function tweet_get() { } function tweet_put() { } function tweet_post() { } function tweet_delete() { }}

– TweetApp– Vul zelf in de XAML-code hardgecodeerd een account en message in

– Insert:– Start de applicatie en klik op INSERT– Controleer de ID op de server m.b.v. de webtoepassing!

private void ButtonInsert_Click(object sender, RoutedEventArgs e) { Rest rest = new Rest(); rest.InsertTweet(TextBoxAccount.Text, TextBoxMessage.Text, Insert_Klaar);}// Insert_Klaar is de methode die wordt uitgevoerd als het resultaat van de server terugkomt

Page 44: KHK 2Ti C# Samenvatting

// (later meer)

– Rest klasse:

public delegate void UploadStringCompleted(object sender, UploadStringCompletedEventArgs e);public delegate void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e);

public class Rest { const string URL = "http://eduphp.khk.be/~s5061854/CI/beershop.php/restapi/";}

public void InsertTweet(string account, string message, UploadStringCompleted Upload_Klaar) { string resource = "tweet";

WebClient webclient = new WebClient();

StringBuilder postData = new StringBuilder(); postData.AppendFormat("{0}={1}", "account", HttpUtility.UrlEncode(account)); postData.AppendFormat("&{0}={1}", "message", HttpUtility.UrlEncode(message));

webclient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; webclient.Headers[HttpRequestHeader.ContentLength] = postData.Length.ToString();

webclient.UploadStringCompleted += new UploadStringCompletedEventHandler(Upload_Klaar); webclient.UploadStringAsync(new Uri(URL + resource), "PUT", postData.ToString());}

– restapi.php:

function tweet_put() { $tweet->account = $this->put('account'); $tweet->message = $this->put('message');

$this->load->model('tweet_model'); $id = $this->tweet_model->insert($tweet);

$this->response(array('status' => 'success', 'id' => $id), 200);}

// 200 is de HTTP Response code voor OK

// <?xml version="1.0" encoding="utf-8">// <xml><status>success</status><id>16</id></xml>

– callback functie:

void Insert_Klaar(object sender, UploadStringCompletedEventArgs e) { XDocument xmlDoc = XDocument.Parse(e.Result); String id = (from item in xmlDoc.Root.Descendants("id") select item.Value).Single<String>(); TextBoxID.Text = id;}

// <?xml version="1.0" encoding="utf-8">// <xml><status>success</status><id>16</id></xml>

– Update – Klik op UPDATE – Controleer de aanpassing op de server m.b.v. de webtoepassing!

private void ButtonUpdate_Click(object sender, RoutedEventArgs e) { Rest rest = new Rest(); rest.UpdateTweet(int.Parse(TextBoxID.Text), TextBoxAccount.Text.ToUpper(), TextBoxMessage.Text.ToUpper(), Update_Klaar);}

void Update_Klaar(object sender, UploadStringCompletedEventArgs e) { MessageBox.Show(e.Result);}

// <?xml version="1.0" encoding="utf-8">// <xml><status>success</status><id>16</id></xml>

– Rest klasse:

public void UpdateTweet(int id, string account, string message, UploadStringCompleted Upload_Klaar) { string resource = "tweet";

Page 45: KHK 2Ti C# Samenvatting

WebClient webclient = new WebClient();

StringBuilder postData = new StringBuilder(); postData.AppendFormat("{0}={1}", "id", HttpUtility.UrlEncode(id.ToString())); postData.AppendFormat("&{0}={1}", "account", HttpUtility.UrlEncode(account)); postData.AppendFormat("&{0}={1}", "message", HttpUtility.UrlEncode(message));

webclient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded"; webclient.Headers[HttpRequestHeader.ContentLength] = postData.Length.ToString();

webclient.UploadStringCompleted += new UploadStringCompletedEventHandler(Upload_Klaar); webclient.UploadStringAsync(new Uri(URL + resource), "POST", postData.ToString());}

– restapi.php:

function tweet_post() { $tweet->id = $this->post('id'); $tweet->account = $this->post('account'); $tweet->message = $this->post('message');

$id = $this->post('id');

$this->load->model('tweet_model'); $id = $this->tweet_model->update($tweet);

$this->response(array('status' => 'success', 'id' => $id), 200);}

// 200 is de HTTP Response code voor OK

// <?xml version="1.0" encoding="utf-8">// <xml><status>success</status><id>16</id></xml>

– List:

private void ButtonList_Click(object sender, RoutedEventArgs e) { Rest rest = new Rest(); rest.GetTweets(Read_Klaar);}

// Read_Klaar is de methode die wordt uitgevoerd als het resultaat van de server terugkomt// (later meer)

– Rest klasse:

public void GetTweets(DownloadStringCompleted Download_Klaar) { string resource = "tweets";

WebClient webclient = new WebClient();

webclient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Download_Klaar); webclient.DownloadStringAsync(new Uri(URL + resource));}

– restapi.php:

function tweets_get() { $this->load->model('tweet_model');

if (! $this->get('top')) { $tweets = $this->tweet_model->getAll(); } else { $top = $this->get('top'); $tweets = $this->tweet_model->getTop($top); }

if ($tweets) { $this->response($tweets, 200); } else { $this->response(array('error' => 'Geen tweets gevonden!'), 404); }}

Page 46: KHK 2Ti C# Samenvatting

// 200 is de HTTP Response code voor OK// 404 is de HTTP Response code voor Not Found

// <?xml version="1.0" encoding="utf-8">// <xml>// <item><id>17</id><account>s5061854</account><message>BLA BLA BLA…</message></item>// <item><id>16</id><account>s5061854</account><message>BLA BLA BLA…</message></item>// </xml>

– callback functie:

void Read_Klaar(object sender, DownloadStringCompletedEventArgs e) { XDocument xmlDoc = XDocument.Parse(e.Result); var boodschappen = (from item in xmlDoc.Root.Descendants("message") select item.Value); ListBoxTweets.ItemsSource = boodschappen;}

// <?xml version="1.0" encoding="utf-8">// <xml><status>success</status><id>16</id></xml>

Page 47: KHK 2Ti C# Samenvatting

UNIT TESTING

– Unit Testing:– Unittesten: De Class Builders testen of kleine delen van de software het gewenste resultaat

geven– Gebruikerstesten: Reageert de software zoals de eindgebruiker het verwacht– Unit Tests (methodes) worden bewaard in een Test Class (klasse)– Dus twee Class Libraries:

– De te testen klassen– Unit test klassen

– We schrijven de unit tests vóór de te testen code – Alleen publieke methodes en properties worden getest

– Project:– Visual Studio 2010 > File > New Project > Visual C# > Class Library > Bank

Create directory for solution wel aanvinken– Bank is een Class Library (bevat klassen die gebruikt worden in een banktoepassing)– De eerste klasse in die library is Rekening

– Klasse Rekening:– Hernoem Class1.cs naar Rekening.cs

namespace Bank { public class Rekening { private float saldo = 0f;

public void Stort(float bedrag) { saldo += bedrag; }

public void HaalAf(float bedrag) { saldo -= bedrag; }

public void SchrijfOver(Rekening begunstigde, float bedrag) { // eerst de test schrijven en daarna pas de methode }

public float Saldo { get { return saldo; } } }}

– Testproject:– Solution > Add > New Project > Visual C# > Test > TestProject > BankTest– BankTest is een Class Library (bevat testklassen die we gebruiken om de klassen van de Class

Library Bank uit te testen)– Referentie van BankTest naar Bank:

– BankTest > Rechts > Add Reference > Projects > Bank > OK

– Klasse UnitTestRekening:– Hernoem UnitTest1.cs naar UnitTestRekening.cs

using Bank;

namespace BankTest { [TestClass] public class UnitTestRekening { [TestMethod] public void SchrijfOverTest() { } }}

– Postcondities:– Alles wat na het uitvoeren van de methode (use case) voldaan moet zijn– Doet de methode wat ze moet doen? Geeft de methode het juiste resultaat?– Met behulp van: Assert.AreEqual(expected_value, actual_value);– De test slaagt als de expected_value gelijk is aan de actual_value– Bijvoorbeeld: na overschrijving van een bedrag zijn zowel het saldo van de opdrachtgever als

de begunstigde aangepast

Page 48: KHK 2Ti C# Samenvatting

– SchrijfOverTest (UnitTestRekening.cs):

[TestMethod]public void SchrijfOverTest() { // basisbedragen storten Rekening opdrachtgever = new Rekening(); opdrachtgever.Stort(200f); Rekening begunstigde = new Rekening(); begunstigde.Stort(150f);

// opdrachtgever stort 100€ naar begunstigde opdrachtgever.SchrijfOver(begunstigde, 100f);

// controle Assert.AreEqual(250f, begunstigde.Saldo); Assert.AreEqual(100f, opdrachtgever.Saldo);}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Failed, Assert.AreEqual failed. Expected: <250>. Actual: <150>.

– SchrijfOverTest (Rekening.cs):

public void SchrijfOver(Rekening begunstigde, float bedrag) { begunstigde.Stort(bedrag); HaalAf(bedrag);}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Passed

– Precondities:– Voorwaarden die moeten voldaan zijn vóór het oproepen van een methode (vind je in use case)– We gaan na of de methoden de juiste uitzonderingen (exceptions) throwen als aan die

precondities niet voldaan is– Bijvoorbeeld: een bedrag mag alleen overgeschreven worden als het saldo toereikend is

– Saldo ontoereikend (Rekening.cs):

namespace Bank { public class SaldoOntoereikendException : ApplicationException { }

public class Rekening { private float saldo = 0f; private float minimumSaldo = 10f; …

public float MinimumSaldo { get { return minimumSaldo; } } }}

– Saldo ontoereikend (UnitTestRekening.cs):

[TestMethod][ExpectedException(typeof(SaldoOntoereikendException))]

public void SchrijfOverMetOntoereikendSaldoTest() { // de test slaagt als tijdens het uitvoeren ervan de exceptie optreedt Rekening opdrachtgever = new Rekening(); opdrachtgever.Stort(200f); Rekening begunstigde = new Rekening(); begunstigde.Stort(150f);

opdrachtgever.SchrijfOver(begunstigde, 300f);}

– Saldo ontoereikend (Rekening.cs)

public void SchrijfOver(Rekening begunstigde, float bedrag) { // eerst de test schrijven en daarna pas de methode

Page 49: KHK 2Ti C# Samenvatting

begunstigde.Stort(bedrag); // saldo controleren if (Saldo - bedrag < MinimumSaldo) { throw new SaldoOntoereikendException(); }

HaalAf(bedrag);}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Passed SchrijfOver, Passed SchrijfOverMetOntoereikendSaldoTest

– Atomicity (ondeelbaarheid)– Ofwel worden alle taken uitgevoerd ofwel geen enkele: er mag geen geld verdwijnen of bijkomen– We schrijven een test om te kijken of onze methode ondeelbaar is

– Atomicity (UnitTestRekening.cs):

[TestMethod]public void OntoereikendSaldoOndeelbaarTest() { // basisbedragen storten Rekening opdrachtgever = new Rekening(); opdrachtgever.Stort(200f);

Rekening begunstigde = new Rekening(); begunstigde.Stort(150f); // controleren of saldo toereikend is try { opdrachtgever.SchrijfOver(begunstigde, 300f); } catch (SaldoOntoereikendException e) { } Assert.AreEqual(200f, opdrachtgever.Saldo); Assert.AreEqual(150f, begunstigde.Saldo);}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Passed SchrijfOver, Passed SchrijfOverMetOntoereikendSaldoTest,

Failed OntoereikendSaldoOndeelbaarTest

– Atomicity (Rekening.cs):

public void SchrijfOver(Rekening begunstigde, float bedrag) { // saldo controleren if (Saldo - bedrag < MinimumSaldo) { throw new SaldoOntoereikendException(); }

// transactie uitvoeren begunstigde.Stort(bedrag); HaalAf(bedrag);}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Passed SchrijfOver, Passed SchrijfOverMetOntoereikendSaldoTest,

Passed OntoereikendSaldoOndeelbaarTest

– Refactoring:– Programmacode van de Rekening-klasse kan nu herschreven worden: de unit-tests gaan na of de

code nog altijd werkt– Hier de testcode herschrijven: vermijden dat telkens opnieuw de rekening van opdrachtgever en

begunstigde moet gecreëerd worden

– Refactoring (UnitTestRekening.cs) -> PAS AAN:

[TestClass]public class UnitTestRekening { Rekening opdrachtgever; Rekening begunstigde;

[TestInitialize()]

Page 50: KHK 2Ti C# Samenvatting

public void StortStartSaldo() { // TestInitialize wordt voor het uitvoeren van elke test uitgevoerd // basissaldo’s storten opdrachtgever = new Rekening(); opdrachtgever.Stort(200f);

begunstigde = new Rekening(); begunstigde.Stort(150f); }

[TestMethod] public void SchrijfOverTest() { // opdrachtgever stort 100€ naar begunstigde opdrachtgever.SchrijfOver(begunstigde, 100f);

// controle Assert.AreEqual(250f, begunstigde.Saldo); Assert.AreEqual(100f, opdrachtgever.Saldo); } [TestMethod] [ExpectedException(typeof(SaldoOntoereikendException))]

public void SchrijfOverMetOntoereikendSaldoTest() { // de test slaagt als tijdens het uitvoeren ervan de exceptie optreedt opdrachtgever.SchrijfOver(begunstigde, 300f); }

[TestMethod] public void OntoereikendSaldoOndeelbaarTest() { // controleren of saldo toereikend is try { opdrachtgever.SchrijfOver(begunstigde, 300f); } catch (SaldoOntoereikendException e) { } Assert.AreEqual(200f, opdrachtgever.Saldo); Assert.AreEqual(150f, begunstigde.Saldo); }}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– Test Results: Passed SchrijfOver, Passed SchrijfOverMetOntoereikendSaldoTest,

Passed OntoereikendSaldoOndeelbaarTest

– Unit Testen:– Unit testen zouden uitgevoerd moeten worden tijdens elke build

– Dus niet alleen compileren maar ook de testen uitvoeren op eigen klasse– Smoke testing

– Regressietesten– Hiermee wordt nagegaan of na het verbeteren van een bug de ongewijzigde bestaande

code nog doet wat ze moet doen– Kan dagelijks 's nachts gebeuren– Full testing

Page 51: KHK 2Ti C# Samenvatting

TEST DRIVEN DEVELOPMENT

– Test Driven Development (TDD):– Schrijf eerst de test, daarna de code– Refactor: kleine aanpassingen aan de code die de leesbaarheid verhogen (bv. dubbele code

verwijderen)– Gebruikt bij Extreme Programming– Opgelet: de volgorde waarin de tests worden uitgevoerd, mag geen invloed hebben op het

resultaat (pass vs. fail)

– Middle Tier:– Programmeren en testen van de Account klassen (Middle Tier: DAL en BLL) van een applicatie op

een ± Test Driven manier: alle testen zijn gegeven, we zorgen ervoor dat ze allen slagen– Voer CreateAccount.sql uit met de Sql Server Management Studio– Open Solution MiddleTier

– Twee projecten: – MiddleTier (Class Library): BLL en DAL– MiddleTierTest (Test Project): Unit Testen

– Met de Server Explorer connecteren met je persoonlijke SQL Server database

– Account DAL:– DAL > Rechts > Add > New Item > DataSet > DBMiddleTier.xsd– Versleep Account uit Server Explorer naar DBMiddleTier.xsd– Hernoem GetData in GetAllAcounts (alleen tweede vinkje aanvinken)– Save

– Account BBL:

using MiddleTier.DAL;using MiddleTier.DAL.DBMiddleTierTableAdapters;

namespace MiddleTier.BLL { public class BLAccount { AccountTableAdapter adapter;

public BLAccount() { adapter = new AccountTableAdapter(); }

// de hoofding van de methodes voorzien in de uit te testen klasse

public int InsertAccount(DBMiddleTier.AccountRow account) { return -1; }

public DBMiddleTier.AccountRow GetAccount(string login) { return new DBMiddleTier.AccountDataTable().NewAccountRow();

}

public void DeleteAccount(string login) { }

public void UpdatePassword(string login, string oldPassword, string newPassword) { } }}

– Testproject:– Referentie van MiddleTierTest naar MiddleTier:

– MiddleTierTest > Rechts > Add Reference > Projects > MiddleTier > OK– App.Config kopiëren van MiddleTier naar MiddleTierTest– MiddleTierTest > Rechts > Add Reference > .NET

> System.Data > System.Data.DataSetExtentions > System.Xml

– Klasse UnitTestAccount:

using MiddleTier.DAL;using MiddleTier.BLL;

namespace MiddleTierTest { [TestClass] public class UnitTestAccount { }}

Page 52: KHK 2Ti C# Samenvatting

– ClassInitialize (UnitTestAccount.cs):

// uitgevoerd vóór start eerste test[ClassInitialize()] public static void Initialize(TestContext testContext) { // testdata in db stoppen DBMiddleTier.AccountRow account = new DBMiddleTier.AccountDataTable().NewAccountRow(); BLAccount blAccount = new BLAccount();

account.Login = "admin"; account.Password = "p@ssw0rd"; account.Email = "[email protected]"; blAccount.InsertAccount(account);

account.Login = "user"; account.Password = "p@ssw0rd"; account.Email = "[email protected]"; blAccount.InsertAccount(account);

account.Login = "customer"; account.Password = "p@ssw0rd"; account.Email = "[email protected]"; blAccount.InsertAccount(account);}

– ClassCleanup (UnitTestAccount.cs):

// uitgevoerd na laatste test[ClassCleanup()]public static void Cleanup() { // testdata terug verwijderen BLAccount blAccount = new BLAccount(); blAccount.DeleteAccount("admin"); blAccount.DeleteAccount("user"); blAccount.DeleteAccount("customer");}

– GetAccountTest (UnitTestAccount.cs):

// haalt GetAccount inderdaad de juiste account-gegevens op[TestMethod]public void GetAccountTest() { BLAccount blAccount = new BLAccount(); DBMiddleTier.AccountRow account = blAccount.GetAccount("user");

Assert.AreEqual<string>("user", account.Login); Assert.AreEqual<string>("p@ssw0rd", account.Password); Assert.AreEqual<string>("[email protected]", account.Email);}

– GetAccountNotFoundTest (UnitTestAccount.cs):

// treedt bij een niet-bestaande account een AccountNotFoundException op[TestMethod][ExpectedException(typeof(AccountNotFoundException))]public void GetAccountNotFoundTest() { BLAccount blAccount = new BLAccount(); DBMiddleTier.AccountRow account = blAccount.GetAccount("joeser");}

– GetAccountNotFoundTest (BLAccount.cs):

public class AccountNotFoundException : ApplicationException { }

– LoginAlreadyExistsTest (UitTestAccount.cs):

// bij toevoegen van een reeds bestaande login een LoginExistsException?[TestMethod][ExpectedException(typeof(LoginExistsException))]public void LoginAlreadyExistsTest() { DBMiddleTier.AccountDataTable accounts = new DBMiddleTier.AccountDataTable(); DBMiddleTier.AccountRow account = accounts.NewAccountRow();

BLAccount blAccount = new BLAccount();

account.Login = "user";

Page 53: KHK 2Ti C# Samenvatting

account.Password = "p@ssw0rd"; account.Email = "[email protected]"; blAccount.InsertAccount(account);}

– LoginAlreadyExistsTest (BLAccount.cs):

public class LoginExistsException : ApplicationException {}

– UpdatePasswordTest (UnitTestAccount.cs):– A.h.v. attribuut TestProperty kan je in bepaalde tools aangeven welke testen je wanneer wil

uitvoeren

// wordt door UpdatePassword het wachtwoord aangepast?[TestMethod][TestProperty("TestCategory", "Everyday")]public void UpdatePasswordTest() { BLAccount blAccount = new BLAccount(); blAccount.UpdatePassword("admin", "p@ssw0rd", "newpassword");

DBMiddleTier.AccountRow account = blAccount.GetAccount("admin"); Assert.AreEqual<string>("newpassword", account.Password);}

– UpdatePasswordFailTest (UnitTestAccount.cs):

// blijft het wachtwoord ongewijzigd bij opgeven van een foutief oldPassword?[TestMethod]public void UpdatePasswordFailTest() { BLAccount blAccount = new BLAccount(); blAccount.UpdatePassword("customer", "password", "newpassword");

DBMiddleTier.AccountRow account = blAccount.GetAccount("customer"); Assert.AreEqual<string>("p@ssw0rd", account.Password);}

[TestMethod, Ignore()]public void SkipThisOne() {}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– 5 x Fail -> Code schrijven zodat de tests Passed

– Account DAL (VOEG TOE):– DeleteAccount(@Login):

DELETE FROM Account WHERE Login=@Login

– GetAccount(@Login): SELECT Id, Login, Password, Email FROM Account WHERE Login=@Login

– InsertAccount(@Login, @Password, @Email):INSERT INTO Account(Login, Password, Email) VALUES(@Login, @Password, @Email)SELECT SCOPE_IDENTITY()

– UpdateAccount(@Login, @Password, @Email, @Id):UPDATE Account SET Login=@Login, Password=@Password, Email=@Email WHERE Id=@Id

– Account BBL (PAS AAN):

using MiddleTier.DAL;using MiddleTier.DAL.DBMiddleTierTableAdapters;

namespace MiddleTier.BLL { public class BLAccount { AccountTableAdapter adapter;

public class AccountNotFoundException : ApplicationException { }

public class LoginExistsException : ApplicationException { }

public BLAccount() { adapter = new AccountTableAdapter();

Page 54: KHK 2Ti C# Samenvatting

}

// de hoofding van de methodes voorzien in de uit te testen klasse

public int InsertAccount(DBMiddleTier.AccountRow account) { DBMiddleTier.AccountDataTable accountTable = new DBMiddleTier.AccountDataTable(); accountTable = adapter.GetAccount(account.Login);

if (accountTable.Rows.Count != 0) { throw new LoginExistsException(); }

return Convert.ToInt32( adapter.InsertAccount(account.Login, account.Password, account.Email)); }

public DBMiddleTier.AccountRow GetAccount(string login) { DBMiddleTier.AccountDataTable accountTable = new DBMiddleTier.AccountDataTable(); accountTable = adapter.GetAccount(login);

if (accountTable.Rows.Count > 0) { return (DBMiddleTier.AccountRow)accountTable.Rows[0]; }

throw new AccountNotFoundException(); }

public void DeleteAccount(string login) { adapter.DeleteAccount(login); }

public void UpdatePassword(string login, string oldPassword, string newPassword) { DBMiddleTier.AccountRow account = GetAccount(login);

if (account.Password == oldPassword) { adapter.UpdateAccount(account.Login, newPassword, account.Email, account.Id); } } }}

– Tests uitvoeren:– Run All Tests in Solution (CTRL+ R, A)– 5 x Passed!

Page 55: KHK 2Ti C# Samenvatting

USER CONTROLS

– User Controls:– Besturingselementen die je zelf bouwt om te gebruiken in Windows-formulieren– Voorbeeld: een User Control die bestaat uit een tekstvak waarin alleen numerieke gegevens

kunnen ingevoerd worden

– Voorbeeld:– Visual Studio 2010 > New Project > Windows Forms Control Library > KHKFormControls– Hernoem UserControl1.cs in TextBoxNumeriek.cs– Na compilatie: KHKFormControls.dll met daarin één control TextBoxNumeriek – Voeg een tekstvak textBox toe aan de User Control

– Numeriek (TextBoxNumeriek.cs):– Property Numeriek: true = alleen numerieke waarden

private bool numeriek;

public bool Numeriek { get { return numeriek; } set { numeriek = value; }}

public TextBoxNumeriek() { InitializeComponent(); Numeriek = false; // default = false}

– KeyPress (TextBoxNumeriek.cs):– TextBox > Properties > Events > KeyPress > dubbelklikken

private void textBox_KeyPress(object sender, KeyPressEventArgs e) { if (Numeriek) { if (!Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar)) { // als geen cijfers en geen backspace e.Handled = true; } }}

– TestProject:– Solution > Rechts > Add > New Project > Windows Form Application > TestFormControls– Hernoem Form1.cs naar FormControls.cs– TestFormControls > Rechts > Set as StartUp Project– Referentie leggen van testproject naar control library:

– TestFormControls > Rechts > Add Reference– TextBoxNumeriek1– TextBoxNumeriek2: Numeriek = true– buttonBTW

– Een methode:– IsBTWNummer om te controleren of het tekstvak een geldig BTW nummer bevat– Voorbeeld: 449.386.647

– 4493866 / 97 = 46 328, rest van de deling = 50– verschil tussen 97 en rest van de deling: 97 - 50 = 47– 47 = laatste twee cijfers van het BTW nummer

– IsBTWNummer (TextBoxNumeriek.cs):

public bool IsBTWNummer() { int i;

if (! int.TryParse(textBox.Text, out i)) { return false; }

if (textBox.Text.Length != 9) { return false; }

if ((97 - (int.Parse(textBox.Text.Substring(0, 7)) % 97)) != int.Parse(textBox.Text.Substring(7, 2))) { return false; }

Page 56: KHK 2Ti C# Samenvatting

return true;}

– Uittesten (TestFormControls.cs):– Eerst Control opnieuw compileren (na elke toepassing):

KHKFormControls > Rechts > ReBuild

private void buttonBTW_Click(object sender, EventArgs e) { MessageBox.Show(textBoxNumeriek2.IsBTWNummer().ToString());}

– Een event (TextBoxNumeriek.cs):– Een button zal een event (Clicked) raisen telkens een gebruiker erop klikt– Onze control moet een event (WrongInput) raisen telkens een gebruiker verkeerde input invoert– Dat event wordt gedefinieerd binnen de User Control (door de makers van de User Control),

maar kan je geprogrammeerd worden buiten de User Control (door de gebruikers van de User Control)

– D.m.v. een delegate (een functiepointer)

namespace KHKFormControls { // WrongInputEventHandler is een functie met twee parameters: een object en eventargs public delegate void WrongInputEventHandler(Object sender, EventArgs e);

public partial class TextBoxNumeriek : UserControl { // WrongInput is zo'n WrongInputEventHandler functie public event WrongInputEventHandler WrongInput; …

private void textBox_KeyPress(object sender, KeyPressEventArgs e) { if (Numeriek) { if (!Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar)) { // als geen cijfers en geen backspace e.Handled = true;

// als de eventhandler er is, dan uitvoeren if (WrongInput != null) { WrongInput(this, e); } } } } … }}

– Uittesten (TestFormControls.cs):– Eerst Control opnieuw compileren (na elke toepassing):

KHKFormControls > Rechts > ReBuild

private void textBoxNumeriek2_WrongInput(object sender, EventArgs e) { MessageBox.Show("Alleen cijfers.");}

– Schikking van het tekstvak (TextBoxNumeriek.cs):

public TextBoxNumeriek() { InitializeComponent(); Numeriek = false; Schik(); // schikken bij creatie control}

private void TextBoxNumeriek_Resize(object sender, EventArgs e) { Schik(); // en bij grootte aanpassen}

private void Schik() { textBox.Location = new Point(0, 0); textBox.Width = this.Width; this.Height = textBox.Height;}

– In andere projecten:– Herstart Visual Studio > New Project > … > Toolbox > General > Rechts > Choose Item > Browse

> KHKFormControls.dll aanklikken (in bin\Debug)

Page 57: KHK 2Ti C# Samenvatting

GENERICS

– Stapel of Stack – Veel gebruikte datastructuur bij softwareontwikkeling– Datastructuur waarin een aantal elementen kunnen bewaard worden volgens het principe Last In

First Out (LIFO) ↔ Queue: First In First Out (FIFO)– Operaties:

– Push(element): element op de stapel leggen– Pop(): element van de stapel halen

– Stack 1ste versie: Stapel-klasse met een array (max aantal elementen op de stapel is 10 integers)

public class Stapel { int top = -1; int[] items = new int[10];

public void Push(int item) { top++; items[top] = item; } public int Pop() …}

– Stack 2de versie: Twee Stapel-klassen

public class IntStapel { int top = -1; int[] items = new int[10];

public void Push(int item) { top++; items[top] = item; }

public int Pop() …}

public class StringStapel { int top = -1; string[] items = new string[10];

public void Push(string item) { top++; items[top] = item; }

public string Pop() …}

– Stack 3de versie: Implementatie m.b.v. objecten (hierin kan je alles stoppen)

public class Stapel { int top = -1; object[] items = new object[10];

public void Push(object item) { top++; items[top] = item; }

public object Pop() …}

– Nadelen van de object-oplossing:– Performatie gaat omlaag omdat telkens gecast moet worden, aangezien het returntype van Pop

een object is.

Stapel stapel = new Stapel();stapel.Push(1);int number = (int)stapel.Pop();

– Geen compile-time type safety, invalid casts worden pas opgemerkt at runtime

Page 58: KHK 2Ti C# Samenvatting

Stapel stapel = new Stapel();stapel.Push("een");int number = (int)stapel.Pop();

– Oplossing:– Generics:

– Eén algoritme dat je kan gebruiken voor verschillende datatypes– Generieke klasse = geparameteriseerde klasse– Declareren van de generieke typeparameter tussen < en >– Geen dubbel werk, geen performatieproblemen en wel type-safe

– Bestaande generieke klasse List:

List<int> lijst = new List<int>();lijst.Add("een"); // compilatiefout

– Generieke Stapel-klasse:– Visual Studio 2010 > File > New Project > Visual C# > Class Library > Generics– Create directory for solution wel aanvinken (twee projecten in één solution)– Class1.cs hernoemen naar Stapel.cs

– Stapel<T>:

public class Stapel<T> { int size; T[] items;

public Stapel(int size) { this.size = size; items = new T[size]; }

public void Push(T item) { }

public T Pop() { return items[0]; }}

– TestProject:– Add New Project > Test > Test Project > GenericsTest– Referentie van GenericsTest naar Generics– UnitTest1.cs hernoemen naar UnitTestStapel.cs

– Klasse UnitTestStapel:

using Generics;

namespace GenericsTest { [TestClass] public class UnitTestStapel { …

[TestMethod] public void IntStapelTest() { Stapel<int> stapel = new Stapel<int>(10); stapel.Push(1); stapel.Push(5); Assert.AreEqual(5, stapel.Pop()); Assert.AreEqual(1, stapel.Pop()); } }}

– Run All Tests = FAIL

– Stapel<T>:

public class Stapel<T> { int top = -1; int size; T[] items;

Page 59: KHK 2Ti C# Samenvatting

public void Push(T item) { top++; items[top] = item; }

public T Pop() { top--; return items[top + 1]; }}

– Run All Tests = PASSED

– Boek (GenericsTest.cs)– GenericTest > Add Class > Boek.cs

// normaal private en met getters en setterspublic class Boek { public string Isbn; public string Titel; public int Breedte; public int Hoogte;

public Boek(string isbn, string titel, int breedte, int hoogte) { Isbn = isbn; Titel = titel; Breedte = breedte; Hoogte = hoogte; } }

– Stapel<Boek>:

[TestMethod]public void BoekStapelTest(){ Stapel<Boek> stapel = new Stapel<Boek>(5);

stapel.Push(new Boek("9789021008035", "De Da Vinci code", 15, 30)); stapel.Push(new Boek("9789024522262", "De Zoektocht", 10, 20));

// Pop().Titel zonder cast! Assert.AreEqual("De Zoektocht", stapel.Pop().Titel); Assert.AreEqual("9789021008035", stapel.Pop().Isbn);}

– Run All Tests = PASSED

– Extra methode:– Boolean IsPiramide() (is standaard geen methode van de Stack-klasse)

– IsPiramide (Stapel.cs):

public Boolean IsPiramide() { Boolean isPiramide = true; for (int i = 0; i < top; i++) { // een stapel is een piramide als het grootste element telkens beneden ligt // als dit ergens niet zo is, geen piramide if (!(items[i] > items[i + 1])) { isPiramide = false; } } return isPiramide;}

– Probleem: Build -> Operator '>' cannot be applied to operands of type 'T' and 'T'– Aangezien T nog niet gedefinieerd is (kan om het even wat zijn), kan de vergelijkingsoperator

niet gebruikt worden. – Ook: wat betekent > als T een boek-object is?– We moeten van buiten de klasse opgeven op welke manier items op de stapel vergeleken moeten

worden (dit is verschillend voor bv. integers en boeken) – → Delegates

– Delgates:– Een delegate is een instructieobject (functie-pointer)– Je geeft instructies aan een object (delegeert)

Page 60: KHK 2Ti C# Samenvatting

– Een delegate bevat de instructies die het object waaraan je de delegate geeft moet uitvoeren– We leggen aan het Stapel-object uit wanneer item1 > item2

public class Stapel<T> { // definitie van de delegate (return-waarde en parameters) // geen body! wat de functie precies doet, // wordt later buiten de klasse bepaald public delegate Boolean VergelijkingsFunctie(T parameter1, T parameter2);

// groter is zo'n functie public VergelijkingsFunctie Groter;

public Boolean IsPiramide() { …

// in IsPiramide gebruiken we een zelf gedefinieerde groter-functie if (! Groter(items[i],items[i + 1])) { isPiramide = false; } }}

– Build -> Geen compilatiefouten meer

– UnitTestStapel (UnitTestStapel.cs binnen TestClass):

private Boolean GroterInt(int i, int j) { return (i > j);}

private Boolean GroterBoek(Boek b, Boek c) { if ((b.Breedte > c.Breedte) && (b.Hoogte > c.Hoogte)) { return true; } else { return false; }}

[TestMethod]public void IntStapelIsPiramideTest(){ Stapel<int> stapel = new Stapel<int>(10); // hier de stapel-klasse vertellen wat groter precies is (in het geval van int) stapel.Groter = GroterInt;

stapel.Push(52); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(40); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(30); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(10); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(15); Assert.IsFalse(stapel.IsPiramide());}

[TestMethod]public void BoekStapelIsPiramideTest(){ Stapel<Boek> stapel = new Stapel<Boek>(10); // en in het geval van Boek stapel.Groter = GroterBoek;

stapel.Push(new Boek("9789021008035", "De Da Vinci code", 15, 30)); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(new Boek("9789024522262", "De Zoektocht", 10, 20)); Assert.IsTrue(stapel.IsPiramide()); stapel.Push(new Boek("9789087445521", "Wandelen in de Alpen", 12, 15)); Assert.IsFalse(stapel.IsPiramide());}

– Run All Tests = PASSED

Page 61: KHK 2Ti C# Samenvatting

THREADING

– Threading:– Één programma wordt opgesplitst in verschillende delen (threads) die tegelijkertijd worden

uitgevoerd– Aangezien de UI draait in een andere thread, die tegelijkertijd wordt uitgevoerd, blijft de

UI voor de gebruiker beschikbaar (i.p.v. zandloper tijdens wachten op andere taak)

– Basics:– Open solution Threading.sln– ThreadingBasics > Rechts > Set as Startup Project– FormThreading: buttonNoThreads

private void buttonNoThreads_Click(object sender, EventArgs e) { Achtergrondproces();}

private void Achtergrondproces() { int i = 1; while (true) { Console.WriteLine("Iteraties: {0}", i); i++; }}

– Run > View > Output > No threads: UI blokkeert!

– Met Threads:

using System.Threading;

namespace ThreadingBasics { public partial class FormThreading : Form { Thread thread; … private void buttonStartThread_Click(…) { thread = new Thread(Achtergrondproces); // dit is ook een delegate! we vertellen via Achtergrondproces // welke instructies het thread-object moet uitvoeren thread.Start(); }

private void buttonStopThread_Click(…) { thread.Abort(); } }}

– Run > View > Output > No threads: UI niet blokkeert!

– Thread pauzeren (in Achtergrondproces):

private void Achtergrondproces() { int i = 1; while (true) { Console.WriteLine("Iteraties: {0}", i); Thread.Sleep(250); // in milliseconden i++; }}

– Threads en Windows Forms:– ThreadingListBox > Rechts > Set as Startup Project– FormListBox

private void Achtergrondproces() { int i = 0; while (true) { if ((i % 10) == 0) { listBoxTel.Items.Clear(); } i++; listBoxTel.Items.Add("Iteraties: " + i); Thread.Sleep(250); }}

Page 62: KHK 2Ti C# Samenvatting

– Run > View > Output > Start Thread: ERROR bij listBoxTel.Items.Clear();– Achtergrondproces krijgt geen toegang tot controls (listBoxTel) van UI (zijn twee

verschillende threads)– Oplossing: vanuit Achtergrondproces aanroepen (invoke) van methodes uit eerste thread (kunnen

wel aan listBoxTel), deze methodes mogen wel geen parameters hebben!

string item;

private void ListBoxClear() { listBoxTel.Items.Clear();}

private void ListBoxAdd() { listBoxTel.Items.Add(item);}

private void Achtergrondproces() { int i = 0; while (true) { if ((i % 10) == 0) { this.Invoke(new MethodInvoker(ListBoxClear)); } i++; item = "Iteraties: " + i; // ListBoxAdd mag geen parameters hebben, dus werken met // variabele om waarde door te geven (ListBoxAdd is een delegate!) this.Invoke(new MethodInvoker(ListBoxAdd)); Thread.Sleep(250); }}

– Threads en Parameters:– MoreThreads > Rechts > Set as Startup Project– FormMoreThreads

public partial class FormMoreThreads : Form { private int aantalThreads = 0; …

private void buttonNewThread_Click(…) { aantalThreads++; Thread thread = new Thread(Tel); thread.Name = "Thread " + aantalThreads; thread.Start(); } public void Tel() { for (int i = 0; i <= 5; i++) { Console.WriteLine(Thread.CurrentThread.Name + " counts " + i); Thread.Sleep(250); } Console.WriteLine(Thread.CurrentThread.Name + " stops counting…"); } // Bedoeling: Thread 1 laten tellen tot 1, thread 2 tot 2, … → Parameters voor Tel()?!}

– Threads en Parameters (PAS AAN):

public partial class FormMoreThreads : Form { …

private void buttonNewThread_Click() { aantalThreads++; Counter counter = new Counter(); counter.Max = aantalThreads; Thread thread = new Thread(counter.Count); thread.Name = "Thread " + aantalThreads; thread.Start(); }}

Page 63: KHK 2Ti C# Samenvatting

public class Counter { private int max;

public int Max { set { max = value; } }

public void Count() { for (int i = 0; i <= max; i++) { Console.WriteLine(Thread.CurrentThread.Name + " counts " + i); Thread.Sleep(250); } Console.WriteLine(Thread.CurrentThread.Name + " stops counting…"); }}

– Threads synchroniseren:– SyncThreads > Rechts > Set as Startup Project– FormSync

int count = 0;object countLock = new object();

public void Count() { lock (countLock) { // dit codeblok kan maar door 1 thread tegelijk uitgevoerd worden for (int i = 0; i < 10; i++) { int tmp = count; Console.WriteLine(Thread.CurrentThread.Name + ": Read count={0}", tmp); Thread.Sleep(10); tmp++; Console.WriteLine(Thread.CurrentThread.Name + ": Incremented tmp to {0}", tmp); count = tmp; Console.WriteLine(Thread.CurrentThread.Name + ": Written count={0}", tmp); Thread.Sleep(10); } }}

Page 64: KHK 2Ti C# Samenvatting

INTERFACES

– Een interface is een intermediaire schakel tussen twee systemen. De interface laat de twee systemen toe met mekaar te communiceren.

– Een interface is een klasse zonder code waarin je alles verwijdert wat niet public is. – Properties en methodes worden niet gecodeerd in de interface, ze worden enkel gedefinieerd.– Interfaced-based design leidt tot los gekoppelde componenten, eenvoudiger te onderhouden software en

gemakkelijker te herbruiken code omdat de implementatie gescheiden wordt van de interface.

– Voorbeeld:– Viual Studio 2010 > New Project > Console Application > Interfaces– Interfaces > Rechts > Add New Item > Class > Potlood.cs

class Potlood { private bool isScherp; public void Schrijf(string boodschap) { Console.Write(boodschap); }

public bool IsScherp { get { return isScherp; } }}

– Potlood Interface:– Interfaces > Rechts > Add New Item > Interface > IPotlood.cs, Ipotloodslijper.cs

interface IPotlood { // enkel definiëren van de publieke methodes en properties void Schrijf(); bool IsScherp { get; }}

interface IPotloodslijper { void Slijp(IPotlood potlood);}

– Potlood Klasse implementeer IPotlood Interface:

class Potlood : IPotlood { private bool isScherp; private string boodschap;

public string Boodschap { get { return boodschap; } set { boodschap = value; } }

public void Schrijf() { Console.Write(boodschap); }

public bool IsScherp { get { return isScherp; } }}

– Solution > BUILD

– Verbeteren van het contract tussen Potlood en Slijper:– Hoe kunnen we het potlood melden dat het aangescherpt is door de slijper?– Boodschap via property

interface IPotlood { string Boodschap { get; set; } void Schrijf(); bool IsScherp { get; } void Aangescherpt();}

– Solution > BUILD

– Potlood Klasse:

class Potlood : IPotlood {

Page 65: KHK 2Ti C# Samenvatting

const int MAXTEKENS = 30; private bool isScherp; private string boodschap; private int tekensGeschreven;

public Potlood() { tekensGeschreven = 0; this.Boodschap = ""; }

public string Boodschap { get { return boodschap; } set { boodschap = value; } }

public void Schrijf() { foreach (char c in boodschap) { if (IsScherp) { Console.Write(c); tekensGeschreven++; } else { Console.Write('.'); } } Console.WriteLine(); }

public bool IsScherp { get { return tekensGeschreven < MAXTEKENS; } }

public void Aangescherpt() { tekensGeschreven = 0; }}

– PotloodSlijper Klasse:

class Potlood : IPotlood { public void Slijp(IPotlood potlood) { potlood.Aangescherpt(); }}

– Solution > BUILD

– Main (Program.cs):

class Program { static void Main(string[] args) { IPotlood potlood = new Potlood(); potlood.Boodschap = "Mijn potlood is bot en moet aangescherpt worden."; potlood.Schrijf();

if (!potlood.IsScherp) { IPotloodslijper potloodslijper = new Potloodslijper(); potloodslijper.Slijp(potlood); }

potlood.Boodschap = "Terug SCHERP!"; potlood.Schrijf(); Console.ReadLine(); }}

– Multiple Interfaces:– Een klasse kan meerdere Interfaces implementeren ↔ kan maar van één superklasse erven.

– Voeg een nieuwe Interface IGom.cs toe:

interface Igom { void Wis(); }

class Potlood : IPotlood, Igom { …

Page 66: KHK 2Ti C# Samenvatting

public void Wis() { Console.Clear(); }}