Коришћење контроле DataGridView¶
До сада је фокус био на томе како да на сигуран и организован начин дођеш до података из базе. Научио си да креираш слој за приступ подацима који враћа податке у облику објеката. Сада је време да истражиш како да те податке представиш кориснику на различите, визуелно привлачне и функционалне начине.
У оквиру Windows Forms (.NET Framework) пројекта доступан је богат скуп контрола за приказ података. У овој лекцији, научићеш да радиш са четири најчешће коришћене:
DataGridViewза детаљан, табеларни приказ података,ListViewза флексибилан приказ који може бити у форми листе, детаља или иконица,Chartза графички приказ података, идеалан за визуелизацију, иTreeViewза хијерархијски приказ података, као што су категорије и производи.
За потребе ове лекције, користићеш податке из табеле Products из базе
података Northwind, укључујући и називе категорија. Претпоставимо да си за овај
задатак применио до сада научено: написао ускладиштену процедуру…
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE usp_Proizvodi
AS
BEGIN
SELECT ProductID, ProductName, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder
FROM Products
END
…унео конекциони стринг у App.config…
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="NorthwindCS"
connectionString="Data Source=LOCALHOST\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>
…креирао класу Konekcija…
using System.Configuration;
namespace KontroleZaPrikazPodataka
{
internal class Konekcija
{
public static string ConnString
{
get
{
return ConfigurationManager.ConnectionStrings["NorthwindCS"].ConnectionString;
}
}
}
}
…и креирао класу Proizvod са методом Proizvod.UcitajSve() који враћа
List<Proizvod>:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
namespace KontroleZaPrikazPodataka
{
internal class Proizvod
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public string QuantityPerUnit { get; set; }
public decimal UnitPrice { get; set; }
public short UnitsInStock { get; set; }
public short UnitsOnOrder { get; set; }
public static List<Proizvod> UcitajSve()
{
List<Proizvod> proizvodi = new List<Proizvod>();
using (SqlConnection con = new SqlConnection(Konekcija.ConnString))
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "usp_Proizvodi";
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
foreach (DataRow dr in dt.Rows)
{
Proizvod p = new Proizvod();
p.ProductID = Convert.ToInt32(dr["ProductID"]);
p.ProductName = dr["ProductName"].ToString();
p.QuantityPerUnit = dr["QuantityPerUnit"].ToString();
p.UnitPrice = Convert.ToDecimal(dr["UnitPrice"]);
p.UnitsInStock = Convert.ToInt16(dr["UnitsInStock"]);
p.UnitsOnOrder = Convert.ToInt16(dr["UnitsOnOrder"]);
proizvodi.Add(p);
}
}
return proizvodi;
}
}
}
Контрола DataGridView¶
DataGridView
је најмоћнија и најлакша контрола за приказ табеларних података.
Њена највећа предност је што може директно да се повеже са извором података
(као што је List<Proizvod>) и аутоматски генерише колоне и редове. Потребно
је да само доделиш листу података DataSource својству контроле.
Из ToolBox-а превуци контролу DataGridView на форму и подеси својство Name
на нпр. dgvProizvodi. Можеш поставити и својство
DataGridViewAutoSizeColumnsMode на AllCells како би се аутоматски одредила
ширина свих ћелија на основу садржаја у ћелијама и својство ReadOnly на
true ако је потребан само приказ података.
Након тога, можеш да доделиш листу података DataSource својству контроле,
имајући у виду да свака операција која укључује комуникацију са базом података
треба бити у try-catch блоку унутар презентационог слоја. То може да изгледа
овако:
private void Form1_Load(object sender, EventArgs e)
{
try
{
List<Proizvod> proizvodi = Proizvod.UcitajSve();
if (proizvodi != null && proizvodi.Count > 0)
{
dgvProizvodi.DataSource = proizvodi;
dgvProizvodi.Columns["ProductID"].HeaderText = "ID";
dgvProizvodi.Columns["ProductName"].HeaderText = "Naziv proizvoda";
dgvProizvodi.Columns["QuantityPerUnit"].HeaderText = "Količina po jedinici";
dgvProizvodi.Columns["UnitPrice"].HeaderText = "Cena";
dgvProizvodi.Columns["UnitsInStock"].HeaderText = "Na stanju";
dgvProizvodi.Columns["UnitsOnOrder"].HeaderText = "Poručeno";
}
else
{
MessageBox.Show("Nema podataka o proizvodima u bazi.");
}
}
catch (Exception ex)
{
MessageBox.Show("Greška prilikom učitavanja podataka: " + ex.Message);
}
}

