Senti, è una cosina fatta in 10 minuti, non pretendo che non abbia qualche bug che potrai magari correggere da solo, comunque la falsariga di quello che ho in testa è questa:
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
Dim Casella As TextBox = CType(sender, TextBox)
Dim Testo As String = Casella.Text
'Evito di avvitarmi in un loop senza fine indicando quando il cambio del testo non è opera dell'utente, ma del codice
Static HoCambiatoIo As Boolean = False
If Not HoCambiatoIo Then
'Resetto il flag, perchè potrei anche non cambiare niente della stringa
HoCambiatoIo = False
'memorizzo la posizione del cursore
Dim Posizione As Integer = Casella.SelectionStart
'Memorizzo quanti spazi ci sono fino alla posizione attuale del cursore
Dim SpaziPrima As Integer = 0
SpaziPrima = Posizione + 1 - Testo.Substring(0, Posizione).Replace(" ", "").Length
'Tolgo tutti gli eventuali spazi
Testo = Testo.Replace(" "c, "")
'Ora inserisco uno spazio ogni 2 caratteri, se almeno due caratteri ci sono.
Dim i As Integer = 2
Do While i < Testo.Length
Testo = Testo.Insert(i, " ")
i += 3
Loop
'Se c'è stato un cambiamento, allora devo aggiornare il testo della textbox
If Testo <> Casella.Text Then
HoCambiatoIo = True
Casella.Text = Testo
'Ora riposiziono il cursore, tenendo conto di quanti spazi sono stati aggiunti rispetto a prima
Dim SpaziDopo As Integer = Posizione - Testo.Substring(0, Posizione - 1).Replace(" ", "").Length
Casella.SelectionStart = Posizione - SpaziPrima + SpaziDopo
End If
Else
HoCambiatoIo = False
End If
End Sub
Come ti ho già detto, è inutile che tu cerchi di intercettare un tasto, come il 'canc', quando chiunque ti ci può incollare un testo privo di spazi, o comunque effettuare operazioni da mouse.
Rimane da valutare il consiglio di Massimo. Se la Masked è in grado di fornire una prestazione simile, è senz'altro preferibile. Io non la conosco abbastanza da sapere se sia fattibile, o meno, nè con quali settaggi ottenere l'effetto voluto.