Gianni Giaccaglini

Tricks & mini applics on WPF
posts - 46, comments - 0, trackbacks - 0

Prove threading, con un pulsante “ballerino”

 

Prove threading, con un Button “ballerino”

Questo piccolo divertissement deriva da un prezioso suggerimento di R. Serati, con qualche ulteriore ampiamento. Lo scopo immediato è di lanciare delle animazioni di un certo pulsante Button1 entro le “celle” di una Grid. Il termine celle è improprio, Grid è una struttura e non dà accesso a colonne e righe, né tantomeno a celle, ma è possibile cambiare dinamicamente le proprietà dipendenti Grid.RowProperty e Grid.ColumnProperty di un controllo come il nostro Button1 facendolo vagabondare qua e là, su e giù, secondo tempi di ritardo fissati by code.

Il lato istruttivo di cotanta frivolezza è comprendere il meccanismo di Thread e Task che, ahimè, impone il rispetto di priorità, code e altre diavolerie, utili in soluzioni avanzate ma che rendono penosa la vita all’utente, non solo primo-armigero. Se costui proviene da vecchi mondi come VB 6 o quello delle macro di Excel se ne stupisce perché là tutto è più semplice e diretto.

Una buona notizia, con Visual Studio 2012: i metodi asincroni. Rispondendo a questo e altri gridi di dolore l’imminente versione permette di tornare alle origini. In parole povere, se ho ben inteso (e come confermatomi a voce dal relatore all’ultimo Community Day a Microsoft Italia), fermi restando thread & task per scopi speciali, si avrà il supporto di metodi asincroni le cui istruzioni procedono indisturbate rispetto ad altri thread (restituendo il controllo al chiamante ma solo al termine). La parolina magica che qualifica tali routine privilegiate è, guardacaso, Async

Nota. Articoli in materia già fioriscono su MSDN e uno è addirittura linkato nelle News di Visual Studio, ma auspicherei che altri blogger Wpf-italioti contribuiscano...

Ma proseguiamo con l’esposizione del nostro esempietto di pulsante ballerino.

XAML

<Window x:Class="MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Pulsante ballerino" Height="450" Width="450">

    <Grid Name="miaGrid" Background="LawnGreen"  ShowGridLines="True">

    <Grid.RowDefinitions>

        <RowDefinition/>

        <RowDefinition/>

        <RowDefinition/>

        <RowDefinition/>

    </Grid.RowDefinitions>

      <Grid.ColumnDefinitions>

        <ColumnDefinition/>

        <ColumnDefinition/>

        <ColumnDefinition/>

        <ColumnDefinition/>

    </Grid.ColumnDefinitions>

    <Label Grid.Row="0" Grid.Column="0" Name="Lbl1" Height="30" Width="30"

           Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="0" Grid.Column="1" Name="Lbl2" Height="30" Width="30"

           Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="0" Grid.Column="2" Name="Lbl1" Height="30" Width="30"

           Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="0" Grid.Column="3" Name="Lbl4" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="1" Grid.Column="0" Name="Lbl5" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="1" Grid.Column="1" Name="Lbl6" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="1" Grid.Column="2" Name="Lbl7" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="1" Grid.Column="3" Name="Lbl8" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="2" Grid.Column="0" Name="Lbl9" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="2" Grid.Column="1" Name="Lbl10" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue"  Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="2" Grid.Column="2" Name="Lbl11" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue"  Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="2" Grid.Column="3" Name="Lbl12" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue"  Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="3" Grid.Column="0" Name="Lbl13" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue"  Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="3" Grid.Column="1" Name="Lbl14" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="3" Grid.Column="2" Name="Lbl15" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Label Grid.Row="3" Grid.Column="3" Name="Lbl16" Height="30" Width="30"

        Content="X" HorizontalContentAlignment="Center" Background="LightBlue" Visibility="Hidden" FontWeight="Bold"/>

    <Button Content="Ballerino" Height="49" HorizontalAlignment="Left" Margin="12,17,0,0"

        Click ="Balla" Name="Button1" VerticalAlignment="Top" Width="81" />

    </Grid>

 

Come è immediate constatare, si ottiene una Grid 4 x 4, nelle cui 16 caselle si hanno 16 minuscole Label da Lbl1 a Lbl16 in sostanza etichettate con una grossa “X”, assieme a un CommandButton1 indicato come “Ballerino”, al cui clic è associato la routine Balla.

VB

Il codice della MainWindow.xaml.VB è riportato interamente qui sotto. Lo riporto quasi come ricetta, unitamente a commenti tacitiani. Ne anticipo subito quelli relativi all’esordio (le due basilari Imports, ma ne basta una a piacere salvo i casi in cui siano in ballo dei Task che sono babbi di Thread…) e al primo metodo SpostamTempo di argomenti autosplicativi Riga, Colonna e tempo. SpostaTempo, che ho già utilizzata in questo blog di Wpfitalia nel post che precede quello presente, sfrutta razionalmente l’indicazione Sarati, i cui punti chiave sono Dim myThread As New Thread, al cui interno una Sub (delegata?, si dice così?) sfrutta la basilare Dispatcher.Invoke, che a sua volta incastona la Sub che richiama SpostaSuGrid(Riga, Colonna) e Thread.Sleep(Tempo). La prima, subito sotto alla Sub SpostamTempo, agisce sulle proprietà Grid.RowProperty e Grid.ColumnProperty dell’oggetto il nostro Button1 (v. più avanti), la seconda definisce il Tempo in millisecondi della sua “dormita”, ossia ritardo.

Che altro dire? Al termine della SpostamATempo non va, tassativamente, dimenticata myThread.Start() senza la quale il dannato Thread (o Task) non si degna di avviarsi.

Imports System.Threading.Tasks

Imports System.Threading

Class MainWindow

    Private Sub SpostamTempo(ByVal Riga As Integer, ByVal Colonna As Integer, ByVal tempo As Double)

      Dim myThread As New Thread(

        Sub()

          Dispatcher.Invoke(Sub()

              SpostaSuGrid(Riga, Colonna)

              Thread.Sleep(tempo)

          End Sub)

        End Sub)

    ' Esecuzione di myThread

      myThread.Start()

    End Sub

    Sub SpostaSuGrid(ByVal R As Integer, ByVal C As Integer)

      Button1.SetValue(Grid.RowProperty, R)

      Button1.SetValue(Grid.ColumnProperty, C)

    End Sub

 

    Private Sub Balla()

    ' Su clic del pulsante ballerino

      Dim inizTempo = Now

      SpostamTempo(3, 3, 2500) ' Fuziona, ma UNA TANTUM,

      ' poi la seg.te istr. non agisce:

      ' SpostaSuGrid(0, 0)

      MessageBox.Show("Primo giro finito")

      Dim GeneRCasuali As New Random

      Dim NCas = GeneRCasuali.Next(100, 500)

      Dim Nr = miaGrid.RowDefinitions.Count

      Dim Nc = miaGrid.ColumnDefinitions.Count

      ' Coordinate riga/colonna percorso programmato (spirale)

      Dim VettR = {0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 2, 2}

      Dim VettC = {0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0, 1, 2, 2, 1}

      Dim VettLbl = _

    {Lbl10, Lbl11, Lbl7, Lbl6, Lbl5, Lbl9, Lbl13, Lbl14, Lbl15, Lbl16, Lbl12, Lbl8,   Lbl4, Lbl3, Lbl2, Lbl1}

      Dim task As New Task(Sub()

      Dim i = 0, j = 0, k = 0

      ' Secondo giro: percorso programmato "lento"

      ' For k = 0 To VettR.Length - 1 ' Ex giro diretto

      Dim x = 0

      For k = VettR.Length - 1 To 0 Step -1 ' Giro da centro a periferia

             Dispatcher.Invoke(Sub() SpostaSuGrid(VettR(k), VettC(k)))

                 Thread.Sleep(1500)

                 Dispatcher.Invoke(Sub()

                       VettLbl(x).Visibility = Windows.Visibility.Visible

                       x += 1

                       End Sub)

      Next

 

      ' Terzo giro: percorso casuale "veloce"

      For i = 0 To VettLbl.Length – 1

                   ' Occulta tutte le Label con crocette

                   Dispatcher.Invoke(Sub()

                        VettLbl(i).Visibility = Windows.Visibility.Hidden

                   End Sub)

      Next

      Dim GenCas As New Random

      For i = 0 To Nr - 1

              For j = 0 To Nc - 1

                     Dispatcher.Invoke(Sub() SpostaSuGrid(GenCas.Next(Nr), GenCas.Next(Nc)))

                     Thread.Sleep(NCas)

              Next

      Next

                 ' Dispatcher.InvokeShutdown() 'TROPPO DRASTICO: cessa il PROGRAMMA!

            End Sub)

            task.Start()

    ' MessageBox.Show(balla.IsCompleted) ' Viene comunque eseguita...

    End Sub

End Class

 

Il codice rimanente è quasi interamente affidato all’ermeneutica del visitatore, che deve anzitutto meditare sui seguenti vettori:

Dim VettR = {0, 0, 0, 0, 1, 2, 3, ...}

Dim VettC = {0, 1, 2, 3, 3, 3, 3, ...}

Dim VettLbl = {Lbl10, Lbl11, Lbl7, Labl6, ...}

 

I primi due indicano le successive coordinate di un percorso programmato, il terzo elenca i controlli Label che via via debbono apparire sulla scia degli spostamenti del pulsante ballerino. Con un po’ di pazienza si può arrivare a comprendere che la Sub del “secondo giro” agisce come illustra alla buona questa figura:

 

 

 

 

 

Ballerino

X

X

 

 

X

X

 

 

 

 

 

 

La quale fotografa la situazione intermedia in cui il pulsante, partendo dalla casella 10 (della Lb10) si è portato nella 11 (della Lb11), poi nella 7 (della Lb7), poi nella 6 (della Lb6) e ora si trova nella casa 5. Viene così seguito un percorso a spirale dal centro alla periferia.

Sull’ultima Sub che impone al ballerino un percorso semifolle, governato dalla (pseudo) casualità non ho altro da dire, perché è una variante del discorso. Che qui si conclude.

Print | posted on giovedì 5 aprile 2012 20:13 |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 6 and 2 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET