Spostare controlli di una griglia con Grid.RowProperty / Grid.ColumnProperty
Come si sa Grid.RowProperty e Grid.ColumnProperty sono attached property, nel senso che la loro definizione nella tag che in XAML fa riferimento non ad una proprietà specifica dell’oggetto sito in una cella del contenitore Grid (come per esempio Margin) bensì ad una caratteristica del “padre” Grid. Di qui la sintassi dotted, Grid. eccetera, nonché l’obbligo di utilizzare nel codice una sintassi GetValue o SetValue, come nell’esempio seguente:
Dim Riga = Oggetto.GetValue(Grid.RowProperty) ' Ci dà la riga dell’Oggetto
Mentre con Margin si può ricorrere, semplicemente, a Oggetto.Margin
Nota. Volendo, anche Oggetto.GetValue(Margin) è lecito. Analogamente con SetValue.
L’ideuzza che qui mi permetto di suggerire a quanti, forse, non ci hanno ancora pensato è quella di sfruttare le due proprietà in questione per effettuare spostamenti di oggetti da cella a cella della griglia. Vengo al punto senza ulteriori lungaggini con questo esempio (relativo per semplicità a due Button):
Scambio fra due pulsanti
Private Sub ScambiaPuls(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
' Scambia posizione dei pulsanti puls1 e puls2
Dim Riga1 = Puls1.GetValue(Grid.RowProperty)
Dim Colonna1 = Puls1.GetValue(Grid.ColumnProperty)
With Puls1
.SetValue(Grid.RowProperty, Puls2.GetValue(Grid.RowProperty))
.SetValue(Grid.ColumnProperty, Puls2.GetValue(Grid.ColumnProperty))
End With
With Puls2
.SetValue(Grid.RowProperty, Riga1)
.SetValue(Grid.ColumnProperty, Colonna1)
End With
End Sub
Mi astengo da commenti, in quanto lo snippet è eloquente, idem per la sua traduzione in un metodo del tipo Sub ScambiaPulsanti(Puls1 As..., Puls2 As...). Piuttosto faccio notare che
In questo come in altri esempi gli oggetti modificati cambiano proprietà “ereditate” (attached) mantenendo intatta la loro peculiare identità, a partire dal nome, proprio come una persona che si sposta in un’altra località.
Prima di proseguire propongo un altro brano non privo di interesse:
For Each Ctrl In miaGrid.Children
' Tratta tutte e sole le TextBox di miaGrid
If TypeOf Ctrl Is TextBox Then
Riga = Ctrl.GetValue(Grid.RowProperty)
Colonna = Ctrl.GetValue(Grid.ColumnProperty)
MessageBox.Show(Ctrl.ToString & vbLf & _
"Riga: " & Riga & " – " & _
"Colonna: " & Colonna)
Next
Come indica il commento iniziale, il ciclo itera i figlioli di una certa griglia di nome miaGrid (melius abundare quam deficere con i nomi in WPF...) prendendo in considerazione solo le TextBox, segnalandone la Riga e la Colonna della cella di appartenenza. Tale tecnica discriminatoria, adattabile a altri tipi come Button ecc., può essere utilmente sfruttata in vari contesti, e non soltanto per gli spostamenti di cui qui si parla.
Accumulare pulsanti nella prima cella
Private Sub TuttiPulsACella0_0(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
' righe/colonne dei pulsanti indicati in VettPuls
Dim VettPuls() As Button = {Puls3, Puls4, Puls5, Puls2}
Dim N = VettPuls.GetUpperBound(0)
Dim VettRighe(N) As Integer ' = {1, 2, 3} ' Meglio un codice più GENERALE
Dim VettCol(N) As Integer ' = {1, 2, 3} ' " " " " " " "
Dim i = 0
For Each puls In VettPuls
' MessageBox.Show(puls.Name) ' Servita per debug
VettRighe(i) = puls.GetValue(Grid.RowProperty)
VettCol(i) = puls.GetValue(Grid.ColumnProperty)
i += 1
' Sposta il controllo nella cella (0, 0)
puls.SetValue(Grid.RowProperty, 0)
puls.SetValue(Grid.ColumnProperty, 0)
Next
Dim Msg = "Ripristino le posizioni originarie?"
i = 0
If MessageBox.Show(Msg, "", MessageBoxButton.YesNo) _
= MessageBoxResult.Yes Then
For Each puls In VettPuls
puls.SetValue(Grid.RowProperty, VettRighe(i))
puls.SetValue(Grid.ColumnProperty, VettCol(i))
i += 1
Next
End If
End Sub
La precedente curiosa routine prevede una matrice VettPuls di oggetti Button, identificati – ecco un punto di un certo interesse, direi – con il rispettivo nome (proprietà Name) dopo di che il primo ciclo For Each puls In VettPuls ne carica i valori di riga e colonna nei VettRighe e, rispettivamente, VettCol quindi prosegue sistemando tutti nella cella di coordinate nulle.
Nota. En passant, un accumulo di pulsanti non è una stramberia, perché può servire in altri contesti. Per esempio se si desidera presentare all’utente un comando alla volta. La qual cosa può esser fatta anche in ambiente Windows Form, ma mi sembra che WPF anche qui si presenti più versatile.
Va poi da sé che il susseguente ciclo, se ADR l’utente risponde affermativamente, ripristina le posizioni originarie dei nostri Button salvate in precedenza in VettRighe e VettCol. Si dirà che sarebbe stato più elegante una struttura Stack. OK, touché, ma chi ci tiene può accomodarsi con facili modifiche, qual che conta essendo, a modesto mio parere, più la sostanza che la forma...
Piazzare un controllo in un “buco”
Sub SpostaPulsante(ByVal Puls As Button, RigaBuco As Integer, ColonnaBuco As Integer)
Dim Riga As Integer, Colonna As Integer
Riga = Puls.GetValue(Grid.RowProperty)
Colonna = Puls.GetValue(Grid.ColumnProperty)
If Riga = RigaBuco And System.Math.Abs(Colonna - ColonnaBuco) = 1 Then
Puls.SetValue(Grid.ColumnProperty, ColonnaBuco)
ColonnaBuco = Colonna
ElseIf Colonna = _
ColonnaBuco And System.Math.Abs(Riga - RigaBuco) = 1 Then
Puls.SetValue(Grid.RowProperty, RigaBuco)
RigaBuco = Riga
End If
End Sub
Quest’ultima routine proposta, stavolta con argomenti RigaBuco e ColonnaBuco, corrisponde di fatto alla prima che mi ha offerto lo spunto di impiegare le attached property in questione. Si è trattato del famoso Gioco del 15. Per il quale ho implementato una Window contenente una Grid 4 x 4, nelle cui celle ho collocato quindici Button, escludendo l’ultima in basso a destra – il BUCO - di coordinate (3, 3), ciascuno dei quali reca sul gobbo numeretti da 1 a 15 dislocati a caso. La tabellina seguente schematizza la situazione, con tutti i pulsanti dipinti di blu, salvo il buco:
|
10
|
6
|
14
|
13
|
|
12
|
15
|
5
|
7
|
|
4
|
9
|
11
|
1
|
|
3
|
8
|
2
|
|
Premesso che tutti i 15 pulsanti hanno in comune una Sub d’evento Click sostanzialmente conforme alla precedente SpostaPulsante, qui giunti l’analisi del come e perché solo il pulsante prossimo al buco vi può essere spostato viene lasciata per esercizio. Per non tediare e non per sadismo, giuro.
Gianni Giaccaglini
P.S. -Chi fosse interessato a usare il pur futile giochino del 15 a titolo di relax può chiedermelo per posta (giannigiac@tin.it).