BETA
Aby się zalogować, najpiew wybierz portal.
Aby się zarejestrować, najpiew wybierz portal.
Podaj słowa kluczowe
Słowa kluczowe muszą mieć co najmniej 3 sąsiadujące znaki alfanumeryczne
Pole zawiera niedozwolone znaki

Baza wiedzy











Konwersja „w locie” danych prezentowanych w kontrolce DataGrid

04-04-2004 13:18 | saper4116
Styl DataGrid’a konwertujący prezentowane dane w locie

Potrzeba matką wynalazków

Dostałem zadanie napisania aplikacji okienkowej, która miałaby korzystać z bazy danych aplikacji dosowej, tak by obie działały bez zarzutu. Problem wydawał się łatwy do momentu, gdy wyświetliłem dane z bazy w kontrolce DataGrid. Zamiast wymarzonych tekstów pokazały mi się krzaczki. Jak się okazało aplikacja dosowa kodowała tekst za pomocą standardu Mazowia. Danych było dość dużo więc konwersja otrzymanych danych przed ich wyświetleniem nie wchodziła w grę. Dodatkowo takie podejście mogłoby naruszyć więzy integralności, gdyby prezentowany DataTable był częścią większego DataSet’a, nie mówiąc już o narzutach czasowych przy konwersji kilkudziesięciu tysięcy napisów. Pomysł był więc prosty. Konwersji dokonywać jedynie na napisach aktualnie prezentowanych na ekranie, bez fizycznej ingerencji w dane w DataTable.

Nowy styl kolumny

Styl do prezentacji napisów istnieje standardowo. Wystarczy więc z niego dziedziczyć i dokonać drobnych modyfikacji

public class DataGridConvertingTextBoxColumn:DataGridTextBoxColumn

Konieczne są modyfikacje pięciu funkcji edycyjnych oraz funkcji Paint() i PaintText()

Do konwersji tekstu użyć można mechanizmu delegowania. Tworzymy więc delegata kodującego i dekodującego oraz specyfikację delegatów.

/// <summary>
/// Delegat, kodujący string wejściowy do nieznanego standardu
/// </summary>
public delegate string Koduj(string szWejscie);
/// <summary>
/// Funkcja delegowana do kodowania na nieznany standard
/// </summary>
public Koduj DelegatKodujacy;
     
/// <summary>
/// Delegat, dekodujący string wejściowy z nieznanego standardu
/// </summary>
public delegate string Dekoduj(string szWejscie);
/// <summary>
/// funkcja delegowana do dekodowania z nieznanego standardu
/// </summary>
public Dekoduj DelegatDekodujacy;

Funkcje prezentacji danych - rysowanie

Następnie funkcje rysujące tekst. Wystarczy podmienić tekst rysowany przez te funkcje na tekst przekonwertowany przez naszą funkcję dekodującą i wywołać odpowiednią funkcję z klasy bazowej.

/// <summary>
/// Przeciążona funkcja rysująca tekst
/// </summary>
protected new void PaintText(
Graphics g, Rectangle r, string s, bool b)
{
     if(DelegatDekodujacy!=null)
          base.PaintText(g, r, DelegatDekodujacy(s), b);
     else
          base.PaintText(g, r, s, b);
}
/// <summary>
/// Przeciążona funkcja rysująca tekst
/// </summary>
protected new void PaintText(
Graphics g, Rectangle r, string s, Brush b1, Brush b2, bool b)
{
     if(DelegatDekodujacy!=null)
          base.PaintText(g, r, DelegatDekodujacy(s), b1, b2, b);
     else
          base.PaintText(g, r, s, b1, b2, b);
}

Następnie funkcje rysujące całą komórkę. Ponieważ wywoływane są nasze funkcje, możemy wyrysować w nich, co tylko zechcemy. My chcemy wyrysować przekonwertowany tekst. Dzięki parametrom z jakimi są wywoływane metody rysujące możemy dostać się do mającego zostać wyrysowanego tekstu i wywołać nasze funkcje rysujące tekst przekonwertowany.