На догађај учитавања форме, подаци из базе биће приказани у DataGridView
контроли. Ако подаци не постоје у бази или се деси грешка приликом учитавања
података, корисник ће о томе бити обавештен.
DataGridView подржава уређивање података у табели, али уређивање ће имати
ефекта само ако је извор података везан за базу преко објекта који то подржава,
на пример објекта DataTable. Када је као DataSource постављен List<T>,
као у овом примеру (List<Proizvod>), додавање, ажурирање и измене података у
табели неће аутоматски утицати на базу. Све ово мораш „ручно” обрадити и након
тога извршити ажурирање над базом.
ADO.NET нуди и моћнији, аутоматизованији начин рада који је идеалан за брзу израду апликација за управљање подацима. Овај приступ се ослања на сарадњу три кључне компоненте:
DataTable- објекат који у меморији апликације представља тачну копију једне табеле из базе података. Он „зна” који редови су додати, измењени или обрисани.SqlDataAdapter- служи као „мост” измеђуDataTableобјекта у апликацији и стварне табеле у SQL Server бази. Његов задатак је да попуниDataTableподацима и да касније проследи све измене назад у базу.SqlCommandBuilder- је помоћна класа која ради у позадини. Када јој се проследиSqlDataAdapter, она аутоматски генерише потребне SQL команде (INSERT, UPDATE, DELETE) на основу SELECT упита који је задат. Ово те ослобађа писања тих команди.
Како ове компоненте раде заједно у једноставном практичном примеру? Нека се сав
кôд налази унутар форме, што није препоручена пракса за веће апликације, али је
одлична за демонстрацију рада са контролом DataGridView.
public partial class Form1 : Form
{
private SqlDataAdapter da;
private DataTable dt;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string conStr = "Data Source=LOCALHOST\\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True";
SqlConnection con = new SqlConnection(conStr);
da = new SqlDataAdapter("SELECT ProductID, ProductName, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder FROM Products", con);
SqlCommandBuilder builder = new SqlCommandBuilder(da);
dt = new DataTable();
da.Fill(dt);
dgvProizvodi.DataSource = dt;
dgvProizvodi.Columns["ProductID"].ReadOnly = true;
dgvProizvodi.Columns["ProductID"].HeaderText = "ID";
dgvProizvodi.Columns["ProductName"].HeaderText = "Naziv proizvoda";
dgvProizvodi.Columns["QuantityPerUnit"].HeaderText = "Količina po jedinici";
dgvProizvodi.Columns["UnitPrice"].HeaderText = "Cena";
dgvProizvodi.Columns["UnitsInStock"].HeaderText = "Na stanju";
dgvProizvodi.Columns["UnitsOnOrder"].HeaderText = "Poručeno";
dgvProizvodi.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgvProizvodi.AllowUserToAddRows = true;
dgvProizvodi.AllowUserToDeleteRows = true;
dgvProizvodi.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
}
private void button1_Click(object sender, EventArgs e)
{
try
{
da.Update(dt);
MessageBox.Show("Izmene su sačuvane u bazi podataka.");
}
catch (Exception ex)
{
MessageBox.Show("Greška pri čuvanju: " + ex.Message);
}
}
}
Када се апликација покрене, дешава се следеће:
da.Fill(dt)-SqlDataAdapterотвара конекцију, извршава SELECT упит и све резултате учитава уDataTableобјекатdt. Након тога, конекција се затвара. Апликација сада ради са подацима у меморији.dgvProizvodi.DataSource = dt-DataGridViewсе повезује наDataTable. Захваљујући овом „живом” повезивању, свака промена коју корисник направи у табели контролеDataGridView(уређивање ћелије, додавање новог реда, брисање реда) аутоматски се одражава на стањеDataTableобјекта у меморији.button1_Click- када корисник кликне на дугме, позива сеda.Update(dt).SqlDataAdapterтада анализираDataTable, проналази све редове који су измењени, додати или обрисани од последњегFillилиUpdateпозива, и за сваки од њих извршава одговарајућу SQL команду (UPDATE, INSERT или DELETE) коју јеSqlCommandBuilderпретходно припремио.

Овај приступ је изузетно ефикасан за брзу израду апликација где је потребно омогућити кориснику да директно манипулише подацима у табели. Он аутоматизује већину посла, али по цену мање флексибилности у поређењу са ручним приступом који си користио у првом примеру.