Το προφίλ μας στο Google Plus
3

Ανάπτυξη εφαρμογών σε C# για το .NET [μέρος 9ο]

Μέσα από ένα μακρύ αλλά διασκεδαστικό ταξίδι στον κόσμο της C#, καταφέραμε να δημιουργήσουμε μια πλήρη και λειτουργική εφαρμογή για το περιβάλλον των Windows: τον text editor ονόματι deltaPad! Το μικρόβιο του deltaHacker, όμως, δεν μάς αφήνει να επαναπαυθούμε. Συνεχίζουμε λοιπόν με στόχο την κατάκτηση της επόμενης κορυφής του .NET framework.

Δεν ξέρουμε για εσάς αλλά εμείς, στο Eastern Outpost της Parabing, συλλάβαμε πολλές φορές τους εαυτούς μας να χρησιμοποιούμε το deltaPad αντί του κλασικού σημειωματάριου των Windows (βλ. το σχετικό άρθρο στο deltaHacker 046). Δεν μπορούσαμε να προσπεράσουμε τη σκέψη ότι πρόκειται για καρπό των δικών μας προσπαθειών, ενώ ήταν στη μέση και το πείσμα. Θέλαμε να αποδείξουμε την ανωτερότητα της δικής μας εφαρμογής έναντι του Notepad, καθώς και το γεγονός ότι η ενασχόληση με τη C# άξιζε τον κόπο. Κάπως έτσι, δεν πέρασε πολύς χρόνος έως ότου θέσουμε τον επόμενό μας στόχο: την περαιτέρω βελτίωση του deltaPad! Αυτή η προσπάθεια εκτός από διασκεδαστική, θα ήταν και ενδιαφέρουσα, αφού θα μας έφερνε σε επαφή με ακόμα πιο προχωρημένες τεχνικές του .NET framework.

Σώζοντας τον κόπο μας
Κάτι που μας ενοχλούσε ιδιαίτερα στο παλιό deltaPad ήταν η απουσία μιας ερώτησης κατά τον τερματισμό της εφαρμογής, για την αποθήκευση των όποιων τροποποιήσεων στο ανοιγμένο αρχείο. Για να λύσουμε αυτό το πρόβλημα σκεφτήκαμε να δημιουργήσουμε μια μεταβλητή που θα λειτουργεί ως σημαία (flag) και θα επισημαίνει το αν υπάρχουν αλλαγές στο τρέχον κείμενο. Η εν λόγω σημαία θα σηκώνεται με οποιαδήποτε αλλαγή στο κείμενο και θα κατεβαίνει αυτόματα με την αποθήκευση. Η σχετική μεταβλητή θα ελέγχεται από το πρόγραμμα κατά το κλείσιμο του deltaPad ή κατά τη φόρτωση ενός νέου αρχείου, ενέργεια που ως συνέπεια έχει το κλείσιμο του προηγούμενου. Όταν διαπιστώνεται ότι υπάρχουν αλλαγές, το πρόγραμμα θα εμφανίζει στο χρήστη μια ερώτηση για την αποθήκευση του αρχείου ή την ακύρωση της ενέργειας και την παραμονή στο ίδιο έγγραφο.

Ξεκινάμε, λοιπόν, ορίζοντας εντός της κλάσης MainForm μια κατάλληλη μεταβλητή:

private bool textChanged = false;

Προφανώς, όταν ξεκινά το πρόγραμμα η τιμή της textChanged δεν μπορεί παρά να είναι false, εφόσον είναι αδύνατο να έχουμε τροποποιήσει ήδη κάποιο αρχείο. Έτσι, την αρχικοποιούμε σε false κατά την εκκίνηση του προγράμματος (φόρτωση MainForm) και την αλλάζουμε σε true οποτεδήποτε τροποποιείται το κείμενο. Η οποιαδήποτε αλλαγή στο κείμενο ενός textbox πυροδοτεί το συμβάν TextBoxTextChanged. Εμείς εκμεταλλευόμαστε αυτό το συμβάν για την ενημέρωση της textChange ως εξής:

void TextBoxTextChanged(object sender, EventArgs e) {
    if(!textChanged)
        textChanged = true;
}

