Porting evolutivo di una applicazione per la gestione di riferimenti bibliografici in ambiente .net 4.5
1. DIPARTIMENTO DI INGEGNERIA
CORSO DI LAUREA TRIENNALE IN INGEGNERIA DELL'INFORMAZIONE
CURRICULUM INFORMATICA
Porting evolutivo di una applicazione per la gestione di dati
di riferimenti biliografici in ambiente .NET 4.5
Relatore : Prof. Maurizio Fermeglia
Laureando : Giulio Ambrogi
Anno Accademico : 2011 / 2012
2. Indice dei contenuti
CAPITOLO 1 Introduzione (pag. 3)
CAPITOLO 2 Analisi dei requisiti e della situazione esistente
2.1 Intervista al committente (pag. 5)
2.2 Analisi della situazione esistente (pag. 6)
2.2.1 Il database (pag. 6)
2.2.2 Applicazione esistente (pag. 9)
2.2.3 Data Bindig (pag 12)
2.2.4 SharePoint e Web Application (pag. 12)
CAPITOLO 3 Progettazione
3.1 Principi generali di progettazione (pag. 13)
CAPITOLO 4 Implementazione
4.1 Upload di file (pag. 15)
4.2 Cancellazione di file (pag. 22)
4.3 Opzioni utente (pag. 25)
4.4 Export (pag. 26)
4.5 Implementazioni di carattere generale (pag. 41)
CAPITOLO 5 Conclusioni
5.1 Possibili miglioramenti futuri (pag. 43)
5.2 Conclusioni (pag. 43)
2
3. CAPITOLO 1
Introduzione
L'obiettivo di questa tesi è estendere le funzionalità di un'applicazione client
esistente la quale è utilizzata per la gestione dei riferimenti bibliografici e delle
attività del laboratorio MOSE.
In particolare si vuole fornire all'utente, che utilizzerà l'applicazione,la possibilità di
gestire l'upload e la cancellazione di documenti relativi alle pubblicazione, di
'upload cancellazione
esportare in forma tabellare un insieme di record bibliografici e si intende
migliorare ulteriormente l'interfaccia utente ,per rendere l'applicazione
maggiormente user friendly , coerentemente con le nuove funzionalità da
implementare.
Nei capitoli successivi le singole funzionalità e la loro implementazione verranno
illustrate nello specifico.
La situazione iniziale vede l'esistenza di :
- versione beta dell'applicazione di cui si vuole effettuare il porting, ancora priva
delle sopracitate funzionalità;
- una base di dati ,su server remoto , nella quale sono immagazzinate tutte le
informazioni utilizzate dall'applicazione, come dati anagrafici, dati bibliografici,
ecc. ;
- un'istanza di SharePoint Server installata su un server remoto (un altro), utilizzata
dalla web application che lavora in parallelo all'applicazione in questione; è da
notare che quest'ultima ancora non comunica con tale istanza ;
3
4. in questo server vengono salvati i file pdf relativi alle pubblicazioni ;
- una web application che presenta sul Web i dati bibliografici e permette
all'utente di effettuare il download di un file pdf relativo ad una pubblicazione
Ciò che ha motivato la realizzazione di questo lavoro è la necessità di permettere
all'utente di svolgere alcune operazioni direttamente dall'applicazione, ovvero
svincolare l'utente dall'utilizzo di ulteriori software , garantendo così una maggiore
semplicità di esecuzione, una presentazione dei dati funzionale ed una maggiore
sicurezza relativamente all'integrità dei dati e all'aggiornamento degli stessi.
I vincoli progettuali sono rappresentati in primo luogo dallo sviluppo in ambiente
.NET, utilizzando la tecnologia Windows Presentation Foundation e il linguaggio
di programmazione C#, in secondo luogo ,relativamente all'applicazione già
esistente, dall'attenersi per quanto possibile alla sua logica e infine
dall'implementare il tutto in modo tale che possa essere accessibile alla web
application.
Gli obiettivi di questo lavoro sono :
- analisi dei requisiti da parte del committente;
- analisi della situazione preesistente;
- studio delle tecnologie .NET, e nel particolare di WPF e C#;
- studio del funzionamento delle interazioni tra Database, applicazione e server;
- progettazione del front-end;
- realizzazione effettiva e compilazione su piattaforma .NET Framework 4.5;
- test e distribuzione dell'applicazione.
4
5. CAPITOLO 2
Analisi dei requisiti e della situazione esistente
esistente
2.1 Intervista al committente
Le richieste del committente per la nuova
versione dell'applicazione sono :
• effettuare un analisi degli oggetti
ffettuare
programmabili (viste , stored
viste
procedures , user defined functions
functions)
effettivamente utilizzati
dall'applicazione al fine di effettuare un lavoro di pulizia nella base di dati
dati;
• effettuare l'upload di un documento relativo ad una determinata
ffettuare
pubblicazione nel server sul quale è presente un'istanza di ShareP
SharePoint
Server , aggiornando allo stesso tempo i valori presenti nella base di dati
(Titolo del pdf e ID del documento)
documento).
Questo documento dovrà poi essere reperibile dalla web application che
permette la visualizzazione dei contenuti dal web
web;
• effettuare la cancellazione di un documento (analogamente all'upload
ffettuare
bisogna gestire entrambi i "lati" : SharePoint e SQL Server) ed avere a
disposizione opportuni controlli per verificare la presenza o meno di un
5
6. documento, e potere gestire facilmente le azioni (upload , cancellazione,
presentazione) relative ad esso;
• poter esportare un file in formato Excel nel quale vengano riportati tutti i
dati relativi al titolo della pubblicazione, all'anno , agli autori, alla
denominazione e ai riferimenti, fornendo inoltre all'utente la possibilità di
filtrare i risultati a suo piacimento prima di effettuare l'export;
• eventuali accorgimenti per migliorare ulteriormente il front end.
2.2 Analisi della situazione esistente
2.2.1 Il database
2.2.1
La base di dati esistente è sita in un server remoto ( di3.units.it ) ed è composta da
27 tabelle, 63 viste e 133 stored procedures.
Contiene tutti i dati inerenti la gestione del laboratorio come, ad esempio: dati
anagrafici dei dipendenti, dati delle pubblicazioni, congressi, progetti e tesi.
Le viste utilizzate dall'applicazione esistente sono:
• ViewPubblicazioniRivista : mette in relazione le informazioni delle riviste con
le informazioni dello staff;
• ViewPubblicazioniCongressi : mette in relazione i le informazioni dello staff
con il congresso relativo alla pubblicazione ;
• Staff_View : una raccolta dei dati anagrafici dei membri dello staff .
• ViewPubblicazioniRivista_Staff: variante della prima vista citata
Le stored procedures utilizzate dall'applicazione esistente sono:
6
7. • sp_PubbStaff : utilizzata per visualizzare le pubblicazioni relative ad un
membro dello staff. Riceve in input 2 parametri: @ID_staff è l'ID del
componente dello staff, mentre @ID_output è un parametro che se settato
a 0 fornisce sia le pubblicazioni presentate in un congresso che in una
rivista, se settato ad 1 fornisce solo le pubblicazioni presentate in un
congresso ed infine se settato a 2 fornisce solo le pubblicazioni presentate
in una rivista
• sp_PubbWeb: utilizzata per visualizzare le pubblicazioni presentate nel sito
web del MOSE. Riceve in input un parametro @ID_SitoWeb , che
rappresenta l'identificatore del sito web.
Non sono state trovate invece User Defined Functions.
Nella Figura 1 si riporta il Database Diagram della base di dati in questione :
7
8. TBL_Options tblRiviste tblAnno TBL_PDFDocument Versions
ID ID_rivista ID_Anno IDTitolo
VersionId
FolderPDFDocument Denominazione Anno NameFile
Version
RivistaInternazionale
Id
Referee
UserName
[Luogo pubblicazione]
TimeStamp
ISSN
FinalizeTimeStamp
Mode
ModeStack
Updates
Notes
tblProgetti tblTesiDettaglio tblTesi
ID_Progetto
ID_Tesi
ID_Tesi
Progetto
DettaglioIT
ID_Tipo
SiglaProgetto
DettaglioEN ArgomentoIT
Anno_Inizio
ArgomentoEN tblTesiStato
Anno_Fine ID_Stato
Collaborazione
Finanziatore StatoTesi
Correlatori
ResponsabileLocale
ID_Curriculum
ResponsabileGlobale tblCurriculum
Stato
FinanziamentoLocale ID_Curriculum
ID_Relatore
FinanziamentoGlobale Curriculum
Autore
VIsualizzaSito CurriculumEN tblTipiTesi
DataInizioTesi
ID_Tipo
TipoProgetto
MEseLAurea
Tipo
imgproject
AnnoLaurea
summary
Voto
link
tblStaffCongressi
tblStaff
ID_Staff tblStaffGruppi
ID_Staff
ID_Congresso ID_Gruppo
Cognome
Presentazione ID_Staff
Nome
Organizzazione Amministratore
Attivo
tblTItoloProgetto Partecipazione
Username
ID_progetto
SuInvito
Amministratore
ID_titolo
PhotoFileName
PhoneNumber
OfficeLocation
Email tblGruppi
tblCongressi Summary ID_Gruppo
ID_Congresso
Education Nome
tblKeyword Denominazione
ResearchActivity Note
ID_Keyword
Data
ResearchProject
Keyword
Anno
Collaborations
CongressoIntenazionale
Enabled
Referee
tblCollegamentoKeywordsTitolo tblStaffCorsi
Id_Keyword tblTitoli ID_Staff
tblStaffTitoli
ID_Titolo ID_Corso
ID_Titolo ID_Staff
Autori Organizzatore
ID_Titoli
Riferimento
ID_Stato
ID_Rivista
ID_Congresso tblCorsi
Anno
tblTipoPubbl ID_Corso
ID_TipoPubb NomeCorso
Titolo
tblStatoTitolo TipoPubblicazione EnteOrganizzatore
ID_Stato ID_tipoPubb
TipoPubEnglish Periodo
Stato ID_SitoW eb
Class_TS Luogo
Statoen ImpactFactor
Class_PD Anno
ID_Settore
Figura 1
8
9. 2.2.2
2.2.2 Analisi dell'applicazione esistente
Questa applicazione è stata implementata utilizzando la tecnologia Windows
Presentation Foundation (linguaggi C# e XAML).
Il suo aspetto richiama quello delle applicazioni della suite Microsoft Office 2010.
Alcuni studi sulla User Experience condotti dalle più importanti software house,
hanno
dimostrato
come ,
attraverso
l'uso di
tecniche come
l'eye tracking,
l'utente tenda
Figura 2
a leggere i
contenuti delle pagine focalizzando maggiormente la propria attenzione su due
linee orizzontali nella parte alta della pagina e su una linea verticale nella parte
sinistra della pagina, secondo quello che è noto come schema ad F. Questo
rappresenta un grande punto di forza per l'applicazione.
La pagina visualizzata in Figura 2 è la home dell'applicazione , nella quale sono
presenti link testuali che rimandano alle diverse pagine, e pulsanti (nella banda in
grigio chiaro) che rimandano alle stesse.
L'applicazione esistente è di tipo navigation-based nello stile di numerose altre
navigation-based,
applicazioni come Windows Explorer e Windows Media Player.
Le pagine sulle quali è stata focalizzata maggiormente l'attenzione sono quelle
relative alla gestione e alla presentazione dei dati relativi alle pubblicazioni
pubblicazioni.
9
10. Figura 3
Nella pagina relativa alle pubblicazioni si nota la presenza di 3 pulsanti: Dettaglio ,
Griglia e Staff Il primo di questi , lo si può già vedere nell'immagine, fornisce i
Staff.
dettagli della pubblicazione selezionata attraverso gli appositi controlli (Figura 3), il
secondo fornisce una vista in un formato griglia, simulando l'interfaccia di una
tabella di SQL Server (Figura 4) ed il terzo infine raggruppa, in formato-griglia, le
pubblicazioni in base all'autore (Figura 5) ,che può essere selezionato attraverso
una apposita listbox.
Figura 4
10
11. I pulsanti Export e Upload non svolgono ancora alcuna funzione.
Figura 5
2.2.3 Data Binding
Lo scambio di dati tra applicazione e database avviene grazie ad una stringa di
connessione denominata MoseDBConnectionString.
Il tipo di autenticazione utilizzata è Windows Authentication.
<add name="MoseUXPrototype.Properties.Settings.MoseDBConnectionString"
connectionString="Data Source=SERVERNAMESQLEXPRESS;
Initial Catalog=MoseDB;Integrated Security=True"
providerName="System.Data.SqlClient" />
Invece le interazioni tra applicazione e SharePoint (su server remoto) saranno
implementate come richieste HTTP corredate delle opportune credenziali e
verranno illustrate in seguito.
11
12. Questa stringa si trova nel file ./MoseUXPrototype.exe.config.xaml , il quale è
presente nel folder della versione distribuibile dell'applicazione.
2.2.4 Sharepoint Server e Web Application
È presente inoltre una applicazione web che presenta le informazioni
bibliografiche su un sito web. Questo sito web è stato creato utilizzando Microsoft
SharePoint.
SharePoint è un software lato server che permette la creazione di particolari siti
web attraverso lo strumento software SharePoint Designer, con il quale è possibile
gestire, tra le varie cose, un archivio di file che possono essere utilizzati
dall'applicazione web.
I file delle pubblicazioni infatti sono salvati in una apposita cartella (
www.mose.units.it/doc ).
12
13. CAPITOLO 3
Progettazione
3.1 Principi generali di progettazione
L'idea di fondo è sempre quella di conservare il layout dell'applicazione esistente,
per quanto possibile, mantenendo vivace l'interfaccia e garantendone un facile e
sicuro utilizzo da parte dell'utente.
Per quanto riguarda sia l'upload che l'export si è deciso di implementare queste
due funzionalità su due diverse finestre e non delle pagine : il motivo principale è
finestre,
il voler marcare la differenza sostanziale tra la gestione di file e la gestione di
record.
Si immagini di suddividere le funzionalità dell'applicazione su due livelli differenti:
• lettura/scrittura di record (comunicazioni tra applicazione e base di dati)
• gestione di file (upload / cancellazione) ed export (comunicazioni tra
applicazione , SharePoint e base di dati )
L'intera applicazione esistente si presenta all'utente su un'unica finestra e l'utente
può accedere ai diversi contenuti sfogliando tra le pagine di essa.
Si è preferito invece implementare le nuove funzionalità per la gestione dei file , su
finestre pop up , in modo tale da dare all'utente la sensazione di utilizzare uno
strumento con vero e proprio. Si intende implementare quindi un Uploader ed un
Exporter.
13
14. Si intende inoltre permettere all'utente di gestire le connessioni e le credenziali per
le interazioni web con i server, quindi sarà implementata un ulteriore finestra pop
up per la consolle delle opzioni.
Queste finestre saranno aperte da opportuni pulsanti posizionati nelle pagine
dell'applicazione coerentemente con la loro funzione.
14
15. CAPITOLO 4
Implementazione
4.1 Upload
Si vuole caricare un file , relativo ad una determinata pubblicazione, su SharePoint
ed aggiornare i record del database SQL Server che associano "id della
id
pubblicazione" con "titolo del pdf (Figura 6).
titolo pdf"
Figura 6
Il pulsante che permette di accedere a questa funzionalità è il pulsante Upload che
è implementato nella pagina Pubblicazioni (Figura 3,4,5).
Cliccando su tale pulsante si accede ad un nuova finestra : la console di upload
una
(Figura 7).
L'interfaccia (figura 7) di questa finestra è stata implementata in modo tale da :
interfaccia
15
16. • selezionare, attraverso una listBox, la pubblicazione per la quale si vuole
effettuare l'upload del file;
• cercare e selezionare il file da caricare;
• selezionare il server e il percorso all'interno del quale caricare il
documento;
• di impostare le proprie credenziali (Username, Dominio e Password);
• eseguire effettivamente l'upload.
È presente inoltre un pulsante per effettuare un refresh della finestra.
Figura 7
16
17. La listbox che presenta le pubblicazioni esistenti viene popolata attraverso il
metodo RiempiListbox(), il quale esegue una query di tipo select sul database
descritto dalla stringa di connessione, si riporta di seguito il codice:
public void RiempiListBox()
{
try
{
string query = "select ID_Titolo, Anno, Titolo
from ViewPubblicazioniCongressi union
select ID_Titolo,Anno,Titolo
from ViewPubblicazioniRivista";
SqlConnection cn=new
SqlConnection(Properties.Settings.Default.MoseDBConnectionString);
SqlCommand cm = new SqlCommand(query);
cm.Connection = cn;
cn.Open();
SqlDataReader dr = cm.ExecuteReader();
while (dr.Read())
{
listBox1.Items.Add(dr["ID_Titolo"]+", "+dr["Anno"]+", "+dr["Titolo"]);
}
dr.Close();
dr.Dispose();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
Il file dialog per la selezione del documento da caricare viene invece aperto
cliccando sul pulsante Sfoglia (btnSfoglia) , si riporta di seguito il codice relativo:
private void btnSfoglia_Click(object sender, RoutedEventArgs e)
17
18. {
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
Microsoft.Win32.OpenFil
Nullable<bool> result = dlg.ShowDialog();
>
if (result == true
true)
{
string filename = dlg.FileName;
// aggiorno il contenuto della textbox
textBox1.Text = filename;
}
}
Una tipica esecuzione dell'upload è rappresentata in pseudo codice in Figura 8 :
rappresentata
Figura 8
Di seguito è riportato il codice dell'effettivo upload del documento:
private bool Upload(string myFile)
string
{
try
18
19. {
//UPLOAD SU SQL SERVER
SqlConnection connection1 = new
SqlConnection(Properties.Settings.Default.MoseDBConnectionString);
string id = listBox1.SelectedItem.ToString();
string[] words = id.Split(',');
//creo un array contenente i campi della selezione
SqlCommand command = new SqlCommand("select IDTitolo from TBL_PDFDocument
where IDTitolo=" + words[0]);
command.Connection = connection1;
connection1.Open(); //apro la connessione
if (command.ExecuteScalar() != null){
connection1.Close();
}
else { //se non c'è il documento nel db lo inserisco
connection1.Close();
SqlCommand insertPdf = new SqlCommand("insert into
TBL_PDFDocument values ("
+ words[0] + " , " + "'P" + returnID() + ".pdf')");
insertPdf.Connection = connection1;
connection1.Open();
insertPdf.ExecuteNonQuery();
connection1.Close();
}
connection1.Close();
//UPLOAD SU SHAREPOINT
WebClient client = new WebClient();
//vado a reperire le credenziali dell'utente
client.Credentials = new System.Net.NetworkCredential(textBox4.Text,
passwordBox1.Password, textBox5.Text);
string url="http://"+textBox2.Text+":" + textBox3.Text+"/";
client.UploadFile(url+"P"+returnID()+".pdf" , "PUT", myFile);
client.Dispose();
MessageBox.Show("upload su sharepoint e su sql eseguito
nDocumento : p"+returnID()+".pdf");
}
catch (Exception err)
{
MessageBox.Show(err.Message);
19
20. return false;
}
return true;
}
Analisi del codice [ metodo Upload ]
Come si puo' notare il metodo è diviso fondamentalmente in due parti
parti:
l'aggiornamento di record su SQL Server e l'upload su SharePoint.
Per quanto riguarda l'aggiornamento del record su SQL Server , le fasi di
realizzazione sono:
• creazione di un nuovo oggetto Connection (per poter comunicare con
l'istanza di SQL Server) e in particolare attraverso l'utilizzo della
MoseDBConnectionString.
• reperimento dell'ID del documento selezionato dalla listbox , attraverso un
metodo abbastanza rudimentale ma efficace: ovvero, viene effettuato un
parsing dell'elemento selezionato (in formato stringa) al fine di estrarre il
primo membro separato da virgola, ovvero l'ID della pubblicazione
• costruzione della query in linguaggio Transact-SQL
Questa query fornisce un risultato numerico, se il documento è già
presente nella tabella (tabella delle pubblicazioni aventi già un file relativo
caricato), altrimenti restituisce un valore nullo se questo file non è presente:
• Se il record è già presente nella tabella, questo record non viene
aggiornato;
• Se il record non è presente viene eseguita una nuova query
(statement di tipo INSERT ) che inserisce nella tabella (ID, nome del
file).
E' da notare che il nome del file , obbedisce ad uno standard dovuto ad un vincolo
imposto dalla web application ,che lavora in parallelo con questa applicazione , in
particolare il nome del file dovrà essere nel formato "Pxxxx.pdf" dove xxxx
20
21. rappresenta l'id della pubblicazione (ad es. se l'id della pubblicazione è 16 , il
documento si chiamerà "p0016.pdf").
Il metodo returnID() è un semplice metodo che utilizza l'ID della pubblicazione
selezionata e crea la parte numerica del nome del file apponendo a sinistra dell'ID
tanti zeri quanti sono necessari al fine di ottenere un nome con 4 cifre .
private string returnID()
{
string id = listBox1.SelectedItem.ToString();
string[] words = id.Split(',');
string returnstring;
if (words[0].Length == 1) {
returnstring = "000" + words[0];
return returnstring; }
else if (words[0].Length == 2) {
returnstring = "00" + words[0];
return returnstring; }
else if (words[0].Length == 3) {
returnstring = "0" + words[0]; return returnstring; }
else {
return words[0]; }
}
Per quanto riguarda l'upload del documento su SharePoint
upload SharePoint:
• in input si riceve il path del file (locale) da caricare;
• viene creato un oggetto di tipo client che sarà il virtuale esecutore
dell'upload;
• a questo oggetto vengono assegnate le credenziali per la richiesta http ,
che saranno reperite dalle textBox (Username e Dominio) e dalla
passwordBox (Password);
• viene costruita la stringa dell'url nel quale sarà caricato il file andando a
reperire le informazioni dalle textBox (Server e Folder);
21
22. • attraverso il metodo UploadFile dell'oggetto di tipo WebClient viene poi
ttraverso
caricato il documento su SharePoint , per mezzo di una richiesta HTTP di
tipo PUT.
4.2 Cancellazione di files
Figura 9
E' stata implementata un interfaccia utente per migliorare l'interazione con le
funzionalità aggiunte e per dare all'utente la possibilità di cancellare un file pdf
pdf.
Nella schermata relativa al dettaglio della pubblicazione (Figura 9 ) sono stati
aggiunti :
• un indicatore che segnala se è presente o meno, nel database ,un file
relativo alla pubblicazione selezionata
selezionata;
• un pulsante che rimanda alla finestra di upload;
• un pulsante che permette, in caso di presenza di un file, di cancellarlo.
presenza
È più interessante concentrarsi sull'implementazione di quest'ultimo considerata la
semplicità degli altri controlli citati.
Come per l'upload del documento, la cancellazione dovrà essere effettuata su
effettuata
entrambi i "lati" (SQL Server e SharePoint)
SQL SharePoint).
22
23. Per il primo contesto viene eseguita una query del tipo : "delete from
TBL_PDFDocument where IDTitolo= ".
Mentre per la cancellazione in ambiente SharePoint viene eseguita una richiesta
http di tipo DELETE, corredando la request delle credenziali opportune.
Si riporta di seguito il codice utilizzato per la cancellazione del documento.
Figura 10
private void DeletePDF()
{
try
{
//CANCELLAZIONE SU SQL SERVER
System.Windows.Window loading =
new System.Windows.Window { Height = 100, Width = 200,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
WindowStyle = WindowStyle.None };
loading.Content = new TextBlock { Text = "Please Wait",
FontSize = 30, FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center };
loading.Show();// Mostra finestra di attesa
tblTitoli currentTitle =
(tblTitoli)this.MasterView.CurrentItem;
23
24. int IDTitolo = currentTitle.ID_Titolo;
SqlConnection connection1 =
new
SqlConnection(Properties.Settings.Default.MoseDBConnectionString);
SqlCommand command =
new SqlCommand("delete from TBL_PDFDocument
where IDTitolo=" + IDTitolo);
command.Connection = connection1;
connection1.Open();
command.ExecuteNonQuery();
connection1.Close();
label1.Content= SetLabel();
//CANCELLAZIONE SU SHAREPOINT
string nomeFile = "P" + returnID() + ".pdf";
string url = "http://"+Properties.Settings.Default.Server+":"
+Properties.Settings.Default.Sito
+"/"+nomeFile; //server:sito
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url);
req.Credentials =
new System.Net.NetworkCredential(Properties.Settings.Default.Username,
Properties.Settings.Default.Password,
Properties.Settings.Default.Dominio);
req.Method="DELETE";
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
loading.Close();
MessageBox.Show("Il documento : P" + returnID()
+ ".pdf è stato eliminato");
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
24
25. 4.3 Opzioni Utente
Figura 11
Il pulsante Opzioni (Figura 11) permette all'utente di visualizzare una nuova finestra
(Figura 12) all'interno della quale sarà possibile impostare dei valori di default per
le credenziali, in modo tale da non dover selezionare il server ed autenticarsi ogni
qualvolta si dovesse caricare o cancellare un documento da SharePoint.
Queste impostazioni di configurazione, sono salvate, al primo utilizzo
dell'applicazione, nel file MoseUXPrototype,xml.config , mentre successivamente in
automatico, l'applicazione andrà a reperire questi settaggi da un file presente in
C:/Users/NomeUser/AppData/Local/ che viene creato al primo utilizzo di questa .
All'interno di questa finestra, il pulsante Conferma esegue una modifica del valore
delle stringhe di default presenti nelle file di configurazione dell'applicazione:
• Properties.Settings.Default.Server,
• Properties.Settings.Default.Sito,
• Properties.Settings.Default.Username,
• Properties.Settings.Default.Password,
• Properties.Settings.Default.Dominio.
25
26. Figura 12
4.4 Export
Questa funzione permette all'utente di esportare in un foglio elettronico Excel i
dati delle pubblicazioni.
In particolare le informazioni che possono essere reperite sono:
• Anno di pubblicazione;
• Titolo ;
• Autore/i ;
• Riferimento ;
• Congresso (nel quale è stata presentata la pubblicazione) .
Sono presenti inoltre alcuni filtri aggiuntivi che permettono di raffinare la ricerca:
• intervallo di tempo (anni);
• tipo di pubblicazione;
• membro dello staff (autore).
Come si può notare nell'immagine (Figura 13), l'utente può scegliere se inserire o
meno ognuno dei 5 campi in alto a sinistra , selezionando o deselezionando le
checkbox.
26
27. Il processo di esportazione
in formato Excel dei dati
richiesti dall'utente si
svolge in 4 passaggi
fondamentali:
1. operazioni
preliminari (ad es.
popolamento
combobox)
2. costruzione della
query da eseguire
sulla base di dati, in
base ai campi
selezionati
dall'utente
3. compilazione di un Figura 13
file di testo semplice , utilizzando il delimitatore tab (t), utilizzando i dati di
output della query
4. apertura del file di testo da parte dell'applicazione Excel riconoscendo il tab
Primo passo
La comboBox relativa ai membri dello staff si popola grazie ad una query eseguita
sulla base di dati, questa query estrapola Nome e Cognome dei membri dello staff
public void LoadCmbStaff()
{
try
{
27
28. SqlConnection cn = new
SqlConnection(Properties.Settings.Default.MoseDBConnectionString);
SqlCommand cm = new SqlCommand("SELECT Cognome , Nome FROM tblStaff");
cm.Connection = cn;
cn.Open();
SqlDataReader dr = cm.ExecuteReader();
while (dr.Read())
{
//popolamento della combobox
cmbStaff.Items.Add(dr["Cognome"]+" , "+dr["Nome"]);
}
dr.Close();
dr.Dispose();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
Secondo passo
L'idea che sta alla base di questa implementazione è quella di analizzare elemento
per elemento (Titolo, Autore, Anno, ecc ) le richieste dell'utente e costruire
coerentemente la query.
L'algoritmo per la costruzione della stringa , che sarà il contenuto della query si
costituisce di 3 steps fondamentali, in base ai campi che vengono selezionati e in
base ai filtri che vengono attivati:
• costruzione della stringa "SELECT …FROM…"
• costruzione della stringa "WHERE …"
• coordinamento del tutto e costruzione della stringa completa
28
29. Nelle pagine seguenti viene riportato l'algoritmo in pseudocodice (Figure 14 e
15):
Stringa = «SELECT» Stringa1 = «WHERE»
F
F
Anno is checked IF Dal != null
T T
Stringa1 = Stringa1 +
Stringa = Stringa + «Anno»
«ANNO >= Dal »
F F
Titolo is checked IF Al != null
T T
Stringa1 = Stringa1 +
Stringa = Stringa + «Titolo»
«ANNO <= Al »
F F
Autori is checked IF Autore != Tutti
T T
Stringa1 = Stringa1 +
Stringa = Stringa + «Autori»
«AUTORI LIKE ‘CognomeAutore’»
F
Riferimento is checked
strWhere =14
Figura Stringa1
T
Stringa = Stringa + «Riferimento»
F
Denominazione is checked
T
Stringa = Stringa +
«Denominazione»
strSelect = Stringa
Figura 14
29
30. X = tipoPubblicazione
T
X==1
Query = strSelect + «FROM dbo.ViewPubblicazioniCongressi» +strWhere
F
T
X==2 Query = strSelect + «FROM dbo.ViewPubblicazioniRiviste» +strWhere
F
T Query = strSelect + «FROM dbo.ViewPubblicazioniCongressi» +strWhere
X==0
+ «UNION» + strSelect + «FROM dbo.ViewPubblicazioniRiviste» +strWhere
F
Figura 15
Il passaggio finale (Figura 15) è motivato dal fatto che è possibile che una
pubblicazione sia riferita contemporaneamente ad una rivista e ad un congresso e
congresso,
quindi bisogna fornire all'utente la possibilità di scegliere che tipo di dato
visualizzare , se la prima, se la seconda o se entrambe.
Di seguito si riporta il metodo per la creazione della parte iniziale della query:
string CorpoStringCongressi = " FROM dbo.ViewPubblicazioniCongressi ";
string CorpoStringRiviste = " FROM dbo.ViewPubblicazioniRivista ";
private string CostruisciQueryIniziale()
{
string SelectBase = "SELECT ";
string Supporto = SelectBase;
if (checkBoxAnno.IsChecked == true)
{
/*
Questi IF interni servono ad evitare il caso in cui ci si
trovi ad avere una
Select del tipo: >> SELECT , NOME << (virgola in mezzo)
*/
if (Supporto == "SELECT ") { Supporto = Supporto + " Anno ";}
else
{
Supporto = Supporto + ", Anno ";
30
31. }
}
if (checkBoxTitolo.IsChecked == true)
{
if (Supporto == "SELECT ") { Supporto = Supporto + " Titolo "; }
else
{
Supporto = Supporto + ", Titolo ";
}
}
if (checkBoxAutori.IsChecked == true)
{
if (Supporto == "SELECT ") { Supporto = Supporto + " Autori "; }
else
{
Supporto = Supporto + ", Autori ";
}
}
if (checkBoxRiferimento.IsChecked == true)
{
if (Supporto == "SELECT "){ Supporto = Supporto+" Riferimento"; }
else
{
Supporto = Supporto + ", Riferimento ";
}
}
if (checkBoxCongresso.IsChecked == true)
{
if (Supporto == "SELECT "){ Supporto =Supporto+" Denominazione";}
else
{
Supporto = Supporto + ", Denominazione ";
}
}
return Supporto;
}
31
32. Di seguito si riporta il codice relativo allo statement WHERE della query:
private string CostruisciQueryWhere()
{
string Original = " WHERE ";
string Supporto = Original;
if (cmbStaff.Text != null && checkBoxStaff.IsChecked==false )
{
//nella combobox sono presenti COGNOME,NOME io vado ad utilizzare
solo
//il cognome facendo un parsing della stringa cmbbox.text
string autore =cmbStaff.Text.ToString();
string[] arr = autore.Split(',');
if (Supporto == Original) //per risolvere il problema di "WHERE ,
//NOME STAFF.." SINTASSI T-SQL
{
Supporto = Supporto + " Autori LIKE '%" + arr[0] + "%' ";
}
else
{
Supporto = Supporto + " AND " + " Autori LIKE '%"
+ arr[0] + "%' ";
}
}
if ( textBoxDal.Text!=null && textBoxDal.Text!=""){
if (Supporto == Original)
{
Supporto = Supporto + " Anno >= "+ textBoxDal.Text + " ";
}
else
{
Supporto = Supporto + " AND " + " Anno >= "
+ textBoxDal.Text + " ";
}
}
if (textBoxAl.Text != null && textBoxAl.Text != "")
{
32
33. if (Supporto == Original)
{
Supporto = Supporto + " Anno <= " + textBoxAl.Text + " ";
}
else
{
Supporto = Supporto + " AND " + " Anno <= "
+ textBoxAl.Text + " ";
}
}
//passaggio finale
if (Supporto == Original)
{
return "";
}
else
{
return Supporto;
}
}
Infine si riporta il codice relativo alla concatenazione delle diverse sottostringhe e
alla formazione query completa:
private string CostruisciQuery()
{
string vuota = "";
string TotalQuery = vuota;
if (cmbTipoPubb.SelectedIndex == 0)
{
TotalQuery = TotalQuery + CostruisciQueryIniziale() + " " +
CorpoStringCongressi + CostruisciQueryWhere() + " UNION " +
CostruisciQueryIniziale() + CorpoStringRiviste +
CostruisciQueryWhere();
}
else if (cmbTipoPubb.SelectedIndex == 1)
33
34. {
TotalQuery = TotalQuery + CostruisciQueryIniziale() +
CorpoStringCongressi + CostruisciQueryWhere();
}
else if (cmbTipoPubb.SelectedIndex == 2)
{
TotalQuery = TotalQuery + CostruisciQueryIniziale() +
CorpoStringRiviste + CostruisciQueryWhere();
}
else
{
TotalQuery = TotalQuery + CostruisciQueryIniziale() +
CorpoStringCongressi + CostruisciQueryWhere() + " UNION " +
CostruisciQueryIniziale() + CorpoStringRiviste +
CostruisciQueryWhere();
}
Console.WriteLine(TotalQuery);
if (TotalQuery != vuota)
{
return TotalQuery;
}
else return vuota;
}
Terzo passo
Di seguito si riporta il codice per la costruzione del file di testo:
public string generaCsv(int colonne, string sqlCommand)
{
string csvFile = ".//TempFile.txt";
try
{
string connectionString =
Properties.Settings.Default.MoseDBConnectionString;
using (SqlConnection connection = new SqlConnection(connectionString))
{
34
35. try
{
connection.Open();
}
catch (System.Data.SqlClient.SqlException ex)
{
// handle
return "";
}
using (SqlDataAdapter adapter =
new SqlDataAdapter(sqlCommand, connection))
{
using (System.Data.DataTable table =
new System.Data.DataTable("tbl"))
{
adapter.Fill(table);
StringBuilder commaDelimitedText = new StringBuilder();
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < colonne; i++)
{
/*Qui era sorto un problema, il mio "Generatore di Csv" , quando trovava 2 spazi
consecutivi li considerava come una nuova
colonna. risolto usando espressione regolare */
row[i] = Regex.Replace(row[i].ToString(), @"s+", " ");
}
switch (colonne)
{
case 1:
{
string value = string.Format("{0}",
row[0]);
commaDelimitedText.AppendLine(value);
File.WriteAllText(csvFile,commaDelimitedText.ToString());
break;
}
case 2:
{
35
36. string value=string.Format("{0}t{1}",
row[0], row[1]);
commaDelimitedText.AppendLine(value);
File.WriteAllText(csvFile,commaDelimitedText.ToString());
break;
}
case 3:
{
string value =
string.Format("{0}t{1}t{2}", row[0], row[1],
row[2]);
commaDelimitedText.AppendLine(value);
File.WriteAllText(csvFile,commaDelimitedText.ToString());
break;
}
case 4:
{
string value =
string.Format("{0}t{1}t{2}t{3}",row[0], row[1], row[2],
row[3]);
commaDelimitedText.AppendLine(value);
File.WriteAllText(csvFile,commaDelimitedText.ToString());
break;
}
case 5:
{
string value =
string.Format("{0}t{1}t{2}t{3}t{4}",
row[0], row[1], row[2], row[3], row[4]);
commaDelimitedText.AppendLine(value);
File.WriteAllText(csvFile,commaDelimitedText.ToString());
break;
}
}
}
}
}
}
36
37. }
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
return csvFile;
}
Analisi del codice
Questo metodo sfrutta il metodo Contacolonne() che restituisce il numero di colonne
che andranno a formare la tabella in formato Excel, il risultato di questo metodo è
passato come input (int colonne).
È necessario contare le colonne perché la sintassi del comando string.Format(..)
permette di stabilire un unico tipo di formattazione.
L'input string sqlCommand è invece la query che sarà eseguita sulla base di dati.
Sono stati identificati 5 casi possibili in base al risultato del metodo Contacolonne().
Il riempimento del file di testo è effettuato attraverso un ciclo , all'interno del
quale vengono copiati nel file i dati della tabella (risultato della query) apponendo
un delimitatore /t eccezion fatta per l'ultima colonna ( /n).
/t,
Questo file viene salvato con il nome TempFile.txt nella cartella in cui è presente
l'eseguibile dell'applicazione, ed ogni qual volta verrà eseguita un'operazione di
questo tipo lo stesso file verrà riscritto prevenendo problemi di capienza di
memoria o di semplice esubero di file non necessari.
Quarto passo
Una volta creato il file di testo viene aperto un foglio elettronico Excel il quale
attraverso l'impostazione che prevede il riconoscimento del delimitatore tab ( "/t" )
popola le sue celle in base a quanto contenuto nel file di testo.
37
38. Successivamente verranno inserite le intestazioni delle colonne attraverso il
metodo generaIntestazioniExcel().
Il metodo CSVtoExcel prende in input:
• il path del file che si vuole aprire;
• il foglio Excel nel quale aprire il file;
• le coordinate della cella da cui iniziare la popolazione delle celle;
• un array di tipi di dato per le rispettive colonne;
• un valore booleano che che imposta l'autodimensionamento (o meno)
delle colonne.
Di seguito si riporta il codice del metodo in questione:
public void CSVtoExcel(string importFileName, Excel.Worksheet destinationSheet,
Excel.Range destinationRange, int[] columnDataTypes, bool autoFitColumns)
{
destinationSheet.QueryTables.Add( "TEXT;" +
System.IO.Path.GetFullPath(importFileName),destinationRange,
Type.Missing);
destinationSheet.QueryTables[1].Name =
System.IO.Path.GetFileNameWithoutExtension(importFileName);
destinationSheet.QueryTables[1].FieldNames = true;
destinationSheet.QueryTables[1].RowNumbers = false;
destinationSheet.QueryTables[1].FillAdjacentFormulas = false;
destinationSheet.QueryTables[1].PreserveFormatting = true;
destinationSheet.QueryTables[1].RefreshOnFileOpen = false;
destinationSheet.QueryTables[1].RefreshStyle =
XlCellInsertionMode.xlInsertDeleteCells;
destinationSheet.QueryTables[1].SavePassword = false;
destinationSheet.QueryTables[1].SaveData = true;
destinationSheet.QueryTables[1].AdjustColumnWidth = true;
destinationSheet.QueryTables[1].RefreshPeriod = 0;
destinationSheet.QueryTables[1].TextFilePromptOnRefresh = false;
//l'id 65001 rappresenta l'id della piattaforma che decodifica i
contenuti in UTF-8
destinationSheet.QueryTables[1].TextFilePlatform = 65001;
destinationSheet.QueryTables[1].TextFileStartRow = 1;
38
39. destinationSheet.QueryTables[1].TextFileParseType =
XlTextParsingType.xlDelimited;
destinationSheet.QueryTables[1].TextFileTextQualifier =
XlTextQualifier.xlTextQualifierDoubleQuote;
destinationSheet.QueryTables[1].TextFileConsecutiveDelimiter = false;
destinationSheet.QueryTables[1].TextFileTabDelimiter = true;
destinationSheet.QueryTables[1].TextFileSemicolonDelimiter = false;
destinationSheet.QueryTables[1].TextFileCommaDelimiter = false;
destinationSheet.QueryTables[1].TextFileSpaceDelimiter = false;
destinationSheet.QueryTables[1].TextFileColumnDataTypes =
columnDataTypes;
destinationSheet.QueryTables[1].Refresh(false);
if (autoFitColumns == true) {
destinationSheet.QueryTables[1].Destination.EntireColumn.AutoFit();
}
}
Mentre il pulsante che aziona questo meccanismo agisce in questo modo: viene
aperta inizialmente una finestra che avvisa l'utente di attendere l'esecuzione del
processo, viene aperto un foglio Excel (non visibile ancora all'utente), viene
generato il file di testo sopra citato, viene eseguito il metodo CsvToExcel ,
vengono generate le intestazioni per le colonne del file Excel , dunque sparisce la
finestra di attesa e viene visualizzato il file.
private void button1_Click(object sender, RoutedEventArgs e)
{
try
{
System.Windows.Window loading = new System.Windows.Window {
Height = 100,
Width = 200, WindowStartupLocation =
WindowStartupLocation.CenterScreen,
WindowStyle = WindowStyle.None };
39
40. loading.Content = new TextBlock { Text = "Please Wait",
FontSize = 30,FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center };
loading.Show();
Excel.Application xla = new Excel.Application();
Excel.Workbook wb =
xla.Workbooks.Add(Excel.XlSheetType.xlWorksheet);
Excel.Worksheet ws = (Excel.Worksheet)xla.ActiveSheet;
xla.Visible = false;
int cln = contacolonne();
string filepath = generaCsv(cln, CostruisciQuery());
CSVtoExcel(filepath, (Excel.Worksheet)(wb.Worksheets[1]),
(Excel.Range)(((Excel.Worksheet)wb.Worksheets[1]).get_Range("$A$2")),
new int[] { 2, 2, 2, 2, 2 }, true);
//Come ultima cosa inserisco le intestazioni delle colonne
List<string> lista = generaIntestazioniExcel();
for (int i = 0; i < cln; i++)
{
ws.Cells[1, i + 1] = lista[i];
}
xla.Visible = true;
wb.ActiveSheet.QueryTables[1].Delete();
loading.Close();
}
catch (Exception myex)
{
MessageBox.Show(myex.Message);
}
}
40
41. Il risultato di un possibile Export può essere:
(a) Creazione del file txt, non visibile all'utente
(b) Apertura del file da parte di Excel
4.5 Implementazioni di carattere generale
Sempre nell'ottica di migliorare ulteriormente l'interfaccia utente per renderla più
funzionali ai bisogni del committente sono implementate alcune features , di
minor rilevanza per, ma di indubbia utilità per l'utente:
• nella pagina relativa alle pubblicazioni in formato-griglia, viene aggiunta
una colonna che presenta il nome del documento pdf , se presente, o nulla
in caso contrario (Figura 16);
• per le pagine "PubblicationGrid" e "PubblicationCustom" è stato riscritto , in
modo coerente con le modifiche apportate, il codice che permette
all'utente di accedere ai dettagli della pubblicazione selezionata cliccando
due volte sulla riga della griglia;
• sono stati abilitati, nella home page, i link testuali che rimandano alle
pagine relative (come Upload e Export) e sono stati modificati i label relativi
alla versione ed al nome dell'applicazione.
41
43. CAPITOLO 5
Conclusioni
5.1 Possibili miglioramenti
Svolgendo questo lavoro si è pensato inoltre a possibili miglioramenti , che
potrebbero essere sviluppati in futuro.
Tra i quali quelli di maggior rilevanza sono:
• funzionalità di import: permettere all'utente di effettuare un importazione
di dati, da tabella excel, in modo tale da non dover inserire i record
manualmente (ad esempio : nome, cognome e dati anagrafici di un nuovo
membro dello staff).
Questa feature alleggerirebbe ancor di più il lavoro per l'utente che
attualmente deve inserire i nuovi dati , pur sempre via applicazione, ma
manualmente
• versione web dell'applicazione, completamente svincolata quindi dal Pc
dell'utente .
Possibile utilizzo della tecnologia ASP.NET
5.2 Conclusioni
Features implementate
Upload PDF
Cancella PDF
Export
Gestione Credenziali
43
44. L'obiettivo di implementare tutte le funzionalità richieste dal committente è stato
raggiunto.
Per quanto riguarda invece le conoscenze e le abilità acquisite, sono stati raggiunti
i seguenti obiettivi:
• raccolta dei requisiti mediante intervista al committente
• analisi della situazione esistente
• studio di .NET e di Windows Presentation Foundation
• studio di Database Relazionali
• studi sulle comunicazioni tra le diverse entità (server, applicazione, db)
L'applicazione ora non è più in fase di test (beta), ma è a tutti gli effetti in uso ,
nella sua versione 2.0
2.0.
Righe di codice scritte :
• C# : 700
• XAML : 300
44
45. Bibliografia
• http://msdn.microsoft.com/en-us/library/aa970268.aspx (Introduction to
WPF - MSDN)
• M. Miotto, "Progettazione e sviluppo di una applicazione per la gestione di
dati bibliografici in ambiente .NET " , Tesi per corso di laurea triennale in
Ingegneria Informatica (Università degli studi di Trieste)
• Dispense del corso di Basi di Dati tenuto dal Dott. Fermeglia presso
l'Università degli Studi di Trieste
• http://msdn.microsoft.com/it-it/library/ms254978(v=vs.110).aspx
• http://stackoverflow.com/questions/8947410/using-settings-settings-in-a-
windows-wpf-app-vs2010
• http://www.codeproject.com/Articles/19509/Write-Data-to-Excel-using-C
• Adam Nathan - WPF 4 Unleashed - Pearson Education (US) – 2010
• http://blogs.msdn.com/b/pietrobr/archive/2007/09/07/parliamo-di-linq-
parte-3.aspx
• http://msdn.microsoft.com/it-it/vcsharp/cc788743(en-us).aspx
45