/// <summary>
/// Przeciązona funkcja rysująca TextBoxa
/// </summary>
protected override void Paint(
Graphics g,Rectangle Bounds,CurrencyManager Source,int RowNum)
{
     Paint(g, Bounds, Source, RowNum, false);
}
     
/// <summary>
/// Przeciązona funkcja rysująca TextBoxa
/// </summary>
protected override void Paint(
Graphics g,Rectangle Bounds,CurrencyManager Source,
int RowNum,bool AlignToRight)
{
     string Text = GetColumnValueAtRow(Source, RowNum).ToString();
     PaintText(g, Bounds, Text, AlignToRight);
}
          
/// <summary>
/// Przeciązona funkcja rysująca TextBoxa
/// </summary>
protected override void Paint(
Graphics g,Rectangle Bounds,CurrencyManager Source,
int RowNum,Brush BackBrush ,Brush ForeBrush ,bool AlignToRight)
{
     string Text = GetColumnValueAtRow(Source, RowNum).ToString();
     PaintText(g, Bounds, Text, BackBrush, ForeBrush, AlignToRight);
}

Tak przygotowane funkcje zapewniają prezentację danych po ich uprzednim przekonwertowaniu. Problem pojawia się jednak, gdy przechodzimy do edycji komórki. Uruchamiane są wtedy funkcje edycyjne nijak nie powiązane z tymi, które właśnie stworzyliśmy.

Edycja komórki – funkcje trybu edycji

Potrzebne nam będą dwie zmienne prywatne. Jedna, mówiąca o tym, czy komórka znajduje się w trybie edycji i druga, przechowująca wartość przed edycją

/// <summary>
/// Prywatna zmienna wskazująca na to, czy znajduje się w trybie edycji
/// </summary>
private bool InEdit=false;
/// <summary>
/// prywatna zmienna. Przechowuje starą zmienna podczas edycji
/// </summary>
private string OldVal=new string(string.Empty.ToCharArray());

Zacznijmy więc od początku. W momencie wejścia do komórki wywoływana jest funkcja Edit(). Najpierw musimy pobrać z TextBox’a starą wartość, ustawić zmienną edycji na true a następnie podmienić prezentowany w trybie edycji tekst.

/// <summary>
/// Funkcja przygotowuje DataGrida do edycji
/// </summary>
/// <param name="Source">Źródło danych</param>
/// <param name="Rownum">numer wiersza</param>
/// <param name="Bounds">Granice rysunku</param>
/// <param name="ReadOnly">Czy pole jest ReadOnly</param>
/// <param name="InstantText">tekst mający się pojawić</param>
/// <param name="CellIsVisible">Czy komórka jest widoczna</param>
protected override void Edit(
CurrencyManager Source ,int Rownum,Rectangle Bounds,
bool ReadOnly,string InstantText, bool CellIsVisible)
{
     if(!this.ReadOnly)          
     {
          Rectangle OriginalBounds = Bounds;
          OldVal = TextBox.Text;
          if(CellIsVisible)               //ustawienie wielkości
          {
               Bounds.Offset(xMargin, yMargin);
               Bounds.Width -= xMargin * 2;
               Bounds.Height -= yMargin;
               TextBox.Bounds = Bounds;
               TextBox.Visible = true;
          }
          else
          {
               TextBox.Bounds = OriginalBounds;
               TextBox.Visible = false;
          }
          if(DelegatDekodujacy!=null)
               TextBox.Text =
                    DelegatDekodujacy(GetColumnValueAtRow(
                    Source, Rownum).ToString());
          else
               TextBox.Text =
                    GetColumnValueAtRow(
                    Source, Rownum).ToString();
          InEdit = true;
          TextBox.Select();
          TextBox.SelectAll();          
     }
               
}

Następnie funkcję kończenia edycji z zatwierdzeniem zmian i ich wycofaniem. Tu z pomocą przychodzi delegat kodujący, który do naszego DataTable zapisze tekst odpowiednio zakodowany w przypadku zatwierdzenia zmian i tekst stary (ten pobrany podczas wywołania funkcji Edit() na początku edycji). Są to odpowiednio funkcje Commit() i Rollback().

