Autor Téma: DrawText na SpeedButton tlačítku občas nedokreslí text  (Přečteno 2984 krát)

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Dobrý den,

prosím o radu na co bych se měl zaměřit u Windows.DrawText funkce u tlačítka převzatého a upraveného ze SpeedButtonu (grafické rozšíření - zaoblené rohy, hezčí vzhled). Může něco v Delphi / Windows způsobit, aby 1000x vykreslené tlačítko zničeho nic při opětovném překreslení nedokreslilo "Caption" text? Příklad:

Tlačítko bTlacitko.Caption := 'Potvrdit';
Náhodně (ale velmi zřídka) se při vstupu do menu nedokreslí text a zůstane třeba jen toto: Potv
Opětovné opuštění a návrat do menu (nebo jen změna stavu tlačítka) chybu opraví.

V projektu máme stovky tlačítek a chyba se objevuje náhodně (cca 1 denně). Nerozhoduje žádný "properties" parametr, nezáleží na tom zda-li je to group tlačítko, aktivní textwrap nebo má glyph. 

Zkoušel jsem kontrolovat snad vše (zda-li je vstupní text v pořádku, definované místo pro kreslení, barvy atd.). Ani zdvojování kreslení nepomohlo.

Napadá někoho něco, co by to mohlo způsobit?

Díky za rady.
AN

Offline jarex

  • Plnoletý
  • ***
  • Příspěvků: 225
  • Karma: 5
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #1 kdy: 07-11-2016, 15:53:08 »
Nebyl by k dispozici kód, který dělá to kreslení textu? Nějak si nedokážu představit, že by to vynechávalo vždy na celé znaky. Je tam např. vždy dost velké plátno? Nebo se to kreslí pod nějakou další vrstvu?....  Chtělo by to kód.
D2007 Professional

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #2 kdy: 07-11-2016, 15:57:32 »
Kód pro kreslení SpeedButtonu je obsáhlejší. Zkusím zítra sem na fórum vykopírovat kreslící proceduru. Jen mě zaráží, že jedno a to samé tlačítko to vykreslí xxx-krát v pořádku a jednou ne. Proto se domnívám, že chyba není v kódu... ale už opravdu nevím.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #3 kdy: 07-11-2016, 16:07:44 »
Kód pro kreslení SpeedButtonu je obsáhlejší. Zkusím zítra sem na fórum vykopírovat kreslící proceduru. Jen mě zaráží, že jedno a to samé tlačítko to vykreslí xxx-krát v pořádku a jednou ne. Proto se domnívám, že chyba není v kódu... ale už opravdu nevím.
A co nejake ProcessMessages(), nenaslo by se?

Offline JaroB

  • Guru
  • *****
  • Příspěvků: 1066
  • Karma: 29
    • Verze Delphi: XE8, Sydney
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #4 kdy: 07-11-2016, 21:20:41 »
Otázka je, jestli je to prosté Draw() na Canvas z backbufferu např. předpřipravené bitmapy (to by mělo zafungovat bez problémů, je to blit), nebo je to vykreslované po částech (bývá problém tansparentních částí a v kreslení detailů, protože to je kreslené napřímo na Canvas) nebo je to pod Themes a je to vykreslované elementem přes StyleServices nebo ThemeServices (tam je kapku záludnost v pozadí)?

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #5 kdy: 08-11-2016, 07:18:51 »
ProcessMessages v kódu komponenty není. Níže je kód v starající se o napozicování glyphu a vytvoření plochy pro kreslení textu. O kreslení textu se stará Windows.DrawText funkce. Tato část kódu je úplně na konci procedury pro kreslení tlačítka. Před ní je část kódu starající se o vzhled - kreslení výplně, borderu, zaoblení rohů atd. To vždy funguje bez problémů a proto to sem nevkládám.

Kód je poměrně jednoduchý a srozumitelný.

Napadá někoho něco?

Kód: Delphi [Vybrat]
  1.   ...
  2.   ...
  3.  
  4.   case Alignment of
  5.       taLeftJustify:  dtMode := DT_LEFT or DT_WORDBREAK;
  6.       taRightJustify: dtMode := DT_RIGHT or DT_WORDBREAK;
  7.       taCenter:       dtMode := DT_CENTER; // or DT_WORDBREAK;
  8.    end;
  9.    
  10.    if not Enabled then Canvas.Font.Color := clGray;
  11.  
  12.    if Style = bsShape then
  13.    begin
  14.  
  15.        ... vymazáno jelikož tlačítka nejsou bsShape
  16.  
  17.    end
  18.    else
  19.    begin
  20.       if (Layout in [blGlyphLeft, blGlyphRight]) and (NumGlyphs > 0) then
  21.       begin
  22.          iCaptionWidth := Width - 8; if iGlyphWidth > 0 then dec(iCaptionWidth, iGlyphWidth + 4);
  23.          if iCaptionWidth < 0 then iCaptionWidth := 0;
  24.          if not WordWrap then while Canvas.TextWidth(Caption) > iCaptionWidth do Caption := copy(Caption, 1, length(Caption)-1);
  25.          aRect := Rect(0, 0, iCaptionWidth, iCaptionHeight);
  26.          DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, DT_WORDBREAK or DT_CALCRECT);
  27.          iCaptionHeight := aRect.Bottom;
  28.          iCapY := (Height - iCaptionHeight) div 2 + iOffset;
  29.          iCapX := 4 + iOffset;
  30.          iGlY := (Height - iGlyphHeight) div 2 + iOffset;
  31.          iGlyphOffset := 0; if Caption <> '' then iGlyphOffset := aRect.Right + 4;
  32.  
  33.          if Layout = blGlyphLeft then
  34.          begin
  35.             if iGlyphWidth > 0 then inc(iCapX, iGlyphWidth + 4);
  36.             iGlX := iOffset + 4;
  37.             if not WordWrap then
  38.             begin
  39.                case Alignment of
  40.                   taLeftJustify:  iGlX := iOffset + 4;
  41.                   taRightJustify: iGlX := Width - 4 - iGlyphWidth - iGlyphOffset + iOffset;
  42.                   taCenter:       iGlX := (Width - iGlyphWidth) div 2 + iOffset - (iGlyphOffset div 2);
  43.                end;
  44.             end;
  45.          end
  46.          else if Layout = blGlyphRight then
  47.          begin
  48.             iGlX := iCapX + iGlyphOffset;
  49.             if not WordWrap then
  50.             begin
  51.                case Alignment of
  52.                   taLeftJustify:  iGlX := iCapX + iGlyphOffset;
  53.                   taRightJustify: iGlX := Width - 4 + iOffset - iGlyphWidth;
  54.                   taCenter:       iGlX := (Width - iGlyphWidth) div 2 + iOffset + (iGlyphOffset div 2);
  55.                end;
  56.             end;
  57.          end;
  58.  
  59.          DrawGlyph(iGlX, iGlY, iGlyphIndex, 0, iGlyphWidth, iGlyphHeight);
  60.          aRect := Rect(iCapX, iCapY, iCapX + iCaptionWidth, iCapY + iCaptionHeight);
  61.          DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, dtMode);
  62.       end
  63.       else if (Layout in [blGlyphTop, blGlyphBottom]) and (NumGlyphs > 0) then
  64.       begin
  65.          iCaptionWidth := Width - 8;
  66.          if iCaptionWidth < 0 then iCaptionWidth := 0;
  67.          if not WordWrap then while Canvas.TextWidth(Caption) > iCaptionWidth do Caption := copy(Caption, 1, length(Caption)-1);        
  68.          aRect := Rect(0, 0, iCaptionWidth, iCaptionHeight);        
  69.          DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, DT_WORDBREAK or DT_CALCRECT);        
  70.          iCaptionHeight := aRect.Bottom;
  71.          iVertHeight := iCaptionHeight; if iGlyphHeight > 0 then inc(iVertHeight, iGlyphHeight + 4);
  72.          iCapX := 4 + iOffset;
  73.  
  74.          if Layout = blGlyphTop then
  75.          begin
  76.             iCapY := (Height - iVertHeight) div 2 + iGlyphHeight + iOffset;
  77.             iGlY := (Height - iVertHeight) div 2 + iOffset;
  78.             case Alignment of
  79.                taLeftJustify:  iGlX := 4 + iOffset;
  80.                taRightJustify: iGlX := Width - iGlyphWidth - 4 + iOffset;
  81.                taCenter:       iGlX := (Width - iGlyphWidth) div 2 + iOffset;
  82.             end;
  83.          end
  84.          else if Layout = blGlyphBottom then
  85.          begin
  86.             iCapY := (Height - iVertHeight) div 2 + iOffset;
  87.             iGlY := (Height - iVertHeight) div 2 + iCaptionHeight + iOffset;
  88.             case Alignment of
  89.                taLeftJustify:  iGlX := 4 + iOffset;
  90.                taRightJustify: iGlX := Width - iGlyphWidth - 4 + iOffset;
  91.                taCenter:       iGlX := (Width - iGlyphWidth) div 2 + iOffset;
  92.             end;
  93.          end;
  94.  
  95.          DrawGlyph(iGlX, iGlY, iGlyphIndex, 0, iGlyphWidth, iGlyphHeight);
  96.          aRect := Rect(iCapX, iCapY, iCapX + iCaptionWidth, iCapY + iCaptionHeight);
  97.          DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, dtMode);
  98.       end
  99.       else
  100.       begin
  101.         iCaptionWidth := Width - 8; if iCaptionWidth < 0 then iCaptionWidth := 0;
  102.         if not WordWrap then while Canvas.TextWidth(Caption) > iCaptionWidth do Caption := copy(Caption, 1, length(Caption)-1);
  103.         aRect := Rect(0, 0, iCaptionWidth, iCaptionHeight);
  104.         DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, DT_WORDBREAK or DT_CALCRECT);
  105.         iCaptionHeight := aRect.Bottom;
  106.  
  107.         DrawGlyph((Width-iGlyphWidth) div 2, (Height-iGlyphHeight) div 2, iGlyphIndex, 0, iGlyphWidth, iGlyphHeight);
  108.         aRect := Rect(iOffset, iOffset + (Height-iCaptionHeight) div 2, Width, Height);
  109.         DrawText(Canvas.Handle, pChar(Caption), Length(Caption), aRect, DT_CENTER or DT_WORDBREAK);
  110.       end;
  111.    end;
  112.  
  113.    ... konec procedury
  114.  
  115.  

