Vykreslování grafiky   otázka

VB.NET

Vykresluji grafiku rovnou na Form1, vše je ve Form1_Paint. Parametry vykreslování (souřadnice) se mění událostmi myši a tlačítek po Me.Invalidate. Vše funguje až na jemné rychlé změny prováděné změnou souřadnic v opakovacím cyklu (vlastně něco jako kousek animace). Změny se neukazují.

Po záměně Me.Invalidate za Me.Refresh se ukazují, ale je to dost velké násilí a překreslení je pak trhavé (nastavení Me.DoubleBuffered nemá žádný vliv). Podobně, když za Me.Invalidate přidám DoEvent.

Je nějaká možnost donutit počítač ukázat i jemné změny v grafice v rychlém sledu? Nebo jak zpomalit běh programu, aby počítač měl čas kreslit. ale bez DoEvents.

Prosím o radu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Myslím, že to není tím, že by počítač ty změny ignoroval. Je to tím, že když s myší trochu víc cuknete, souřadnice se nemění po pixelech, ale přímo po jednotkách pixelů a formulář pak reflektuje pouze to, co myš vrací. Řešení je snadné. Ukládejte si poslední souřadnice a pak zjistěte offset nových dat od starých (vodorovně a svisle a zvolte větší číslo -> větší změnu) a vykreslování proveďte i přes vynechané pixely.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Tahání myší mi jde normálně, ani kurzor se při rychlém pohybu nevykreslí po všech pixelech. Problém je jinde - zjednodušená ukázka

Public Class Form1
  Dim roz As Integer

  Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.DoubleBuffered = True
  End Sub

  Private Sub Form1_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDoubleClick
    For d As Integer = 10 To 0 Step -1
      roz = -10 * d
      Me.Invalidate()
      'Me.Refresh()
      Application.DoEvents()
      roz = 10 * d
      Me.Invalidate()
      'Me.Refresh()
      'Application.DoEvents()
    Next

  End Sub

  Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
    e.Graphics.FillRectangle(Brushes.SteelBlue, 20, 120 + roz, 260, 8)
  End Sub
End Class

Pohyb dolu se neprovede. Refresh se nehodí, musím přidat zde mnohokrát odsouzené DoEvents.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Aha, myslel jsem, že jde o to, že vykreslujete v MouseMove. Tady je problém v tom, že když zavoláte Me.Invalidate(), grafika formuláře se vyčistí, formulář se překreslí, a dokreslí se to, co vy vykreslíte v handleru události Form.Paint. Tzn., že zůstane poslední render, jelikož ty předchozí se maží tí, jak kreslíte další. Tady je Váš kód upravený tak, aby rendery nemizely. Vložte na formulář PictureBox a dejte mu Dock na DockStyle.Fill.

Public Class Form1

    Dim b As New Bitmap(Me.ClientSize.Width, Me.ClientSize.Height)
    Private Sub PictureBox1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseClick
        Using g As Graphics = Graphics.FromImage(b)
            g.Clear(SystemColors.Window)
            For d As Integer = 10 To 0 Step -1
                g.FillRectangle(Brushes.SteelBlue, 20, 120 + (-10 * d), 260, 8)
                g.FillRectangle(Brushes.Gray, 20, 120 + (10 * d), 260, 8)
            Next
        End Using
        PictureBox1.Image = b
    End Sub

End Class
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Díky za příklad. Jenže nechci, aby zůstávalo všechno vykreslení, ale aby se vykreslovalo postupně a v jednom okamžiku bylo vidět vždy jen to jediné - jde o pohyb, něco jako animace.

Omlouvám se za neúplný popis. Ten jsem napsal, ale odeslání skončilo chybou a při opakovaném psaní jsem byl stručnější - asi příliš.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Dobře, a čím chcete ten pohyb vyvolávat? Dvojklikem? Předělat to nebude složité, stačí místo For cyklu použít Timer a v handleru jeho události Tick vykreslit a inkrementovat proměnnou d.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ten dvojklik jsem použil v ukázce, v aplikaci je ten problém nepatrná část a jde z tlačítka. To není podstatné. Jde stále o jedno, že se kus kódu prostě nevykreslí. Použil jsem za Invalidate ještě DoEvents a jde to. Jen jsem se DoEvents chtěl vyhnout, když je zde vícekrát sděleno, že není vhodný.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Problém je v tom, že metodou Invalidate pouze dáte grafickému prostředí informaci, že se něco změnilo a při nejbližší vhodné příležitosti bude nutné překreslení. No a ta nejbližší vhodná příležitost nastane buď až ve chvíli, kdy "odlehčíte" vláknu (tj. ve vašem příkladě doběhne cyklus), nebo nařídíte běhovému prostředí, aby pozastavil vykonávání aktuální fronty příkazů a vypořádal se se všemi systémovými událostmí, které se mohly doposud nastřádat (tou metodou DoEvents).

Kdežto metodou Refresh formuláři řeknete, aby zaktualizoval svou klientskou pracovní plochu a okamžitě nato dáte jemu i jeho child prvkům příkaz, aby se překreslily.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback