Blog

NoNamePlayer

Crossplatform Coding with C#

Part#1 Windows Layout



Wichtig ist, dass ihr bevor ihr anfangt meine kurze Einleitung dazu lest, bzw. die nötigen Grundkenntnisse auf Codeacademy aneignet.

In diesem Part wird das Windows Layout mit Xaml erstellt. Dazu nutzen wir das Grafik-Framework Windows Presentation Foundation (WPF) welches als Teil des .Net Frameworks auf Windows Vista, 7, 8, 10 mitgeliefert wird.

Zum Beginnen öffnen wir Visual Studio und erstellen das Projekt.

  • Visual Studio Starten
  • Auf der Startseite auf "Neues Projekt..."
  • In der liste auf der linken Seite Installiert/Vorlagen/Visual C#/Windows wählen
  • In der mittleren Liste WPF-Anwendung auswählen
  • Name und Speicherort beliebig wählen
  • Mit "OK" bestätigen
  • Auf der linken Seite befindet sich der Projektmappen-Explorer, hier sieht man die einzelnen Dokumente unserer Anwendung. Darunter erscheint euer Projekt, unter Rechtsklick -> Optionen kann man weitere Einstellungen tätigen oder zB. den Namen ändern. Öffnet man das Projekt durch drücken des Pfeils links daneben kommen weitere Sachen zum vorschein.

  • Properties: Hier sind die Einstellungen des Projektes gespeichert.
  • Verweise: Hier können externe Librarys in das Projekt eingebunden werden um fremden Code zu verwenden.
  • App.config: Configurationsdatei f¨r die WPF-Anwendung.
  • App.xaml: Als Startpunkt der Anwendung, kann für verschiedene Einstellungen beim Start der Anwendung oder erstes Fenster verwendet werden. Die Einstellungen wie zB. Layout Styles können für die ganze Anwendung gelten.
  • MainWindow.xaml: Wird üblicherweise als erstes Fenster verwendet, so werden auch wir es machen.

  • Wenn ihr jetzt oben auf "Starten" neben dem grünen Pfeil drückt öffnet sich ein leeres Fenster, dieses werden wir nun bearbeiten.
    Dafür öffnen wir zuerst "MainWindow.xaml". Hier erscheint uns nun oben eine Anzeige, welche Änderungen direkt anzeigt, wodurch wir wissen wie das Layout aussieht ohne immer die Anwendung neu zu Kompilieren (in Maschinencode übersetzten, sodass der Computer die Anwendung ausführen kann).

    Das Gesamte Layout ist ähnlich wie html aufgebaut, so werden Objekte mit geöffnet und mit geschlossen. Wie wir sehen ist bereits ein Grid-Layout erstellt, welches alles Angezeigte beherbergt, hier können wir jetzt Reihen und Spalten definieren. Spalten liegen in der Eigenschaft "Grid.ColumnDefinitions" und Reihen in "Grid.RowDefinitions", einzelne Spalten und Reihen wiederrum darin, diese werden über "ColumnDefinition" und "RowDefinition" definiert.


    	<Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
        </Grid>
    

    In unserer Anzeige oben, hat sich nun ein Raster aus gleichmäßigen Spalten und Reihen gelegt, nun sollen diese aber nicht alle gleich groß sein, weshalb wir mittels "Width" (Breite für Spalten) und "Height" (Höhe für Reihen) die Grä&suml;en ändern.


                <ColumnDefinition Width="10"/>
    
                <RowDefinition Height="20"/>
    

    Diese Größen sind relativ zueinander, wenn man "*" angibt nimmt dies den Rest des Platzes ein durch "0.5*" kann man den eigentlich rest Halbieren oder durch "2*" verdoppeln wodurch sich dann alles andere wieder relativ dazu ändert. Das ist ein sehr großer Vorteil, da dadurch auf allen Geräten das layout gleich aussieht zB. auf 24" 1080p und 13" 2160p. Folgende Größen habe ich gewählt:


    <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="10"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="10"/>
            </Grid.ColumnDefinitions>
            
            <Grid.RowDefinitions>
                <RowDefinition Height="10"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="10"/>
                <RowDefinition Height="30"/>
                <RowDefinition Height="10"/>
                <RowDefinition Height="200"/>
                <RowDefinition Height="20"/>
            </Grid.RowDefinitions>
        </Grid>
    


    Um das Layout zu verfeinern habe ich zwei weitere Grid Layouts eingefügt, eines soll später die Anzeige des aktuellen Spielers etc übernehmen, das andere das Spielfeld selbst. Über die Eigenschaften "Grid.Row" und "Grid.Column" kann man angeben in welcher Spalte und Reihe des darüberliegenden Grid-Layouts sich dieses befindet. Kommentare werden über "<!--" angefangen und mit "-->" geschlossen.
    Das Layout für die Spieleranzeige:


    	  <!-- Grid-Layout to show Player -->
    	   <Grid Grid.Row="1" Grid.Column="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="10"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
            </Grid>
    
    		
    	  <!-- Grid-Layout to show pitch -->
    		<Grid Grid.Row="5" Grid.Column="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="260"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
            </Grid>
    
    	



    Das Layout für das Spielfeld:


    <Grid Grid.Column="1">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>
                </Grid>
    
    	


    Wenn wir das jetzt ausführen haben wir aber immernoch ein leeres Fenster, deshalb fügen wir jetzt weitere UIElemente hinzu. Anfangen tun wir beim Label (Textanzeige) für den aktiven Spieler. Hier gibt es neben den Eigenschaften, wo im Grid-Layout es sich befindet auch Text Eigenschaften, Text Größe (FontSize), Farbe (FontColor als Wert, also was in den "" steht sind Html Farbcodes möglich also zb #FFFFFF für schwart). Diese Eigenschften (Variablen) besitzt jedes UIElement, welches Text beinhaltet. Der Text selbst wird zwischen den Anfangs und End Tag des Labels geschrieben. x:Name="" wird benötigt um später im C# Code auf das Objekt zuzugreifen.


    <Label x:Name="ActivePlayer" Grid.Row="3" Grid.Column="1" FontSize="15">active Player</Label>
    	


    So ähnlich wie wir den Text anzeigen können wir auch Texteingaben annehmen, um die namen der Spieler zu erstellen. Deshalb erstellen wir zwei TextBox Elemente für die Spielernahmen. Hier geben wir das ¨bliche an wie den Ort im GridView und die Textgröße. Um das ganze zu verschönerne können wir noch einstellen, dass es Vertical oder Horizontal rechts, links oder mittig angeordnet ist. Durch VerticalAlignment="Center" wird die TextBox in der Breite so groß, in der Höhe nur so hoch wie benötigt.


    <TextBox Name="Player1" Grid.Column="0" VerticalAlignment="Center" FontSize="18">Player1</TextBox>
    <TextBox Name="Player2" Grid.Column="2" VerticalAlignment="Center" FontSize="18">Player2</TextBox>
    	


    Zum Starten brauchen wir nun nat¨rlich auch noch einen Button. Wie wir den klick erkennen kommt erst später.


    <Button Name="Start" Grid.Column="4" VerticalAlignment="Center" FontSize="18">Start</Button>
    	


    Nun erstellen wir noch das Spielfeld, dieses besteht bei uns aus 9 ToggleButton Elementen. Warum nehmen wir diese und nicht einfach Button Elemente? Die Sache ist diese, ToggleButton Elemente können verschiedene Werte annehmen, "null" falls noch keine Zuweisung stattgefunden hat, "true" falls er sozuagen an ist und "false" falls aus.


    	  <ToggleButton Grid.Column="0" Grid.Row="0"> </ToggleButton>
          <ToggleButton Grid.Column="0" Grid.Row="2"> </ToggleButton>
          <ToggleButton Grid.Column="0" Grid.Row="4"> </ToggleButton>
          <ToggleButton Grid.Column="2" Grid.Row="0"> </ToggleButton>
          <ToggleButton Grid.Column="2" Grid.Row="2"> </ToggleButton>
          <ToggleButton Grid.Column="2" Grid.Row="4"> </ToggleButton>
          <ToggleButton Grid.Column="4" Grid.Row="0"> </ToggleButton>
          <ToggleButton Grid.Column="4" Grid.Row="2"> </ToggleButton>
          <ToggleButton Grid.Column="4" Grid.Row="4"> </ToggleButton>
    	


    Jetzt haben wir, wenn wir das Programm starten, ein Recht trostlos aussehendes Spielfeld und alles ist noch ohne Funktion. Um das ganze nun schöner zu machen, fügen wir den Elementen sogenannte Styles hinzu. Dort kann man zB. Bilder als Hintergrund, möchte oder das etwas geschieht, wenn die Maus das Element berührt. Das lohnt sich vor allem dann, wenn wir viele Elemente haben die gleich aussehen sollen. In unserem Beispiel, nutzen wir das nur für die Spielfeld Buttons, da nur diese häufig vorkommen. Für die Bilder erstellen wir nun noch einen Ordner (sowie en "SourceCode" Ordner) und nennen diesen "Graphic". Dort fügen wir die Bilder ein, diese findet ihr im Blog unter Downloads -> TicTacToe Dateien oder direkt hier. Eingefügt werden die Bilder entweder per Drag & Drop oder über rechtsklick -> "Hinzufügen" -> "Vorhandenes Element ...".
    Fangen wir nun endlich mit dem ersten richtigen Style an. Dieser beginnt wie zu erwarten mit "Style". Die Eigenschaften sind hier aber Grundlegen anders. So muss man den "TargetType" angeben, also der Zieltyp des UIElements auf den der Style angewendet werden soll, in unserem Fall ein ToggleButton. Über x:Key="Name" geben wir einen öffentlichen Bezeichner an, wodurch der Style genutzt werden kann, da dieser natürlich noch in den ToggleButton eingebunden, bzw. als Style Variable festgelegt werden muss. Innerhalb des Styles wird ein Setter erstellt, dieser enthält das "Template" also die Vorgabe, wie der Button auszusehen hat.


    	 <Style TargetType="{x:Type ToggleButton}" x:Key="FieldButton">
                <Setter Property="Template"></Setter>
            </Style>
    	

    Weiter geht es innerhalb des Setters, hier setzten wir über "Setter.Value" seine Eigenschaften. Benutzt wird hier ein ControlTemplate, ein UIElement ist vom Typ "Control" (ganauer Windows.System.Controls), auch hier wird der Zieltyp angegeben.
    	<Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToggleButton}"> </ControlTemplate>
                    </Setter.Value>
    	

    Nun fangen wir an die Bilder hinzuzufügen, dazu erstellen wir innerhalb eines ControlTemplate ein StackPanel und zwar nehmen wir hier den Typ VirtualizingStackPanel, das ist Resourcensparender, warum genau könnt ihr selbst im Internet nachlesen. Hier als Beispiel wird das "null.png" angezeigt, dafür geben wir als Source einfach den Pfad "Graphic/null.png" an.


    	 <VirtualizingStackPanel Orientation="Horizontal" >
                                <Image Name="PART_Image" Source="Graphic/null.png" VerticalAlignment="Center" RenderOptions.BitmapScalingMode="HighQuality" ></Image>
                            </VirtualizingStackPanel>
    	

    Um je nach Status (nichts, an, aus) ein anderes Hintergrundbild anzuzeigen brauchen wir noch Trigger im ControlTemplate, diese fügen wir über ControlTemplate.Triggers hinzu.


    	<ControlTemplate.Triggers> </ControlTemplate.Triggers>
    	

    Hier fügen wir nun mehrere Trigger ein. Als erstes für "true" das Steht für Spieler 1 (Tic) also das X Bild. Im Setter des Triggers geben wir als Property "Source" an, da der Pfad des Hintergrundbildes geändert werden soll. Als TargetName geben wir den Namen des Bildes an, dieser lautet hier "PART_Image". Als Value den Pfad des Bildes, in diesem Fall "Graphic/x.png";


    	<!--x Bild -->
                                <Trigger Property="IsChecked" Value="True">
                                    <Setter Property="Source" Value="Graphic/x.png" TargetName="PART_Image"/>
                                </Trigger>
    	

    Jetzt erstellen wir das selbe noch für das Bild des zweiten Spielers also den Wert "false";


    	<!--is playing and mous is not over-->
                                <Trigger Property="IsChecked" Value="False">
                                    <Setter Property="Source" Value="Graphic/o.png" TargetName="PART_Image"/>
                                </Trigger>
    	

    Um dem ganzen jetzt noch einen etwas professionelleren touch zu geben, machen wir, dass wenn die Maus das Bild berührt dieses ein wenig größer wird. Dazu fügen wir einen weiteren Trigger ein. Der Setter hat dieses mal die Eigenschaft (Property) Render Transform. Dadurch kann man das was gerendert wird verändern. Als Value geben wir "0.5 , 0.5" an, das ist der Ort wo der Mittelpunkt der Transformation ist, 0.5 macht hier die Mitte der Breite und die Mitte der Höhe. Als Setter.Value machen wir ScaleTransform, dadurch wird das Bild größer skaliert. Hier könnt ihr später mit den Werten herumspielen ich nehme 1.2, dadurch wird das Bild merklich größer aber nicht übergroß. Das gebe ich als ScaleX und ScaleY and, da unser Bild natürlich die Seitenverhältnisse beibehalten soll.


    	  <Trigger>
    	 <Setter Property="RenderTransformOrigin" Value="0.5, 0.5"/>
                                    <Setter Property="RenderTransform">
                                        <Setter.Value>
                                            <ScaleTransform ScaleX="1.2" ScaleY="1.2" />
                                        </Setter.Value>
                                    </Setter>
    								</Trigger>
    	

    Diesen Style übernehmen wir nun noch für jeden ToggleButton, wir fügen also "Style="{StaticResource FieldButton}"" ein, da unser Style den Namen "FieldButton" hat und in den Resources definiert ist.


    	 <ToggleButton Grid.Column="0" Grid.Row="0" Style="{StaticResource FieldButton}"> </ToggleButton>
    
    	

    Jetzt könnt ihr einmal oben auf "Start" starten und die Funktion des layouts überprüfen, da jetzt bei Klicks aber noch nichts passiert müssen wir diese erstmal erkennen. und Zwar fügen wir in jeden ToggleButton die Eigenschaft Click="fieldBtnlick" ein, hierdurch wird bei einem Klick die Methode mit dem angegebenen Namen aufgerufen. Das sieht wie folgt aus.


    		 <ToggleButton Click="fielBtnClick" Grid.Column="0" Grid.Row="0" Style="{StaticResource FieldButton}"> </ToggleButton>
    
    	

    Das selbe machen wir noch mit dem Start Button, nur das wir hier die Methode "startBtnClick" aufrufen. Die beiden Methoden müssen wir jetzt noch in der datei "MainWindow.xaml.cs" hinzufügen.


    	 private void startbtnClick(object sender, RoutedEventArgs e)
            {
    
    		}
    	

    Zur besseren Code Übersicht und Struktur erstellen wir noch schnell ein paar Ordner und Klassen dann geht es los

  • Rechtsklick auf den Projektordner -> Hinzufügen -> Neuer Ordner, dies soll der Ordner für den Hauptcode werden, weshalb ich ihn "SourceCode" nenne, innerhalb dieses Ordners erstellen wir 2 weitere "Interfaces" und "MVC" was es damit auf sich hat wird im weiteren Verlauf erklärt.
  • Jetzt erstellen wir ein paar Interfaces, rechtsklicken dazu den "Interfaces" Ordner -> Hinzufügen -> Neues Element -> Schnittstelle (Visual C#), als Name "IConroller.cs" -> "Hinzufügen"
  • So erstellen wir nur ein weiteres Interface "IView.cs"
  • Klassen werden nahezu identisch erstellt, nur das anstelle "Schnittstelle" "Klasse" ausgewählt wird, hier erstellen wir in den Ordner "MVC" die Klassen "Model.cs", "View.cs" und "Controller.cs". Für die die es bemerkt haben, ja MVC (Model-View-Controller) setzt sich aus diesen Klassen zusammen und ist ein Muster zur Software Entwicklung. Dieses Muster verwenden wir, da es alles nötige bietet, was uns das portieren auf Android im späteren Verlauf erleichtert.
  • Im Ordner "SourceCode" erstellen wir noch "Player.cs" und "Game.cs", welches unsere spätere Spiele und Spieler Logik darstellen wird.

  • Es sieht nun wie folgt aus:

    Später füllen wir das ganze noch mit Logik, also die MainWindow.cs Klasse. Nun gehts aber erstmal weiter zur Programmlogik. Android Layout mache ich danach, aber ihr wollt sicher erst einmal sehen, dass etwas funtioniert.


    weiter ->