/// <summary>
/// Funkcja zatwierdzająca edycję TextBoxa
/// </summary>
/// <param name="DataSource">żródło danych</param>
/// <param name="RowNum">Numer wiersza</param>
/// <returns>Czy operacja się udała</returns>
protected override bool Commit(CurrencyManager DataSource,int RowNum)
{
     this.HideEditBox();
     if(!InEdit)
     {
          return true;
     }
     try
     {
          string Value = DelegatKodujacy(TextBox.Text);
          if(NullText.Equals(Value))
          {
               Value = System.Convert.DBNull.ToString();
          }
          SetColumnValueAtRow(DataSource, RowNum, Value);
     }
     catch
     {
          this.RollBack();
          return false;     
     }
               
     this.EndEdit();
     return true;
}
/// <summary>
/// cofnij zmiany w TextBoxie
/// </summary>
private void RollBack()
{
     InEdit=false;
     TextBox.Text = OldVal;
     Invalidate();
}

Warto zauważyć, że w przypadku, gdy wpisany tekst w trybie edycji nie odpowiada naszym wymogom, na przykład nie jest poprawną wartością, nasza funkcja kodująca może rzucić wyjątek co spowoduje powrót do poprzedniej wartości. Jeżeli zaś konwersja się uda, do naszego DataTable wpisana zostanie wartość już zakodowana.

Na koniec funkcja kończąca edycję EndEdit()

/// <summary>
/// Zakończ edycję TextBoxa niezależnie od wyniku
/// </summary>
public new void EndEdit()
{
     InEdit = false;
     Invalidate();
}

Jak widać w prezentowanym przez nas DataTable (lub innym źródle danych) nigdy nie dokonała się jakakolwiek fizyczna konwersja danych. Całość dokonywana jest jedynie na poziomie prezentacji danych. Staje się to nad wyraz pomocne przy prezentacji danych, które z jakiegoś powodu nie mogą lub nie powinny być prezentowane w sposób, w jaki są przechowywane. Przedefiniowane funkcje edycji zaś pozwalają na korzystanie z wielu udogodnień ADO.NET w sposób naturalny.

Załączniki:

Podobne artykuły

Komentarze 9

falowski3781
falowski3781
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
-
testa1435
testa1435
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Nawet komentarze w kodzie nie zostały przetłumaczone
User 79130
User 79130
9 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Dla mnie artykuł mało przydatny. Jedyna ciekawa rzecz to zwrócenie uwagi na możliwość ingerencji w DataGrid. Reszta do niczego, zwłaszcza styl artykułu. Czasami autor używa zbyt daleko idących skrótów myślowych i zakłada że czytelnik większość rzeczy już wie (to po co pisać artykuł?). Warto by było napisać jak działa DataGrid od środka, jak renderuje tabelkę i jakie metody są z tym związane. Tak to nie wiadomo za bardzo o co chodzi. No i jeszcze jedna rzecz - w c# są metody a nie funkcje. :)
User 108709
User 108709
1 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
No - pomysł może i dobry - ale tekstu by się trochę przydało...
User 79623
User 79623
1 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
-
kasprzol
kasprzol
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
artykuł mierny
User 79089
User 79089
0 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Trochę to przypomina sprawozdanie z ćwiczenia
User 79314
User 79314
44 pkt.
Poczatkujacy
21-01-2010
oceń pozytywnie 0
Czuje sie, jakbym przeczytal plik naglowkowy ze starego poczciwego C++ - MazoviaConverter.h ;)
User 79209
User 79209
2 pkt.
Nowicjusz
21-01-2010
oceń pozytywnie 0
Artykul porusza bardzo silne narzedzie sluzace do konfiguracji DataGrida. Jednak autor powinnien zaznaczyc, ze artykul jest skierowany do zaawansowanych uzytkownkow ADO.NET. W sytuacji braku tego "ostrzezenia" ;) artykul powinien zawierac 3 razy wiecej tekstu :).
A tak BTW to ocenianie artykulu na 1 lub 2 punkty powinno byc poparte komentarzem, bo inaczej nie jest to zagranie fair...
pkt.

Zaloguj się lub Zarejestruj się aby wykonać tę czynność.