Offline jarex

  • Plnoletý
  • ***
  • Příspěvků: 225
  • Karma: 5
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #6 kdy: 08-11-2016, 08:13:18 »
Z kódu není vidět asi nic anomálního. Možná bych já osobně na to šel tak, že bych si vytvořil proceduru, která bude těsně před funkcí DrawText někam ukládat všechny parametry. Tedy icaption, length(icaption), arect, canvas width height, ... Třeba do souboru, stringlistu... A pak pokoušel do té doby, než  to ten nápis na nějakém tlačítku nakreslí špatně. Potom bych se podíval co je špatně, a jestli je vůbec něco špatně.   
« Poslední změna: 08-11-2016, 08:21:19 od jarex »
D2007 Professional

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #7 kdy: 08-11-2016, 08:17:11 »
To ukládání je dobrý nápad. Realizuji to a uvidím.

Offline JaroB

  • Guru
  • *****
  • Příspěvků: 1066
  • Karma: 29
    • Verze Delphi: XE8, Sydney
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #8 kdy: 08-11-2016, 09:21:03 »
Kdybych to dělal já, tak asi k ověření použiju způsob vykreslení motivu na bitmapu a pak přes např. Draw(0,0,bmp) natažení na Canvas prvku.
Pokud je to ale nedotažení textu, tak do toho kecá optimalizace DrawText (chybný překryv nebo chybně spočtená pravá mez)

Offline Slappy

  • Hrdina
  • ****
  • Příspěvků: 252
  • Karma: 12
  • Slappy
    • Verze Delphi: 11 Alexandria + Vsetky :)
    • unSigned
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #9 kdy: 08-11-2016, 16:25:47 »
Lepsie raz vidiet, ako stokrat pocut :)

-- Slovenske prislovie

Obrazok by sa nenasiel?
Moje projekty: http://www.unsigned.sk Tvorba cool dizajnovych instalatorov v NSIS a Inno Setup. Rozsirenie pre Visual Studio a RAD Studio pre tvorbu NSIS a Inno Setup instalatorov.

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #10 kdy: 09-11-2016, 07:14:17 »
Obrázek je, ale již nevím kde.
Jinak to logování ukázalo pár nedostatků. Například když se na formuláři změní obrázek, tak se všechny tlačítka překreslují. Po umístění obrázku na panel se problémek vyřeší.

Zbývá už jen čekat až nastane problém s nedopsáním textu ...

Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #11 kdy: 10-11-2016, 07:35:06 »
Tak chyba se projevila, bohužel však když nebylo aktivní logování. Mám ale obrázek!!! :)

Na něm je černé tlačítko se žlutým rámečkem. Šedivý text je kvůli tomu, že je tlačítko "Enabled := false;".
Celý text: 'Úkosy použité' + #13#10 + 'v plánu'

Z obrázku usuzuji, že Caption, délka Captionu, rectangle pro Canvas i text musí být v pořádku. Z tohoto důvodu jsem ztracen, protože mě nenapadá, kde a proč vzniká tato chyba...

Dodatek: chybějící text začíná od poloviny tlačítka. Mám pocit, že to tak bylo u každé předchozí chyby.

Nějaká rada? Chytnu se každého stébla!
« Poslední změna: 10-11-2016, 08:05:54 od age.new »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3343
  • Karma: 139
    • Verze Delphi: D2007, XE3, DX10
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #12 kdy: 10-11-2016, 08:53:49 »
Kód: Delphi [Vybrat]
  1.   if not WordWrap then while Canvas.TextWidth(Caption) > iCaptionWidth do Caption := copy(Caption, 1, length(Caption)-1);
  2.  
Nemam cas a s ohledem na nestandardni formatovani kodu ani chut to lustit, ale prvni co me prastilo do oci je kod nahore: je Caption property? Jestli ano, uvedomujes si, ze je za tim setter SetText, ktery zavola SetTextBuf?
Kód: Delphi [Vybrat]
  1. procedure TControl.SetTextBuf(Buffer: PChar);
  2. begin
  3.   Perform(WM_SETTEXT, 0, Longint(Buffer));
  4.   Perform(CM_TEXTCHANGED, 0, 0);
  5. end;
  6.  

Pokud je to Caption property, tak pravdepodobne vyvolavas nejakou rekurzi, at uz na urovni vnitrku wokennich procedur nebo vlastniho kodu.

Pri kresleni nemas co menit vlastnosti objektu, ktery renderujes, ale jen renderovat.


Offline age.new

  • Hrdina
  • ****
  • Příspěvků: 316
  • Karma: 0
Re:DrawText na SpeedButton tlačítku občas nedokreslí text
« Odpověď #13 kdy: 11-11-2016, 15:56:33 »
Díval jsem se do kódu a Caption je parametr procedury. Naplní se z Caption property. K rekurzi tak zde nedochází.