Objekte in einem Grid bewegen – Windows 8
Dieser Artikel ist ein Gastbeitrag von Malte, welcher sonst für CodingCave.de Beiträge verfasst. Beim letzten CodingCamp an der Uni Kiel kam die Frage auf, wie die Bewegung von Objekten in einem Grid möglich ist. Realisiert habe ich dies bereits in der App ‚Zahlenwurm‚. Hier muss der Nutzer geeignet lange Kugelketten durch das Ziehen von Kugeln über das Feld erstellen. Wie ihr das jetzt in eure App integrieren könnt, möchte ich euch in diesem Beitrag zeigen.
Hierzu eignet sich zum Beispiel die Verwendung eines Grids. In diesem Fall erstelle ich zur Demonstration ein Grid mit 3×3 Feldern:
1 2 3 4 5 6 7 8 9 10 11 12 |
<Grid Grid.Row="1" Margin="120,10" Name="grid" PointerPressed="grid_PointerPressed" PointerMoved="grid_PointerMoved" PointerReleased="grid_PointerReleased" PointerExited="grid_PointerExited" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="3*"/> <RowDefinition Height="3*"/> <RowDefinition Height="3*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="3*"/> <ColumnDefinition Width="3*"/> <ColumnDefinition Width="3*"/> </Grid.ColumnDefinitions> </Grid> |
Man erkennt, dass bereits die Events für verschiedene Pointer-Aktionen angegeben sind. Dazu gehören PointerPressed, Moved, Released und Exited. Über diese Events wollen wir die Bewegung einer Kugel über das Feld steuern, beziehungsweise ermöglichen. Wir benötigen nun die folgende globale Variablen:
1 2 3 4 |
private ImageBrush image; private bool movePointer; private Border[,] border = new Border[3, 3]; private Point pointClicked; |
Hierbei ist image unsere Kugel, die bewegt werden soll und movePointer gibt an, ob gerade eine Kugel über das Feld gezogen wird. PointClicked speichert das zuletzt geklickte Feld. Um ein Raster über das Grid zu legen, definieren wir ein Border-Array. Dieses initialisieren wir in einer im Konstruktor aufgerufenen initializeGrid() Methode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private void initializeGrid() { //Rahmen für einzelne Felder setzen for (int j = 0; j < 3; j++) { for (int i = 0; i < 3; i++) { border[i, j] = new Border(); border[i, j].BorderBrush = new SolidColorBrush(Windows.UI.Colors.Black); border[i, j].BorderThickness = new Thickness(2); grid.Children.Add(border[i, j]); Grid.SetColumn(border[i, j], i); Grid.SetRow(border[i, j], j); } } //Bild laden image = new ImageBrush(); image.ImageSource = new BitmapImage(new Uri(@"ms-appx:///Assets/ball.png")); //Bild in obere linke Ecke setzen border[0, 0].Background = image; } |
Über die Events für das Grid können wir nun anfangen, Bewegungen und Verschiebungen zu ermöglichen. Realisieren möchten wir hier das Entfernen eines Objektes beim Klicken auf das jeweilige Feld und das Ziehen von einem Feld in andere, um damit die Erstellung des Wurmes nachzubilden. Über die globalen Variablen pointClicked und pointerMoved speichern wir die getätigten Eingaben in dem PointerClicked-Event.
Über die Abfrage dieser Variablen können in den anderen Events damit je nach Bedarf Zustände abgerufen werden und die Bewegung und Erstellung neuer Grafiken veranlasst werden. Dies geschieht hier im PointerMoved und PointerReleased. Das Event PointerMoved feuert, sobald man die Maus bewegt. Wir ändern also den Hintergrund des Feldes, wenn wir gerade die Maus gedrückt halten, also wenn pointerMoved == true. Für das Löschen beim Anklicken einer Kugel definieren wir im PointerReleased-Event die kurze if-Abfrage, die überprüft, ob das angeklickte Feld (pointClicked) und das Feld, in dem die Maus losgelassen wurde, übereinstimmen.
Schaut euch dazu einfach das Beispielprojekt und die darin enthaltenen Kommentare an. Durch das Ausführen der App und Herumprobieren können viele Bewegungen und Regeln für diese realisiert werden. Die bisher programmierten Bewegungen haben natürlich noch ihre Ecken und Kanten. Zum Beispiel reicht eine kleine Bewegung der Maus beim Klicken (um die Kugel zu Entfernen) aus, um die Kugel erneut zu zeichnen. Dies könnte natürlich weiter abgefragt werden.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
private void grid_PointerPressed(object sender, PointerRoutedEventArgs e) { //aktuelle Position ermitteln Point p = e.GetCurrentPoint(grid).Position; //Feld ermitteln int posX = (int)p.X / (int)(grid.ActualWidth / 3); int posY = (int)p.Y / (int)(grid.ActualHeight / 3); //aktuelle Position speichern pointClicked = new Point(posX, posY); border[posX, posY].Background = new SolidColorBrush(Windows.UI.Colors.Transparent); //außerhalb des Spielfeldes? if (posX > 2 || posY > 2 || posY < 0 || posX < 0) { movePointer = false; return; } //wir bewegen die gedrückte Maus movePointer = true; } private void grid_PointerMoved(object sender, PointerRoutedEventArgs e) { //bewegen wir eine gedrückte Maus? if (!movePointer) return; Point p = e.GetCurrentPoint(grid).Position; int posX = (int)p.X / (int)(grid.ActualWidth / 3); int posY = (int)p.Y / (int)(grid.ActualHeight / 3); //Bild wird gesetzt, wenn wir mit gedrückter Maus in ein anderes Feld kommen border[posX, posY].Background = image; } private void grid_PointerReleased(object sender, PointerRoutedEventArgs e) { Point p = e.GetCurrentPoint(grid).Position; int posX = (int)p.X / (int)(grid.ActualWidth / 3); int posY = (int)p.Y / (int)(grid.ActualHeight / 3); //Befinden wir uns beim Loslassen der Maus noch immer im Ausgangsfeld, wird das Bild nicht gesetzt if(!(pointClicked.X == posX && pointClicked.Y == posY)) border[posX, posY].Background = image; //Maus nicht mehr gedrückt movePointer = false; } private void grid_PointerExited(object sender, PointerRoutedEventArgs e) { //Haben wir hier nicht verwendet. } |
Bevor ich das fertige Projekt verlinke, noch schnell der Hinweis: Wenn ich oben von einer Maus gesprochen habe, so kann alles auf Finger- und Stifteingaben umgeschrieben werden. Dies regelt Windows alles automatisch für euch!
Hier das gesamte Projekt zum Ausführen und Testen:
GridMove - Sample (275,9 KiB)
Viel Spaß.