C# DataBinding del 2 – DataGridView, can’t live with it… can’t live without
DataBinding til enkelte tekstbokse og comboboxe er forholdsvis trivielt, så det vil jeg ikke spilde tiden med her. Der findes massevis af tutorials om emnet, og det er rimelig enkelt at få til at virke. Jeg begyndte selv at kode i C# i starten af 2007 for sjov – og da jeg stødte på DataGridView’et(DGV) var jeg noget imponeret. På ingen tid kunne jeg vise læssevis af data, overskueligt med sortering osv. 3 klik og så havde man noget der så proffesionelt ud… og så starter problemerne… KeyXxx-events virkede kun nogle gange… CellValueChanged-eventet virker overhovedet ikke som forventet, og kolonners rækkefølge ved auto-generering af kolonner… det er et mareridt.
Derfor… hvis du skal lave datatunge applikationer med tabel-oversigter, så brug DGV’s (alt andet er simpelthen for fjollet med et så kraftfuldt værktøj ved hånden), men læs den her guide først: DataGridView FAQ. Den vil spare dig for mange problemer og søvnløse nætter.
Nogle af de ting, jeg har lært:
DGV’et er bygget op af en masse celler, som hver har en indlejret kontrol, dvs. de deler den samme instans af deres type. Alle TextBox-columns deler den samme TextBox, alle ComboBox-columns deler den samme ComboBox osv. Det giver en del udfordringer – det er for eksempel her du skal fange tastatur-tryk, når DGV’et er gået i Edit-mode. Det er ikke muligt at sætte properties på DGV’et til at fange tastatur-tryk (ligesom man kan på Forms med KeyPreview) før den indlejrede kontrol. Desuden er der design-time ingen adgang til de indlejrede kontroller, fordi de først bliver instansieret run-time.
Der er nogle mere eller mindre elegante måder at komme omkring det på. Den bedste (og vanskeligste) er efter min mening at bruge EditingControlShowing-eventet. Det fyrer, når DGV’et går i edit-mode, og her er der adgang til den indlejrede kontrol. Vær opmærksom på at kontrollen ikke bliver disposed før DGV’et bliver det, hvilket vil sige, at hvis du kobler et event på TextBoxen i EditingControlShowing-eventet, vil blive koblet på ligeså mange gange, som DGV’et går edit-mode… hvilket sjældent er hvad man har lyst til. Derfor skal man som minimum fjerne evt. EventHandlers i EditingControlShowing eventet før man kobler dem på, for at undgå det samme event fyrer mere end en gang. Derudover skal man overveje om man har en anden vej til samme funktionalitet, da man risikerer at Eventet, der er lagt på, bliver fyret på det forkerte tidspunkt (fx i en helt anden celle end det var tiltænkt).
Locations i et DGV… er noget rod, men igen er der en workaround. Anvend GetCellDisplayRectangle() på DGV’et – den tager et kollonne- og rækkeindeks som parametre og returnerer rektanglet for cellen. Der er nogle der foreslår at man ‘bare’ skal tælle antallet af celler til venstre og ovenfor… Der er et par problemer ved den fremgangsmåde. 1) Du bliver nødt til at iterere over alle cellerne for at finde deres eksakte bredde/højde, da de kan være forskellige fra celle til celle – og 2) ikke mindst skal du tage højde for om brugeren har scrollet, så de første par rækker/kolonner måske ikke er synlige. Det er alt for mange problemer ved den fremgangsmåde (for ikke at tale om en gang klyt-kode, der skal til), så jeg vil klart foretrække at anvende den indbyggede funktion til det.
Hvis du overhovedet har mulighed for det, så anvend BindingSource’s som datakilde. Der er så mange ting, man får forærende, når man bruger dem (de implementerer IBindingListView mfl. så det er ikke en undskyldning). Koden bliver pænere, du kan run-time få let adgang til dine forretningsobjekter gennem .CurrentObject. Derudover kan du koble mange forskellige objekter til den samme BindingSource, så du ikke skal bekymre dig om at synkronisere skidtet. Især til Master/Detail problematikker er det suverænt. Du kan centralisere koden for opdatering/validering af data.
Brug Parse/Format-events, hvis overhovedet muligt til at lave konverteringen fra String til forretningsobjekt og tilbage igen. Ellers får du problemer med nogle grimme fejlbeskeder run-time. Og at skulle fange dem efter exceptionen er sket, er i mine øjne for åndsvagt, når man nu har muligheden for at lave noget pænt i de to events.
Minimer brugen af CellPaint – der er nogle uhensigtsmæssigheder, som du skal løse for at undgå at DGV’et først tegner med den indbyggede for derefter at tegne med dit fine layout. Brug istedet CustomRender objekter. De kan løse de fleste problemstillinger, og har ikke samme antal problemer.
DGV’et er et monster af kode og en meget sofistikeret kontrol – men når man har fundet sig i dens … mærkeligheder… er den rigtig rar at arbejde med og ikke at glemme… intuitiv for brugeren (den opfører sig jo næsten bedre end indbakken i Outlook…).
nckjox said,
October 1, 2008 at 16:50
DataGridView FAQ har skiftet placering til http://windowsclient.net/samples/Go%20To%20Market/Data%20Binding/DataBinding%20FAQ.doc
GoblinHero said,
October 4, 2008 at 14:42
Tak. Linket er nu rettet – hvor ville det være fantastisk hvis MS kunne finde ud af at lade det gamle link redirecte til den nye placering…