Εφόσον δεν χρειάζεται να θέτουμε την τιμή της textChanged σε true διαρκώς, δηλαδή κάθε φορά που τροποποιείται το κείμενο, αρκεί να ελέγχουμε την τιμή της κι αν είναι ήδη true να παρακάμπτουμε την εκχώρηση. Με αυτές τις λίγες γραμμές ολοκληρώνεται ο μηχανισμός της σημαίας που μας ειδοποιεί για τυχόν αλλαγές στο κείμενο. Σειρά έχουν οι απαραίτητοι έλεγχοι της μεταβλητής textChanged, καθώς και η εμφάνιση της σχετικής ερώτησης. Μια προφανής θέση γι’ αυτόν τον έλεγχο είναι το κλείσιμο του προγράμματος και άλλη μία αποτελεί το άνοιγμα ενός αρχείου. Ακολουθώντας τις καλές πρακτικές προγραμματισμού, δημιουργούμε μια μέθοδο που θα εξυπηρετεί τις ανάγκες μας και στις δύο περιπτώσεις. Δείτε τη μέθοδο AskSave:

private DialogResult AskSave() {
    DialogResult result = MessageBox.Show(
         "Θέλετε να αποθηκεύσετε τις αλλαγές που πραγματοποιήθηκαν;",
         "Το κείμενο έχει τροποποιηθεί",
         MessageBoxButtons.YesNoCancel,
         MessageBoxIcon.Question);

    if(result == DialogResult.Yes) {
        SaveText();
    }
    return result;            
}

Η παραπάνω μέθοδος δεν δέχεται παραμέτρους και εμφανίζει ένα πλαίσιο μηνύματος με τρεις δυνατές απαντήσεις: “Ναι”, “Όχι” και “Άκυρο”. Η μέθοδος MessageBox.Show() είναι υπεύθυνη για τη σχεδίαση και την εμφάνιση του πλαισίου με την ερώτηση. Επιστρέφει μια απαρίθμηση τύπου DialogResult, από την οποία μπορούμε να παίρνουμε την απάντηση του χρήστη. Έτσι, στην περίπτωση που ο χρήστης πατήσει το κουμπί “Ναι”, καλούμε τη μέθοδο SaveText(). Πρόκειται για μια μέθοδο που είχαμε δημιουργήσει στο προηγούμενο μέρος της σειράς και η οποία αναλαμβάνει την αποθήκευση του κειμένου. Στο τέλος, η AskSave επιστρέφει την επιλογή του χρήστη (την απαρίθμηση που πήραμε από τη MessageBox.Show), καθώς η επιλογή του “Άκυρο” απαιτεί διαφορετικό χειρισμό (ανάλογα με την περίσταση).

Ας εξετάσουμε τώρα το σημείο τερματισμού της εφαρμογής. Ο τερματισμός συνεπάγεται το κλείσιμο της κύριας φόρμας, γεγονός που πυροδοτεί τα συμβάντα FormClosing (η φόρμα κλείνει αλλά είναι ακόμα ορατή) και FormClosed (η φόρμα έκλεισε και δεν υπάρχει στην οθόνη). Εμείς θα χρησιμοποιήσουμε το πρώτο συμβάν, αφού δεν θέλουμε να διαγραφεί η φόρμα πριν αποφασίσει ο χρήστης τι προτίθεται να κάνει.

void MainFormFormClosing(object sender, FormClosingEventArgs e) {
    if(textChanged) {
        if(AskSave() == DialogResult.Cancel) {
            e.Cancel = true;
        }
    }
}

Όταν η μεταβλητή textChanged έχει την τιμή true (το κείμενο έχει αλλάξει), καλούμε τη συνάρτηση AskSave() κι αναμένουμε το αποτέλεσμά της. Αν αυτό τυγχάνει να είναι DialogResult.Cancel, φροντίζουμε να ακυρώσουμε το κλείσιμο της φόρμας τροποποιώντας κατάλληλα τις παραμέτρους του συμβάντος του κλεισίματος (FormClosingEventArgs). Συγκεκριμένα, θέτουμε την ιδιότητα cancel σε true, που θα έχει σαν συνέπεια να αποτραπεί ο τερματισμός του προγράμματος.

Το άλλο σημείο όπου θα θέλουμε να αξιοποιήσουμε τη μέθοδο AskSave είναι το άνοιγμα ενός αρχείου. Για το λόγο αυτό επεμβαίνουμε στην υπάρχουσα μέθοδο LoadText, ως εξής:

private void LoadText() {
    if(textChanged) {
        if(AskSave() == DialogResult.Cancel) {
            return;
        }
    }

    DialogResult result = openFileDialog.ShowDialog(this);
    if(result == DialogResult.OK) {
        try{
            textBox.Text = File.ReadAllText(openFileDialog.FileName);
            textChanged = false;
        }catch(Exception err) {
            MessageBox.Show(err.Message,
                "Σφάλμα φόρτωσης αρχείου",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
        }
    }
}

Εδώ, αν η AskSave επιστρέψει την τιμή DialogResult.Cancel τότε η φόρτωση του αρχείου τερματίζεται με την εκτέλεση της return. Επίσης, προσέξτε ότι μετά τη φόρτωση του κειμένου στο textbox ορίζουμε την textChanged σε false, αφού πλέον έχουμε μπροστά μας ένα νέο κείμενο.

Κλείνοντας την εφαρμογή εμφανίζεται πλέον μια σχετική προειδοποίηση, επιτρέποντας στο χρήστη να αποθηκεύσει όσες αλλαγές ενδέχεται να πραγματοποίησε στο ανοιγμένο αρχείο.

Αποθήκευση επιλογών
Κάθε πρόγραμμα που σέβεται τον εαυτό του διατηρεί όλες τις αλλαγές που κάνει ο χρήστης στις προεπιλεγμένες ρυθμίσεις. Γιατί όχι και το deltaPad; Άλλωστε ακόμα κι εμείς οι ίδιοι, οι περήφανοι δημιουργοί του, εκνευριζόμασταν με την ανάγκη να αλλάζουμε συχνά πυκνά τη γραμματοσειρά, το μέγεθος του παραθύρου κ.ο.κ. Θα θέλαμε να κάνουμε τις σχετικές ρυθμίσεις και το πρόγραμμα να τις θυμάται μέχρι να αλλάξουμε γνώμη.

Οι λύσεις για την αποθήκευση των ρυθμίσεων είναι πολλές. Η πιο απλή περιλαμβάνει τη χρήση αρχείων απλού κειμένου. Αυτά τα αρχεία περιλαμβάνουν μια ρύθμιση σε κάθε γραμμή και το περιεχόμενό τους φορτώνεται σε έναν πίνακα κατά την εκκίνηση του προγράμματος. Μια άλλη λύση σχετίζεται με τα αρχεία INI, ενώ μια τρίτη προβλέπει τη χρήση αρχείων XML. Παρεμπιπτόντως, η τρίτη λύση προτείνεται κι από την Microsoft για την πλατφόρμα .NET. Η συγκεκριμένη λύση φαίνεται δελεαστική αλλά μας προκαλεί τρομερή ναυτία, καθώς οι κλάσεις που προσφέρει το .NET γι’ αυτή τη δουλειά είναι επιεικώς για τα μπάζα. Πάντως, εκτός από την αποθήκευση των ρυθμίσεων σε κάποιο αρχείο υπάρχει πάντα και η λύση του μητρώου των Windows. Το γνωστό μας Registry, όπως αρεσκόμαστε να το λέμε εδώ στην περιοχή της Δασκαλόπετρας στη Χίο (δίπλα ακριβώς από το ιερό της Κυβέλης, όπου ο Όμηρος εξιστορούσε τα ομώνυμα έπη στους μαθητές του), έχει μια πολύ μεγάλη ιστορία που ξεκινά από την έκδοση 3.1 των Windows, ενώ συνοδεύεται κι από μια μυστηριακή φήμη. Στην πράξη, το μητρώο δεν είναι τίποτα άλλο παρά μια (ιεραρχικά δομημένη) βάση δεδομένων. Μπορούμε να το φανταζόμαστε σαν ένα σύνολο φακέλων, μέσα στους οποίους υπάρχουν άλλοι φάκελοι και αρχεία, τα οποία φυλάσσουν πληροφορίες/ρυθμίσεις. Στην ορολογία του μητρώου, οι υποφάκελοι ονομάζονται κλειδιά και τα αρχεία τιμές. Επομένως, ένα κλειδί μπορεί να περιλαμβάνει πολλά υποκλειδιά και πολλές τιμές. Μέσα στο μητρώο υπάρχουν κάποια βασικά κλειδιά, κάτω από τα οποία αποθηκεύονται γενικές ρυθμίσεις του συστήματος. Εμείς, για την αποθήκευση των ρυθμίσεων του προγράμματός μας μπορούμε να στραφούμε στο κλειδί HKEY_CURRENT_USER. Εκεί αποθηκεύονται ρυθμίσεις που αφορούν αποκλειστικά στον τρέχοντα χρήστη. Μεταξύ άλλων υπάρχει και το υποκλειδί SOFTWARE, που περιλαμβάνει ρυθμίσεις για τα προγράμματα του συγκεκριμένου χρήστη. Κάθε φορά που σκοπεύουμε να αποθηκεύσουμε πολλές ρυθμίσεις που σχετίζονται μεταξύ τους, μια καλή πρακτική είναι να τις τοποθετούμε “κάτω” από το ίδιο κλειδί. Έτσι και για τις ανάγκες του deltaPad, μπορούμε να δημιουργήσουμε τα εξής κλειδιά:

HKEY_CURRENT_USER\Software\deltaHacker\deltaPad

Για τη διαχείριση του μητρώου το .NET framework παρέχει την κλάση RegistryKey, η οποία ανήκει στο χώρο ονομάτων Microsoft.Win32. Επομένως, αρκεί να εισάγουμε το σχετικό χώρο ονομάτων στον κώδικά μας και να δημιουργήσουμε μια μέθοδο η οποία θα κατασκευάζει όλα τα απαραίτητα κλειδιά που αναφέραμε νωρίτερα:

private RegistryKey ApplicationRegKey() {
    RegistryKey key = Registry.CurrentUser.OpenSubKey("Software",true);
    key.CreateSubKey(“deltaHacker”);
    key = key.OpenSubKey(Application.CompanyName, true);
    key.CreateSubKey(“deltaPad”);
    key = key.OpenSubKey(Application.ProductName, true);
    return key;
}

Η παραπάνω μέθοδος επιστρέφει ένα “ανοιγμένο” κλειδί, έτοιμο να δεχτεί την όποια επεξεργασία. Για να αποθηκεύσουμε μια τιμή μπορούμε να χρησιμοποιήσουμε το ανοιγμένο κλειδί ως εξής:

RegistryKey key = ApplicationRegKey();
key.SetValue("width", this.Width);

Στη συνέχεια, για να διαβάσουμε την αποθηκευμένη τιμή μπορούμε να κάνουμε κάτι τέτοιο:

int formWidth = (int) key.GetValue("width").ToString();
key.Close();

Αυτό που θα πρέπει να προσέχουμε κατά την εργασία με τα κλειδιά, είναι ότι στο τέλος πρέπει να τα “κλείνουμε”!

Οι ρυθμίσεις της εφαρμογής που έχουν αποθηκευτεί στο μητρώο, όπως φαίνονται μέσα από το εργαλείο regedit των Windows.

Αν η εργασία με το μητρώο σάς φαίνεται κουραστική, πρέπει να σας πούμε ότι δεν είσαστε μόνοι. Δεν χρειάζεται όμως να αποθαρρυνόμαστε και να αποφεύγουμε την αξιοποίηση του μητρώου, καθώς οι διάφοροι ανεξάρτητοι προγραμματιστές, ανάμεσά τους και ο γράφων, έχουν αναπτύξει κλάσεις που απλοποιούν στο μέγιστο αυτή την εργασία.

Για τις ανάγκες του άρθρου μας, λοιπόν, καταφεύγουμε στη χρήση της βιβλιοθήκης Multipetros.Confing.dll, η οποία διανέμεται υπό την άδεια FreeBSD και μπορείτε να την κατεβάσετε, μαζί με τον πηγαίο κώδικα και τη σχετική τεκμηρίωση, από εδώ. Η συγκεκριμένη βιβλιοθήκη, εκτός από την κλάση RegIni, η οποία απλοποιεί την αποθήκευση ρυθμίσεων στο μητρώο, παρέχει και τις κλάσεις Ini και SimpleIni, οι οποίες πετυχαίνουν κάτι παρόμοιο χρησιμοποιώντας αρχεία INI. Αφού εισάγουμε την βιβλιοθήκη στο έργο μας, εισάγουμε και τον αντίστοιχο χώρο ονομάτων στην αρχή του κώδικα της φόρμας:

using Multipetros.Config;

Στη συνέχεια δημιουργούμε μια μεταβλητή που την ονομάζουμε properties, μέσω της οποίας θα ανακτούμε και θα γράφουμε τις ρυθμίσεις στο μητρώο:

private RegistryIni properties = new RegistryIni(
                                                    Application.CompanyName,
                                                    Application.ProductName);

Παρατηρήστε ότι χρησιμοποιούμε τις ιδιότητες CompanyName και ProductName του αντικειμένου Application. Το συγκεκριμένο αντικείμενο αντιπροσωπεύει την εφαρμογή μας και οι δύο ιδιότητες, όπως φανερώνει και τ’ όνομά τους, επιστρέφουν το όνομα της εταιρείας ανάπτυξης και το όνομα του προϊόντος. Αυτά τα δύο στοιχεία μπορούμε να τα ορίσουμε στο αρχείο AssemblyInfo.cs, που βρίσκεται κάτω από το στοιχείο Properties του παράθυρου Projects. Εξετάζοντας τις γραμμές του εν λόγω αρχείου θα βρούμε τις καταχωρήσεις AssemblyCompany και AssemblyProduct. Από εκεί πρέπει να δώσουμε στις συγκεκριμένες ιδιότητες μια ταιριαστή τιμή:

[assembly: AssemblyCompany("deltaHacker")]
[assembly: AssemblyProduct("deltaPad")]

Τώρα, θα εξετάσουμε μια μέθοδο που θα φορτώνει τις ιδιότητες από το μητρώο και θα τις εφαρμόζει στο πρόγραμμα. Η συγκεκριμένη μέθοδος θα χρησιμοποιείται κατά την εκκίνηση της εφαρμογής:

private void LoadProperties() {
    string valStr;
    int valInt;

    valStr = properties["width"];
    int.TryParse(valStr, out valInt);
    if(valInt == 0)
        valInt = 450;
    this.Width = valInt;
    
    valStr = properties["height"];
    int.TryParse(valStr, out valInt);
    if(valInt == 0)
        valInt = 300;
    this.Height = valInt;

    valStr = properties["top"];
    int.TryParse(valStr, valInt);
    if(valInt != 0)
        this.Top = valInt;

    valStr = properties["left"];
    int.TryParse(valStr, valInt);
    if(valInt != 0)
        this.Left = valInt;

    valStr = properties["maximized"];
    this.WindowState = (valStr == "True") ? FormWindowState.Maximized : FormWindowState.Normal;

    valStr = properties["font"];
    if(valStr != "") {
        FontConverter fontCon = new FontConverter();
        Font storedFont = (Font) fontCon.ConvertFromString(valStr);
        textBox.Font = storedFont;
    }
}

Ας δούμε και τον κώδικα για μια μέθοδο που θα αποθηκεύει τις ιδιότητες στο μητρώο. Η κλήση αυτής της μεθόδου θα πραγματοποιείται κατά τον τερματισμό του προγράμματος.

private void SaveProperties() {
    if(this.WindowState == FormWindowState.Maximized) {
        properties["maximized"] = true.ToString();
    } else {
        properties["width"] = this.Width.ToString();
        properties["height"] = this.Height.ToString() ;
        properties["top"] = this.Top.ToString();
        properties["left"] = this.Left.ToString();
    }

    FontConverter fontCon = new FontConverter();
    properties["font"] = fontCon.ConvertToString(textBox.Font);
}

Στη μέθοδο LoadProperties δεν κάνουμε τίποτε άλλο από το να διαβάζουμε διαδοχικά τις τιμές που έχουν αποθηκευτεί στο μητρώο από το πρόγραμμά μας. Οι εν λόγω τιμές αποθηκεύονται ως συμβολοσειρές στη μεταβλητή valStr, ενώ ο αντίστοιχος ακέραιος –σε όσες περιπτώσεις πραγματοποιείται αυτή η μετατροπή– αποθηκεύεται στη μεταβλητή valInt. Αν κάποια από τις τιμές που προσπαθούμε να διαβάσουμε δεν υπάρχει στο μητρώο, λαμβάνουμε ένα κενό string. Η μετατροπή των συμβολοσειρών που αναπαριστούν αριθμούς πραγματοποιείται με τη μέθοδο TryParse, του αντικειμένου int. Η ίδια η μέθοδος επιστρέφει true όταν η μετατροπή πραγματοποιείται χωρίς πρόβλημα, ενώ σε κάθε άλλη περίπτωση επιστρέφει false. Το αποτέλεσμα της μετατροπής επιστρέφεται σε μια μεταβλητή που προσδιορίζουμε εμείς κατά την κλήση της TryParse. Όπως βλέπετε, οι πρώτες τιμές που επιχειρεί να διαβάσει από το μητρώο η μέθοδός μας αφορούν στις διαστάσεις και στη θέση της κύριας φόρμας του προγράμματος. Για να λειτουργήσει όπως αναμένουμε ο καθορισμός αυτών των τιμών, πρέπει να έχουμε θέσει την ιδιότητα StartPosition της φόρμας σε Manual. Στη συνέχεια, για τη τιμή maximized περιμένουμε ένα λεκτικό True ή False και, όπως βλέπετε, η συγκεκριμένη τιμή καθορίζει την κατάσταση της φόρμας. Τέλος, για τη φόρτωση της αποθηκευμένης γραμματοσειράς χρησιμοποιούμε το αντικείμενο FontConverter, το οποίο παρέχει τη μέθοδο ConvertFromString. Η συγκεκριμένη μέθοδος επιστρέφει ένα αντικείμενο Font, το οποίο έχει εκφραστεί ως συμβολοσειρά από την αντίστοιχη μέθοδο ConvertToString. Αυτή η μετατροπή, βέβαια, πραγματοποιείται στη μέθοδο που αποθηκεύει τις ρυθμίσεις μας. Τέλος, το αντικείμενο Font που λαμβάνουμε το αποδίδουμε ως τιμή στην αντίστοιχη ιδιότητα του textbox.

Στη μέθοδο SaveProperties πραγματοποιείται η αντίστροφη μετακίνηση δεδομένων: από το πρόγραμμα προς το μητρώο. Αρχικά ελέγχουμε την ιδιότητα WindowState. Αν η συγκεκριμένη ιδιότητα έχει την τιμή Maximized, αποθηκεύουμε στο μητρώο τη σχετική ρύθμιση και προσπερνάμε την αποθήκευση των υπόλοιπων τιμών. Όταν θα ξανατρέξουμε το πρόγραμμα, βλέπετε, θα αρκεί να μεγιστοποιήσουμε το παράθυρό του. Αν η ιδιότητα WindowState δεν έχει την τιμή Maximized, αποθηκεύουμε αυτή τη ρύθμιση, όπως επίσης τις διαστάσεις και τη θέση του παραθύρου. Φυσικά, οι σχετικές τιμές μετατρέπονται πρώτα σε συμβολοσειρές. Τέλος, όπως αναφέραμε και προηγουμένως, για την αποθήκευση της επιλεγμένης γραμματοσειράς χρησιμοποιούμε τη μέθοδο ConvertToString.

Το μόνο που απομένει τώρα είναι να φτιάξουμε δύο μεθόδους για το χειρισμό των γεγονότων Load και Closed της φόρμας μας. Το πρώτο πυροδοτείται κατά τη φόρτωση της φόρμας (εκκίνηση του προγράμματος) και το δεύτερο όταν πια η φόρμα έχει κλείσει (τερματισμός του προγράμματος):

void MainFormLoad(object sender, EventArgs e) {
    LoadProperties();
}

void MainFormFormClosed(object sender, FormClosedEventArgs e) {
    SaveProperties();
}

Μετά κι από αυτό μπορούμε να απολαύσουμε την ευκολία που προσφέρει η αυτόματη αποθήκευση των ρυθμίσεών μας. Μπορούμε να τρέξουμε το πρόγραμμα, να αλλάξουμε γραμματοσειρά και να μεταβάλλουμε τη θέση ή και το μέγεθος του παραθύρου. Την επόμενη φορά που θα το τρέξουμε θα το βρούμε στην ίδια κατάσταση που το αφήσαμε.

Το κερασάκι στην τούρτα
Μετά από τόσο κόπο πρέπει να φροντίσουμε ώστε το deltaPad να κάνει αισθητή την παρουσία του στην επιφάνεια εργασίας. Κάτι τέτοιο μπορεί να γίνει με ένα καλοσχεδιασμένο εικονίδιο! Εμείς φτιάξαμε ένα χρησιμοποιώντας τον αγαπημένο μας επεξεργαστή εικόνας (GIMP) και ένα εργαλείο επεξεργασίας εικονιδίων (Pixelformer). Όταν ετοιμάσουμε το εικονίδιό μας, καθώς και αρκετά στιγμιότυπα διαφορετικής ανάλυσης, μεταβαίνουμε στο παράθυρο Projects του SharpDevelop και κάνουμε διπλό κλικ πάνω στο Project DeltaPad. Από το μενού που εμφανίζεται επιλέγουμε το Properties κι έτσι εμφανίζεται ένα νέο παράθυρο με πολλές καρτέλες. Στην πρώτη από αυτές (Application) συναντάμε την καταχώριση Application Icon, από την οποία μπορούμε να δηλώσουμε το επιθυμητό εικονίδιο. Παρεμπιπτόντως, μια καλή πρακτική είναι να τοποθετούμε το εικονίδιό μας στον φάκελο του Project. Εάν μετά κι από αυτή τη ρύθμιση δοκιμάσουμε να μεταγλωττίσουμε το πρόγραμμά μας, με μεγάλη χαρά θα διαπιστώσουμε ότι το εκτελέσιμο αρχείο που παρήχθη έχει το δικό μας μοναδικό εικονίδιο! Ωστόσο, αν δοκιμάσουμε να τρέξουμε το πρόγραμμα, τόσο στη μπάρα με τον τίτλο της φόρμας όσο και στη γραμμή εργασιών του λειτουργικού θα εμφανίζεται το προεπιλεγμένο εικονίδιο. Για την εμφάνιση του δικού μας εικονιδίου σε αυτές τις θέσεις απαιτείται μία ακόμα ρύθμιση. Συγκεκριμένα, πρέπει να μεταβούμε στις ιδιότητες της φόρμας του προγράμματός μας (όχι στις ιδιότητες ολόκληρου του project) και να δηλώσουμε το εικονίδιό μας στην ιδιότητα Icon. Αυτό ήταν όλο!

Για να εμφανίζεται το εικονίδιο που φτιάξαμε στη γραμμή τίτλου της φόρμας και στη γραμμή εργασιών, πρέπει να το δηλώσουμε και στις ιδιότητες της φόρμας. Ίσως αυτό να μοιάζει ανόητο, αλλά έτσι μπορούμε να έχουμε ξεχωριστά εικονίδια για ξεχωριστές φόρμες, αν η εφαρμογή μας διαθέτει πάνω από μια.

Για να ολοκληρώσουμε τη συζήτηση γύρω από το εικονίδιο, πρέπει να αναφερθούμε σε ένα ακόμα ζήτημα. Δεν θα ήταν ωραία αν το εικονίδιο που σχεδιάσαμε με τόσο μεράκι εμφανιζόταν και στο παράθυρο “About” του deltaPad; Ένας εύκολος τρόπος για να υλοποιήσουμε αυτό το παράθυρο είναι με τη βοήθεια ενός MessageBox. Ωστόσο, το .NET Framework δεν προσφέρει κάποιον άμεσο τρόπο για να επιβάλλουμε την εμφάνιση ενός εικονιδίου καταμεσής ενός MessageBox. Θα μπορούσαμε να πετύχουμε κάτι τέτοιο επεκτείνοντάς την κλάση MessageBox, αλλά αυτό θα αποτελούσε πολύ μεγάλο μπελά για το τίποτα. Έτσι, αποφασίσαμε να υλοποιήσουμε το παράθυρο About δημιουργώντας μια νέα φόρμα. Πιστεύουμε ότι ο τρόπος δημιουργίας και σχεδίασης μιας φόρμας είναι γνωστός και δεν χρειάζεται κάποια περαιτέρω επεξήγηση. Σημειώστε μόνο ότι για την εμφάνιση της εικόνας χρησιμοποιήσαμε ένα στοιχείο PictureBox, για την εμφάνιση των πληροφοριών αξιοποιήσαμε τρία TextBoxes, ενώ για το κλείσιμο του παραθύρου (κλήση της μεθόδου close της ίδιας της φόρμας) χρησιμοποιήσαμε ένα Button. Επιπρόσθετα, φροντίσαμε να τροποποιήσουμε κατάλληλα τις ιδιότητες της φόρμας, ώστε να μην εμφανίζεται στη γραμμή εργασιών του συστήματος, να μην είναι δυνατή η αλλαγή του μεγέθους της και τα κουμπιά μεγιστοποίησης και ελαχιστοποίησης να είναι ανενεργά.

Ο ορισμός του εικονιδίου για το εκτελέσιμο αρχείο γίνεται από την σχετική καταχώρηση, στις ιδιότητες ολόκληρου του Project.

Τέλος, για να εμφανίζεται το δικό μας “About Box” πρέπει να τροποποιήσουμε το κώδικα της μεθόδου που χειρίζεται το συμβάν AboutToolStripMenuItemClick. Η επέμβαση που κάναμε έχει ως εξής:

void AboutToolStripMenuItemClick(object sender, EventArgs e) {
    Form about = new AboutForm();
    about.ShowDialog(this);
}

Όπως βλέπετε, δημιουργήσαμε ένα στιγμιότυπο της κλάσης AboutForm και καλέσαμε τη μέθοδο ShowDialog, ορίζοντας ως γονικό στοιχείο την κεντρική φόρμα. Η διαφορά της ShowDialog() από την Show() είναι ότι η εμφανιζόμενη φόρμα έχει την συμπεριφορά ενός πλαισίου διαλόγου και δεν επιτρέπει την μετάβασή μας στην γονική φόρμα, αν δεν κλείσουμε πρώτα το πλαίσιο διαλόγου. Σας προτρέπουμε να δοκιμάσετε και τις δύο μεθόδους για να διαπιστώσετε κι εσείς τη διαφορά. Σημειώστε ότι μπορείτε να μελετήσετε ολόκληρο τον κώδικα του ανανεωμένου deltaPad, κατεβάζοντας το σχετικό πακετάκι.

Το παράθυρο πληροφοριών της εφαρμογής εμφανίζει πλέον το εικονίδιό της, ενώ και ο τίτλος έχει επισημανθεί με μεγαλύτερη γραμματοσειρά και διαφορετικό χρώμα.

Κάπου εδώ θα σας αφήσουμε να απολαύσετε τον δικό μας text editor. Μέχρι το επόμενο τεύχος σας ευχόμαστε πολλά και δημιουργικά απογεύματα, μπροστά από το SharpDevlop και τη C#!

3 Responses to “Ανάπτυξη εφαρμογών σε C# για το .NET [μέρος 9ο]”

  1. kon | 25/10/2015 at 14:07

    Πως εισάγουμε την βιβλιοθήκη σε περιβάλλον win 7 – 64bit και sharpdev 4.4;
    Προσπαθώ είτε σαν εξωτερικό αρχείο dll είτε αναφορά ή properties αλλά δεν το βλέπει.
    Στο δικό σου πακέτο το αρχείο “Multipetros.Config.dll” το δείχνει σαν αναφορά
    και ενώ το έτρεχε, τώρα δεν το τρέχει και βγάζει την αναφορά ότι λείπει ένα using ή
    μία αναφορά συγκρότησης (στα Multipetros.Config.dll και RegistryIni) που σημαίνει ότι
    δεν εγκαταστάθηκαν στο πακέτο (δηλ. δεν μπορώ και να τα εγκαταστήσω ενώ έβαλα
    σωστά τα κλειδιά).
    Τα ίδια βγάζει και στο δικό μου πακέτο (μόλις έβαλα το using Microsoft.Win32; έσβησε
    τα άλλα μηνύματα όπως ότι λείπουν τα Multipetros, RegistryIni και RegistryKey)
    ευχαριστώ

  2. kon | 25/10/2015 at 15:53

    τελικά βρήκα κάτι οδηγίες και την εγκατέστησα την βιβλιοθήκη:

    1 Στο Add Reference πλαίσιο διαλόγου, επιλέγουμε την καρτέλα .NET Assembly Browser
    2 Επιλέγουμε το Browse κουμπί, και μετά το αρχείο που θέλουμε

    είναι σωστό; πάντως το δέχθηκε

    αν υπάρξει και άλλο πρόβλημα θα στείλω νέο μήνυμα για βοήθεια
    ευχαριστώ

  3. multiPetros | 25/10/2015 at 17:21

    Στο 4ο μέρος της σειράς μας, στο τεύχος 042 του deltahacker μπορείς να βρεις αναλυτική περιγραφή και οδηγίες για την εισαγωγή και χρήση έτοιμων βιβλιοθηκών
    http://deltahacker.gr/actsubs-csharp-p4/

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

Αρχείο δημοσιεύσεων