C# :: Aufgabe #96
3 Lösungen

Das Damenproblem (Teil II)
Fortgeschrittener - C#
von ElPapito
- 07.05.2015 um 11:45 Uhr
Das verallgemeinerte Damenproblem besteht aus einem nxn Schachfeld und n Damen.
Die Aufgabe besteht darin die n Damen so zu positionieren, dass sie sich gegenseitig nicht bedrohen, d.h. es dürfen keine zwei Damen in der gleichen Zeile/Spalte/Diagonale stehen.
Schreibe ein kleines Programm, welches für n = 1, ..., 13 ausgibt wie viele Lösungen existieren.
Die Aufgabe besteht darin die n Damen so zu positionieren, dass sie sich gegenseitig nicht bedrohen, d.h. es dürfen keine zwei Damen in der gleichen Zeile/Spalte/Diagonale stehen.
Schreibe ein kleines Programm, welches für n = 1, ..., 13 ausgibt wie viele Lösungen existieren.
Lösungen:

using System; namespace trainYourProgrammer { class MainClass { static long PlaceQueens(int n, bool[,] blocked, int line) { if (line == n) return 1; long result = 0; for (int i = 0; i < n; i++) { if (blocked [i, line]) continue; result += PlaceQueens (n, PlaceQueenAt (i, line, n, blocked), line + 1); } return result; } static long PlaceQueens(int n) { bool[,] blocked = new bool[n, n]; long result = 0; for (int i = 0; i < n/2; i++) { //Ausnutzen der Symmetrie result += 2 * PlaceQueens (n, PlaceQueenAt (i, 0, n, blocked), 1); } if (n % 2 == 1) result += PlaceQueens (n, PlaceQueenAt (n / 2, 0, n, blocked), 1); return result; } static bool[,] PlaceQueenAt(int x, int y, int n, bool[,] blocked) { int[,] offset = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}}; bool[,] result = (bool[,]) blocked.Clone(); for(int dir = 0; dir < 8; dir++) { int x_ = x + offset[dir, 0]; int y_ = y + offset[dir, 1]; while (x_ >= 0 && x_ < n && y_ >= 0 && y_ < n) { result[x_, y_] = true; x_ += offset[dir, 0]; y_ += offset[dir, 1]; } } return result; } static void Main(string[] args) { for (int i = 1; i < 14; i++) { Console.WriteLine ("n = " + i.ToString().PadLeft(2) + ": " + PlaceQueens(i).ToString().PadLeft(6) + " Möglichkeiten"); } } } }
Hier mein Lösungsvorschlag. Da ich noch nicht sehr lange programmiere, mangelt es mir noch am effizienten Design und dem richtigen Kommentieren. Ich hoffe dennoch, dass mein Code verständlich ist. Eine Kontrolle des einzugebenden n habe ich mir erspart.
C#-Code

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Damenproblem { //Sebastian Woll, 06.08.15 class Program { static int n; //Feldlänge static int WievieleFelder; //Vermutete Menge der zu findenden Felder static string[][,] Felder = new string[WievieleFelder][,]; //Felder werden gespeichert static int CounterFelder = 0; // x-tes gefundenes Feld static int CounterRandom = 0; /* Seed des Zufallsgenerators für "mehr" Zufall * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * Mit diesem Counter werden bei n=8 immer 92 Lösungen gefunden, * ohne ihn erzeugt jeder Durchlauf ein anderes Ergebnis! */ static int CounterVergleichFALSE = 0; //Dient zur Terminierung des Programms nach zu vielen Fehlversuchen static void Main(string[] args) { Console.Write("\nWelche Länge sollen die Felder haben?\t"); n = Convert.ToInt32(Console.ReadLine()); WievieleFelder = n * n * n; Felder = new string[WievieleFelder][,]; for (int i = 0; i < Felder.GetUpperBound(0); i++) //JaggedArray wird gefüllt { Felder[i] = new string[n, n]; Felder[i][0, 0] = "."; //Der Punkt dient als Zeichen für "FREIES FELD" } //Feld generieren NeuesFeld: //Variablen deklarieren/zurücksetzen string[,] Feld = new string[n, n]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { Feld[i, j] = "."; } } Random Zufall = new Random(CounterRandom); CounterRandom++; int x, y, CounterDame, CounterWuerfeln; CounterWuerfeln = 0; CounterDame = 0; //Variablen deklarieren/zurücksetztn ENDE //Feld füllen do { NeuWuerfeln: CounterWuerfeln++; x = Zufall.Next(n); //Dame bekommt zufällige Position y = Zufall.Next(n); if (Feld[x, y] == ".") // Ist die Position frei und nicht bedroht? { Felderstreichen(x, y, Feld, CounterDame); CounterDame++; //Nächste Dame } else { if (CounterWuerfeln > n * n * 10) //Wuerfeln gegen endlos? / Position nicht lösbar? { break; //Abbruch bei zu langem Wuerfeln } else { goto NeuWuerfeln; } } } while (CounterDame < n); //Feld füllen ENDE //Feld generieren ENDE if (CounterVergleichFALSE > 1000) //Wenn nach 1000 Treffern kein neues Feld gefunden wird, endet das Programm mit der Ausgabe des Zählers { Console.WriteLine("\nEs wurden {0} Felder gefunden.", CounterFelder); } else { //Damen alle da? if (CounterDame == n) { //Feld schonmal gefunden? if (ArraysVergleichen(Feld, Felder) == false) //Verlgeich funktioniert nicht { CounterVergleichFALSE = 0; FeldAusgeben(Feld); Felder[CounterFelder] = Feld; CounterFelder++; //FeldAusgeben(Feld); if (CounterFelder < WievieleFelder - 1) //Weniger als WievieleFelder Felder wurden gefunden { //CounterFelder++; goto NeuesFeld; } else { Console.WriteLine("Es wurden {0} Felder gefunden", CounterFelder); } } else //Feld wurde schonmal gefunden { CounterVergleichFALSE++; goto NeuesFeld; } //Feld schonmal gefunden? ENDE } else //Damen weniger als n { goto NeuesFeld; } //Damen alle da? ENDE } Console.ReadLine(); }//Ende Main //Felder streichen static void Felderstreichen(int x, int y, string[,] Feld, int CounterDame) { //Der Strich "-" bedeutet, dass das Feld bedroht ist. //Striche waagerecht und senkrecht for (int i = 0; i < n; i++) { Feld[x, i] = "-"; Feld[i, y] = "-"; } Feld[x, y] = "D"; //Diagonale nach rechts unten for (int i = 1; (i < n - x) && (i < n - y); i++) { Feld[x + i, y + i] = "-"; } //Diagonale nach links oben for (int i = 1; (i <= x) && (i <= y); i++) { Feld[x - i, y - i] = "-"; } //Diagonale nach rechts oben for (int i = 1; (i <= x) && (i < n - y); i++) { Feld[x - i, y + i] = "-"; } //Diagonale nach links unten for (int i = 1; (i < n - x) && (i <= y); i++) { Feld[x + i, y - i] = "-"; } }//FelderstreichenEnde //FeldAusgeben static void FeldAusgeben(string[,] Feld) { Console.WriteLine(); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { Console.Write(Feld[i, j] + " "); } Console.WriteLine(); } Console.WriteLine(); } //FeldAusgeben ENDE //Arrays Vergleichen static bool ArraysVergleichen(string[,] NeuesFeld, string[][,] GefundeneFelder) { int CounterStimmt = 0; for (int i = 0; i < WievieleFelder - 1; i++) { CounterStimmt = 0; for (int j = 0; j < n; j++) { for (int h = 0; h < n; h++) { if (NeuesFeld[j, h] == GefundeneFelder[i][j, h]) { CounterStimmt++; } } } if (CounterStimmt == n * n) { return true; } } return false; } //ArraysVergleichen ENDE //VERGLEICH FUNKTIONIERT NICHT, wurde durch ArrayVergleichen ersetzt // static bool IstFeldSchonVorhanden(string[,] NeuesFeld, string[][,] GefundeneFelder) //{ // for (int i = 0; i < WievieleFelder - 1; i++) // { // if (NeuesFeld.Equals(GefundeneFelder[i])) // { // FeldAusgeben(NeuesFeld); // Console.WriteLine(); // FeldAusgeben(GefundeneFelder[i]); // return true; // } // } // return false; //} //Sebastian Woll, 06.08.15 } }
Hier eine Lösung in WPF. Über die Checkbox (draw) und den Slider (sleep) kann man eine graphische
Simulation steuern. Die RadioButton beeinflussen bei gesetzter (true) Checkbox weitere graphische Eigenschaften.
Braucht man nur eine Consolenanwendung, genügt es, eine Instanz von class QueensProblem mit bo_run_loop = true
zu erzeugen. Die Ergebnis sind über
C#-Code
abfragbar.
C#-Code
C#-Code
Simulation steuern. Die RadioButton beeinflussen bei gesetzter (true) Checkbox weitere graphische Eigenschaften.
Braucht man nur eine Consolenanwendung, genügt es, eine Instanz von class QueensProblem mit bo_run_loop = true
zu erzeugen. Die Ergebnis sind über

public int glob_counter { get; private set; } public List<int[]> solutions { get; private set; }
abfragbar.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Forms; namespace aufgabe_95_WPF { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private struct GUI { public int nqueens; public bool bo_draw; public int nsleep; public bool bo_rb_l; public bool bo_rb_r; } GUI gui = new GUI(); void update_gui() { gui.nqueens = (int)slider_nqueens.Value; gui.nsleep = (int)slider_sleep.Value; gui.bo_draw = (bool)cb_1.IsChecked; gui.bo_rb_l = (bool)rb_l.IsChecked; gui.bo_rb_r = (bool)rb_r.IsChecked; this.lb_nqueens.Content = $"queens: {this.gui.nqueens,2}"; this.lb_sleep.Content = $"sleep(ms): {this.gui.nsleep,4}"; this.textBox.Text = sguistate(); } string sguistate() => $"n_queens: {gui.nqueens}{NL}n_sleep: {gui.nsleep}{NL}bo_draw: {gui.bo_draw}{NL}bo_short_steps: {gui.bo_rb_l}{NL}bo_big_steps: {gui.bo_rb_r}{NL}"; string NL = Environment.NewLine; System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); bool[][] board; int[] queens; QueensProblem qp; int glob_counter; List<int[]> glob_solutions = new List<int[]>(); double canvas_h, canvas_w; string textbox_content; private void bt_reset_Click(object sender, RoutedEventArgs e) { update_gui(); board = new bool[gui.nqueens][]; for (var i = 0; i < board.Length; i++) board[i] = new bool[gui.nqueens]; queens = Enumerable.Repeat(-1, gui.nqueens).ToArray(); qp = new QueensProblem(gui.nqueens); this.textBox.Clear(); canvas_h = this.Canvas.ActualHeight; canvas_w = this.Canvas.ActualWidth; textbox_content = $"canvas: {canvas_h.ToString("n0")} x {canvas_h.ToString("n0")}"; this.textBox.Text = textbox_content + NL + sguistate(); update_canvas(this.board, this.queens); lb_result.Content = string.Empty; glob_counter = 0; glob_solutions = new List<int[]>(); } private void bt_run_Click(object sender, RoutedEventArgs e) { bt_reset_Click(null, null); sw.Restart(); int[] queens_copy = null; bool[][] board_copy = null; int max_firfirst_col = (int)Math.Ceiling(gui.nqueens / 2.0); for (int first_col = 0; first_col < max_firfirst_col; first_col++) { queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); queens_copy[first_col] = 0; board_copy = board.DeepCopy(); int[] coord = qp.attacked[0][first_col]; for (var i = 0; i < coord.Length; i += 2) board_copy[coord[i]][coord[i + 1]] = true; if (gui.bo_draw) { update_canvas(board_copy, queens_copy, gui.bo_rb_l); System.Threading.Thread.Sleep(gui.nsleep); } int found = 0; run(1, board_copy, queens_copy, ref found, gui.bo_draw, true); int faktor = 2; if (gui.nqueens % 2 == 1 && first_col == max_firfirst_col - 1) faktor = 1; glob_counter += faktor * found; lb_result.Content = $"after loop {first_col + 1} glob_counter is {glob_counter.ToString("n0")}"; System.Windows.Forms.Application.DoEvents(); } update_canvas(board_copy, glob_solutions[glob_solutions.Count - 1]); $"ready {glob_counter} {sw.Elapsed.Duration()}".m(); } private void run(int reihe, bool[][] board, int[] queens, ref int found, bool bo_draw, bool bo_run) { if (!bo_run) return; bool[][] board_copy = board.DeepCopy(); int[] queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); if (reihe == gui.nqueens) { if (gui.bo_draw) { update_canvas(board_copy, queens_copy); System.Threading.Thread.Sleep(gui.nsleep); } found++; glob_solutions.Add(queens); this.lb_result.Content = $"found so far: {glob_solutions.Count.ToString("n0")}"; System.Windows.Forms.Application.DoEvents(); return; } else { List<int> empty = new List<int>(); for (var col = 0; col < board[reihe].Length; col++) if (!board[reihe][col]) empty.Add(col); if (empty.Count == 0) return; foreach(int ccol in empty) { board_copy = board.DeepCopy(); queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); queens_copy[ccol] = reihe; for (var j = 0; j < qp.attacked[reihe][ccol].Length; j += 2) board_copy[qp.attacked[reihe][ccol][j]][qp.attacked[reihe][ccol][j + 1]] = true; if (bo_draw) { update_canvas(board_copy, queens_copy, gui.bo_rb_l); System.Threading.Thread.Sleep(gui.nsleep); } run(reihe + 1, board_copy, queens_copy, ref found, bo_draw, bo_run); } return; } } private void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (bo_loaded) update_gui(); } bool bo_loaded = false; private void Window_Loaded(object sender, RoutedEventArgs e) { bo_loaded = true; } private void cb_1_Click(object sender, RoutedEventArgs e) { update_gui(); } private void bt_count_Click(object sender, RoutedEventArgs e) { bt_reset_Click(null, null); sw.Restart(); QueensProblem qp2 = new QueensProblem(gui.nqueens, true); string s_erg = $"{qp2.glob_counter.ToString("n0")} position found in {sw.Elapsed.Duration()}"; this.textBox.Text = s_erg; s_erg.m(); } private void update_canvas(bool[][] board, int[] queens, bool bo_attacked = false) { this.Canvas.Children.Clear(); double w = Math.Min(canvas_h, canvas_w) / gui.nqueens; for(var i = 0; i < board.Length; i++) for(var j = 0; j < board[i].Length; j++) { double y = i * w; double x = j * w; System.Windows.Shapes.Rectangle rect; rect = new System.Windows.Shapes.Rectangle(); rect.Stroke = new SolidColorBrush(Colors.Black); SolidColorBrush c = new SolidColorBrush(Colors.Wheat); if (bo_attacked && board[i][j]) c = new SolidColorBrush(Colors.Red); else { if((i % 2 == 0) && (j % 2 != 0) || (i % 2 != 0) && (j % 2 == 0)) c = new SolidColorBrush(Colors.SandyBrown); } if (queens[j] == i) { //c = new SolidColorBrush(Colors.Black); //falls queen2.jpg nicht vorhanden ist BitmapImage theImage = new BitmapImage (new Uri(@"..\..\pics_and_txt\queen2.jpg", UriKind.Relative)); ImageBrush myImageBrush = new ImageBrush(theImage); Canvas TempCanvas = new Canvas(); TempCanvas.Width = w; TempCanvas.Height = w; TempCanvas.Background = myImageBrush; Canvas.SetLeft(TempCanvas, x); Canvas.SetTop(TempCanvas, y); this.Canvas.Children.Add(TempCanvas); } else { rect.Fill = c; rect.Width = w; rect.Height = w; Canvas.SetLeft(rect, x); Canvas.SetTop(rect, y); this.Canvas.Children.Add(rect); } } System.Windows.Forms.Application.DoEvents(); } } public class QueensProblem { int n; bool[][] board; public int[][][] attacked { get; private set; } public int glob_counter { get; private set; } public List<int[]> solutions { get; private set; } private int[] queens; public QueensProblem(int nqueens, bool bo_run_loop = false) { this.n = nqueens; this.board = new bool[n][]; for (var i = 0; i < board.Length; i++) this.board[i] = new bool[n]; this.attacked = new int[n][][]; for (var i = 0; i < n; i++) { this.attacked[i] = new int[n][]; for (var j = 0; j < this.attacked[i].Length; j++) { List<int> temp = new List<int>(ListAttacked(i, j)); this.attacked[i][j] = new int[temp.Count]; temp.ToArray().CopyTo(this.attacked[i][j], 0); } } queens = Enumerable.Repeat(-1, n).ToArray(); List<int[]> solutions = new List<int[]>(); if (bo_run_loop) run_loop(); } List<int> ListAttacked(int iy, int ix) { List<int> result = new List<int>(); int counter = 1; for (int i = iy + 1; i < n; i++) { { result.Add(i); result.Add(ix); } if (ix - counter >= 0) { result.Add(i); result.Add(ix - counter); } if (ix + counter < n) { result.Add(i); result.Add(ix + counter); } counter++; } return result; } void run(int reihe, bool[][] board, int[] queens, ref int found) { bool[][] board_copy = DeepCopy(board); int[] queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); if (reihe == n) { found++; solutions.Add(queens_copy); return; } else { List<int> empty = new List<int>(); for (var col = 0; col < board[reihe].Length; col++) if (!board[reihe][col]) empty.Add(col); if (empty.Count == 0) return; foreach (int ccol in empty) { board_copy = DeepCopy(board); queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); queens_copy[ccol] = reihe; for (var j = 0; j < attacked[reihe][ccol].Length; j += 2) board_copy[attacked[reihe][ccol][j]][attacked[reihe][ccol][j + 1]] = true; run(reihe + 1, board_copy, queens_copy, ref found); } } } void run_loop() { int max_first_col = (int)Math.Ceiling(n / 2.0); for (int first_col = 0; first_col < max_first_col; first_col++) { int[] queens_copy = new int[queens.Length]; queens.CopyTo(queens_copy, 0); queens_copy[first_col] = 0; bool[][] board_copy = DeepCopy(board); int[] coord = attacked[0][first_col]; for (var i = 0; i < coord.Length; i += 2) board_copy[coord[i]][coord[i + 1]] = true; int found = 0; run(1, board_copy, queens_copy, ref found); int faktor = 2; if (n % 2 == 1 && first_col == max_first_col - 1) faktor = 1; glob_counter += faktor * found; } } T[][] DeepCopy<T>(T[][] x) { T[][] result = new T[x.Length][]; for (var i = 0; i < result.Length; i++) { result[i] = new T[x[i].Length]; x[i].CopyTo(result[i], 0); } return result; } } public static class tools { public static string ToMyString(this bool[][] a) { StringBuilder sb = new StringBuilder(); for (var i = 0; i < a.Length; i++) { for (var j = 0; j < a[i].Length; j++) { int temp = a[i][j] ? 1 : 0; sb.Append($"{temp,2}"); } sb.AppendLine(); } return sb.ToString(); } public static string ToMyString(this int[][][] a) { StringBuilder sb = new StringBuilder(); for(var i = 0; i < a.Length; i++) for(var j = 0; j < a[i].Length; j++) { sb.Append($"{i} {j}: "); for (var k = 0; k < a[i][j].Length; k++) sb.Append($"{a[i][j][k],2}"); sb.AppendLine(); } return sb.ToString(); } public static string ToMyString(this List<int> a) { StringBuilder sb = new StringBuilder(); for (var i = 0; i < a.Count; i++) sb.Append($"{a[i],2}"); return sb.ToString(); } public static void m(this string s) => System.Windows.MessageBox.Show(s); public static T[][] DeepCopy<T>(this T[][] x) { T[][] result = new T[x.Length][]; for (var i = 0; i < result.Length; i++) { result[i] = new T[x[i].Length]; x[i].CopyTo(result[i], 0); } return result; } public static string Duration(this TimeSpan ts) => String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); } }

<Window x:Class="aufgabe_95_WPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:aufgabe_95_WPF" mc:Ignorable="d" Title="n x n queens problem" Height="441.445" Width="745.018" FontFamily="Courier New" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" WindowState="Maximized" Loaded="Window_Loaded"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="5"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="75"/> <RowDefinition Height="1*"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <GridSplitter x:Name="gridSplitter" Grid.Column="1" HorizontalAlignment="Stretch" Margin="0" Grid.Row="1" Width="5"/> <StackPanel Margin="0" Orientation="Horizontal" Grid.ColumnSpan="3"> <Button x:Name="bt_reset" Content="reset" Width="75" Margin="5,0" Click="bt_reset_Click" Height="35"/> <GroupBox x:Name="gb_2" Height="50" Header=" slider queens " Margin="5,0"> <StackPanel Orientation="Horizontal"> <Slider x:Name="slider_nqueens" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,5,0" Minimum="1" Maximum="20" Width="100" TickPlacement="BottomRight" Value="8" ValueChanged="slider_ValueChanged"/> <Label x:Name="lb_nqueens" Content="queens: 8" Margin="0,0,15,0" HorizontalAlignment="Center" VerticalAlignment="Center"></Label> </StackPanel> </GroupBox> <GroupBox x:Name="gb_4" Height="50" Header=" drawing " Margin="5,0"> <StackPanel Orientation="Horizontal"> <CheckBox x:Name="cb_1" Content="drawing" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0" Click="cb_1_Click" IsChecked="True"></CheckBox> <RadioButton x:Name="rb_l" Content="short steps " IsChecked="True" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0"/> <RadioButton x:Name="rb_r" Content="big steps " HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0"/> </StackPanel> </GroupBox> <GroupBox x:Name="gb_3" Height="50" Header=" slider sleep "> <StackPanel Orientation="Horizontal"> <Slider x:Name="slider_sleep" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,5,0" Maximum="5000" Width="100" TickFrequency="250" TickPlacement="BottomRight" ValueChanged="slider_ValueChanged" Value="1000"/> <Label x:Name="lb_sleep" Content="sleep(ms): 0" Margin="0,0,20,0" HorizontalAlignment="Center" VerticalAlignment="Center"></Label> </StackPanel> </GroupBox> <Button x:Name="bt_run" Content="run" Width="75" Margin="15,0,5,0" Click="bt_run_Click" Height="35" /> <Button x:Name="bt_count" Content=" just count " Margin="15,0,5,0" Height="35" Click="bt_count_Click" /> </StackPanel> <StackPanel Height="25" Margin="0" Orientation="Horizontal" Grid.ColumnSpan="3" Grid.Row="2"> <Label x:Name="lb_version" HorizontalAlignment="Center" VerticalAlignment="Center" Content="2018/11/05 VERSION 2018/11/06" Margin="5,0,0,0"></Label> <Label x:Name="lb_result" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0"></Label> </StackPanel> <Canvas x:Name="Canvas" Margin="5,50,5,5" Grid.Row="1" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/> <Expander Grid.Column="2" Grid.Row="1" IsExpanded="True"> <GroupBox x:Name="groupBox" Grid.Column="2" Header="INFO" Margin="5" Grid.Row="1"> <TextBox x:Name="textBox" Margin="5" Text="TextBox" VerticalScrollBarVisibility="Auto" AcceptsReturn="True" AcceptsTab="True" HorizontalScrollBarVisibility="Auto" FontSize="36" FontWeight="Bold"/> </GroupBox> </Expander> </Grid> </Window>