Έχετε φτιάξει ένα φύλλο ακριβώς όπως πρέπει. Η ζώνη κεφαλίδας έχει συγχωνευθεί, τα πλάτη των στηλών ταιριάζουν στα δεδομένα, οι δύο πρώτες σειρές είναι παγωμένες, η περιοχή εκτύπωσης και τα περιθώρια έχουν ρυθμιστεί για καθαρή εξαγωγή A4, και η καρτέλα έχει χρώμα ώστε να τη βρίσκει εύκολα το οικονομικό τμήμα. Τώρα η αναφορά χρειάζεται δώδεκα τέτοια, ένα για κάθε περιοχή, όλα να ξεκινούν από την ίδια διάταξη. Το να ξαναχτίζετε αυτό το φύλλο στον κώδικα δώδεκα φορές είναι ο πιο σίγουρος τρόπος να τρυπώσει ανεπαίσθητη απόκλιση: η περιοχή 7 παίρνει μια στήλη κατά μία μονάδα στενότερη, η περιοχή 11 χάνει το πάγωμα, και κανείς δεν το προσέχει μέχρι να φτάσει το PDF στο γραφείο ενός διευθυντή. Αυτό που πραγματικά θέλετε είναι η προγραμματική εκδοχή του δεξιού κλικ στο Excel, Move or Copy, Create a copy: πάρτε το τελικό φύλλο και παράγετε ανεξάρτητα αντίγραφα
Η μηχανή XLSX στο HotXLS, μια εγγενής βιβλιοθήκη Delphi και C++Builder που διαβάζει και γράφει αρχεία Excel χωρίς να ελέγχει το ίδιο το Excel, μπορούσε ήδη να μετακινεί φύλλα, να διαγράφει φύλλα και να αντιγράφει περιοχές κελιών ανάμεσα σε φύλλα. Αυτό που δεν μπορούσε να κάνει μέχρι τη v2.91.0 ήταν να κλωνοποιήσει ολόκληρο φύλλο εργασίας με μία κλήση. Αυτή η έκδοση προσθέτει δύο σημεία εισόδου: TXLSXWorksheet.CopyFromCopyFromTXLSXSheets.DuplicateDuplicateCopyFromCopyFrom
Μία κλήση για κλωνοποίηση ενός τελικού φύλλου
Η λειτουργία υψηλού επιπέδου είναι DuplicateDuplicateItems[] στη μεριά του XLSX, οπότε το φύλλο ένα είναι δείκτης 1100nil αντί για εξαίρεση, το ίδιο συμβόλαιο αποτυχίας που χρησιμοποιεί και η υπόλοιπη συλλογή φύλλων XLSX
var
Book: TXLSXWorkbook;
Template, Copy: TXLSXWorksheet;
begin
Book := TXLSXWorkbook.Create;
try
Template := Book.Sheets.Add('Template');
Template.Cells[1, 1].Value := 'Quarterly Statement';
Template.Range['A1:C1'].Merge;
Template.ColWidth[1] := 18;
Template.FreezePanes(2, 1); // freeze top row + first column
Template.TabColorIsAuto := False;
Template.TabColor := $FF1F4E79;
// Clone with an explicit name...
Copy := Book.Sheets.Duplicate(1, 'Region-North');
// ...or let it pick the Excel-style default name.
Copy := Book.Sheets.Duplicate(1); // -> "Template (2)"
Book.SaveAs('regions.xlsx');
finally
Book.Free;
end;
end;
Δύο πράγματα σε αυτό το απόσπασμα αξίζουν να τα δούμε πιο αργά. Πρώτον, FreezePanesCopyFromFreezePanes(ARow, ACol) παίρνει τα ορίσματά του με σειρά γραμμής πρώτα, Cells[Row, Col], ώστε να ευθυγραμμίζεται με τη δεικτοδότηση 1-based· το αντίγραφο κληρονομεί ακριβώς το ίδιο σημείο διαχωρισμού του παγώματος. Δεύτερον, η μέθοδος ονομάζεται Duplicate και όχι το πιο προφανές Copy, και αυτό δεν είναι θέμα ύφους. Copy είναι μια τυπική ρουτίνα στη μονάδα System, που χρησιμοποιείται συνεχώς για συμβολοσειρές και δυναμικούς πίνακες. Μια μέθοδος με όνομα Copy σε κλάση θα την σκέπαζε μέσα στα σώματα των μεθόδων και θα δημιουργούσε ακριβώς την ασάφεια ανάλυσης που σας χτυπά έξι μήνες αργότερα. Duplicate παρακάμπτει όλο το πρόβλημα και διαβάζεται σωστά στο σημείο κλήσης
Το προεπιλεγμένο όνομα ακολουθεί τον ίδιο κανόνα του Excel
Όταν καλείτε την υπερφόρτωση με ένα όρισμα ή περνάτε μια κενή συμβολοσειρά ονόματος, το νέο φύλλο ονομάζεται με βάση το αρχικό και παίρνει ένα επίθεμα (2), και το επίθεμα αυξάνεται μέχρι το όνομα να γίνει μοναδικό. Αντιγράψτε το φύλλο Template μία φορά και παίρνετε Template (2); αντιγράψτε το ξανά και παίρνετε Template (3), επειδή το Template (2) είναι ήδη δεσμευμένο. Αυτό αντικατοπτρίζει τα ονόματα που δημιουργεί το Excel από τη δική του εντολή Create a copy, έτσι ώστε ένα βιβλίο εργασίας που παράγει ο κώδικάς σας να μοιάζει όπως θα περίμενε ο χρήστης από ένα χειροκίνητα διπλασιασμένο. Ο έλεγχος μοναδικότητας γίνεται πάνω στην ενεργή συλλογή φύλλων, πράγμα που σημαίνει ότι περνά και πάνω από ονόματα που δημιουργήσατε χειροκίνητα, όχι μόνο από όσα προέκυψαν από παλαιότερες αντιγραφές
Αν δημιουργείτε ένα φύλλο ανά περιοχή ή ανά μήνα, προτιμήστε καλύτερα την υπερφόρτωση με ρητό όνομα. Ένα προβλέψιμο Region-North, Region-South σχήμα είναι πιο εύκολο να το χειριστείτε αργότερα από μια σειρά (2), (3) επιθεμάτων, και κρατά ευανάγνωστα τα ονόματα που ορίζετε και τους τύπους μεταξύ φύλλων
Τι αντιγράφει σε βάθος το CopyFrom
Στο παρασκήνιο, Duplicate προσθέτει το φύλλο και μετά καλεί CopyFrom(ASource), το οποίο μπορείτε επίσης να καλέσετε απευθείας όταν θέλετε να κλωνοποιήσετε σε ένα φύλλο που έχετε ήδη δημιουργήσει. CopyFrom προστατεύει εξαρχής από τις δύο ακραίες περιπτώσεις: η αντιγραφή από nil, ή η αντιγραφή ενός φύλλου πάνω στον εαυτό του, επιστρέφουν αμέσως και δεν κάνουν τίποτα. Ό,τι ακολουθεί είναι το ίδιο το αντίγραφο, και είναι σκόπιμα ευρύ
Πρώτα έρχονται τα δεδομένα των κελιών. CopyFrom ζητά από το αρχικό φύλλο το UsedRange, το στενό πλαίσιο οριοθέτησης των συμπληρωμένων κελιών και των συγχωνευμένων περιοχών, και επαναχρησιμοποιεί τον υπάρχοντα CopyRangeTo μηχανισμό για να μεταφέρει κάθε τιμή, τύπο και δείκτη στυλ ανά κελί στο στόχο, ξεκινώντας από το A1. Πάνω από τα κελιά, αναπαράγει ολόκληρο το επίπεδο κατάστασης σε επίπεδο φύλλου που κάνει ένα πρότυπο να δείχνει ολοκληρωμένο:
- Συγχωνευμένα εύρη, αναδημιουργημένα βάσει συντεταγμένων ώστε το banner να καταλαμβάνει το ίδιο ορθογώνιο
- Πλάτη στηλών και ύψη γραμμών, μαζί με τις λίστες κρυφών, συμπτυγμένων και επιπέδων περιγράμματος, αντιγραμμένα αυτούσια ώστε οι μη προεπιλεγμένες γραμμές και στήλες να ευθυγραμμίζονται ακριβώς
- Πάγωμα περιοχών και η κατάσταση προβολής: επίπεδο ζουμ, εμφάνιση πλέγματος και μηδενικών τιμών, κατεύθυνση από δεξιά προς τα αριστερά και ο τύπος προβολής
- Κατάσταση προστασίας με τις επιλογές δικαιωμάτων ανά ενέργεια, ώστε ένα κλειδωμένο πρότυπο να παραμένει κλειδωμένο με τον ίδιο τρόπο
- Ολόκληρο το μπλοκ ρύθμισης σελίδας: περιθώρια, προσανατολισμός, μέγεθος χαρτιού, κλίμακα και προσαρμογή στη σελίδα, περιοχή εκτύπωσης, τίτλοι εκτύπωσης, κεφαλίδες και υποσέλιδα, καθώς και οι σημαίες εκτύπωσης γραμμών πλέγματος και επικεφαλίδων
- Το εύρος AutoFilter, το χρώμα της καρτέλας και η ορατότητα του φύλλου
Το αποτέλεσμα είναι ένα φύλλο που εκτυπώνεται, φιλτράρεται και παρουσιάζεται ταυτόσημα με το αρχικό του. Και επειδή τα κελιά, οι συγχωνεύσεις και οι λίστες διαστάσεων αναδημιουργούνται φυσικά στο νέο φύλλο αντί να παραπέμπουν στο ίδιο αντικείμενο, το αντίγραφο είναι πλήρως ανεξάρτητο. Γράψτε 999 σε ένα κελί του αντιγράφου και το αρχικό διατηρεί την αρχική του τιμή· αυτή η ανεξαρτησία είναι η σημαντικότερη ιδιότητα ενός κλώνου που προορίζεται για παράλληλες περιφερειακές αναφορές, και το παρεχόμενο SheetCopy demo το επιβεβαιώνει ρητά
Τι αφήνει επιφανειακά, και γιατί
Τώρα το ειλικρινές κομμάτι. Τα γραφήματα, οι ενσωματωμένες εικόνες, οι πίνακες XLSX, οι επικυρώσεις δεδομένων και οι κανόνες υπό όρους μορφοποίησης δεν αντιγράφονται. Πρόκειται για τεκμηριωμένο, σκόπιμο όριο και όχι για παράλειψη, και αξίζει να κατανοήσετε το σκεπτικό ώστε να μπορείτε να προγραμματίσετε γύρω από αυτό αντί να εκπλαγείτε
Κάθε μία από αυτές τις συλλογές κουβαλά ταυτότητα και αναφορές που δεν επιβιώνουν σε μια αφελή αντιγραφή πεδίου. Ένα γράφημα δείχνει σε ένα εύρος δεδομένων προέλευσης και διαθέτει μια σχέση σχεδίασης στο πακέτο OOXML· η κλωνοποίηση του αντικειμένου χωρίς να γίνει αντιστοίχιση της σχέσης και των αναφορών των σειρών παράγει ένα γράφημα που αποδίδει με λάθος δεδομένα ή ένα πακέτο που το Excel επισημαίνει ότι χρειάζεται επιδιόρθωση. Ένας πίνακας έχει όνομα που πρέπει να είναι μοναδικό μέσα στο workbook, μια γραμμή κεφαλίδας δεμένη με συγκεκριμένες στήλες και τη δική του αυτόματα παραγόμενη σχέση. Οι μορφοποιήσεις υπό όρους και οι επικυρώσεις δεδομένων συνδέονται με εύρη συντεταγμένων και, στην περίπτωση της επικύρωσης, μπορούν να αναφέρονται σε άλλα εύρη με τύπο. Η σωστή βαθιά αντιγραφή οποιουδήποτε από αυτά σημαίνει επανεγγραφή αναφορών και δημιουργία νέων ταυτοτήτων, κάτι που είναι πραγματική δουλειά με πραγματικές καταστάσεις αποτυχίας. Το να το κάνεις μισό, αντιγράφοντας το αντικείμενο αλλά όχι τις αναφορές του, είναι χειρότερο από το να μην το αντιγράψεις καθόλου: καταλήγει σε ένα αρχείο που ανοίγει με μήνυμα επιδιόρθωσης και απορρίπτει σιωπηλά περιεχόμενο. Έτσι η μηχανή αντιγράφει όσα μπορεί να αντιγράψει καθαρά και αφήνει τις συλλογές που φέρουν αναφορές στον καλούντα, ο οποίος ξέρει προς τι πρέπει να δείχνει ο στόχος
Στην πράξη αυτό σημαίνει ότι η ροή εργασίας για ένα πιο πλούσιο πρότυπο είναι η εξής: αντιγράψτε το φύλλο για να πάρετε τα κελιά, τη διάταξη και τη ρύθμιση εκτύπωσης, και μετά ξαναχτίστε το γράφημα, τον πίνακα, τις επικυρώσεις ή τις μορφοποιήσεις υπό όρους στο αντίγραφο με το ίδιο API που χρησιμοποιήσατε για να τα δημιουργήσετε την πρώτη φορά. Επειδή τα αναδημιουργείτε πάνω στα δικά του εύρη, οι αναφορές βγαίνουν σωστές εκ κατασκευής. Για ένα γράφημα που διαβάζει A1:C10, προσθέστε ένα νέο γράφημα στο αντίγραφο που δείχνει στο A1:C10; για ένα AutoFilter που θέλετε ενεργό, σημειώστε ότι το φίλτρο εύρος μεταφέρεται, οπότε επανεφαρμόζετε μόνο τα κριτήρια στηλών. Οι κανόνες υπό όρους μορφοποίησης και επικύρωσης δεδομένων που θα ξαναπροσθέτατε μέσω των ίδιων κλήσεων που περιγράφονται στο άρθρο για τα συγχωνευμένα κελιά και τη διάταξη προτύπου αναφοράς, το οποίο διατρέχει τον πίνακα συγχωνεύσεων και το μοντέλο εύρους που κληρονομεί το αντίγραφο
Πού ταιριάζει η αντιγραφή σε μια ροή αναφορών
Η αντιγραφή φύλλου εργασίας είναι ο φυσικός σύντροφος της παραγωγής με βάση placeholders. Η προσέγγιση με αγκύρωση σε token στο οδηγό για τη δημιουργία αναφορών με βάση πρότυπα στο Delphi λύνει το πρόβλημα της εγγραφής δεδομένων σε μια διάταξη που επεξεργάζονται και άλλοι· η αντιγραφή λύνει το πρόβλημα της ανάγκης για την ίδια διάταξη πολλές φορές μέσα σε ένα workbook. Συνδυάστε τα και το μοτίβο είναι καθαρό: κρατήστε ένα καθαρό Template φύλλο με τα tokens, τις συγχωνεύσεις και τη ρύθμιση εκτύπωσης, και μετά για κάθε περιοχή ή περίοδο καλέστε Duplicate, γεμίστε τα tokens του κλώνου με εκείνο το τμήμα δεδομένων και προχωρήστε. Το καθαρό πρότυπο δεν αλλοιώνεται ποτέ, άρα παραμένει αξιόπιστη πηγή για τον επόμενο κλώνο, και κάθε φύλλο εξόδου ξεκινά από διάταξη πανομοιότυπη byte προς byte
Μια σημείωση σειράς ενεργειών γλιτώνει μια ολόκληρη κατηγορία σύγχυσης. Αντιγράψτε το φύλλο πριν πριν ρίξετε δεδομένα σε αυτό, όχι μετά. Ένα πρότυπο πρέπει να κρατά δομή και μορφοποίηση, όχι τους αριθμούς του προηγούμενου τριμήνου, και η κλωνοποίηση ενός κενού φύλλου με στυλ σημαίνει ότι κάθε αντίγραφο ξεκινά καθαρό. Αν αντιγράψετε ένα φύλλο που ήδη περιέχει δεδομένα, αυτά τα δεδομένα θα μεταφερθούν μαζί, επειδή CopyFrom αντιγράφει πιστά το χρησιμοποιημένο εύρος· αυτό είναι μερικές φορές ό,τι θέλετε, αλλά για μια αναφορά μαζικής διανομής συνήθως δεν είναι
Μια γρήγορη συνήθεια επαλήθευσης
Επειδή η διάκριση ανάμεσα σε deep-copy και shallow-copy είναι αόρατη μέχρι να τη ψάξετε, ενσωματώστε έναν έλεγχο πέντε γραμμών στη διαδικασία αντί να εμπιστεύεστε ότι πέρασαν όλα σωστά. Μετά την αντιγραφή, διαβάστε ξανά τα δομικά στοιχεία που υποτίθεται ότι κληρονομεί το αντίγραφο και ελέγξτε ότι ταιριάζουν με το αρχικό
Copy := Book.Sheets.Duplicate(1, 'Region-North');
WriteLn(Format('merged=%d colA=%.1f freezeRow=%d tabAuto=%d',
[Copy.MergedCells.Count, Copy.ColWidth[1],
Copy.FreezeRow, Integer(Copy.TabColorIsAuto)]));
// Prove independence: mutate the copy, confirm the source is untouched.
Copy.Cells[2, 2].Value := 999;
// Template.Cells[2, 2].Value is still whatever it was.
Ο αριθμός συγχωνεύσεων, το πλάτος μιας στήλης, η γραμμή παγώματος και η ένδειξη χρώματος καρτέλας σας λένε ότι το επίπεδο που έχει αντιγραφεί πέρασε πράγματι. Ξεχωριστά, σε κάθε φύλλο που περιείχε γράφημα, πίνακα, επικυρώσεις ή μορφοποιήσεις υπό όρους, αντιμετωπίστε τα ως λίστα επαναδημιουργίας στο αντίγραφο: η απουσία τους είναι σκόπιμη και η διόρθωση είναι λίγες κλήσεις, όχι αναφορά σφάλματος. Αυτό το νοητικό μοντέλο, βαθιά όπου είναι ασφαλές και ρηχά όπου οι αναφορές θα έσπαγαν, είναι όλη η ουσία του πώς να χρησιμοποιείτε σωστά αυτή τη δυνατότητα
Η αντιγραφή φύλλου εργασίας και η CopyFrom αντιγραφή κατάστασης φύλλου που περιγράφονται εδώ κυκλοφορούν στην έκδοση v2.91.0 του εγγενούς HotXLS Delphi spreadsheet component, μαζί με ένα εκτελέσιμο SheetCopy παράδειγμα που δοκιμάζει τον κύκλο αντιγραφής και τροποποίησης από άκρο σε άκρο