Step 1
If not already, follow Setup and Start on how to Install and get Started with Visual Studio 2017 or in Windows 10 choose Start, and then from the Start Menu locate and select Visual Studio 2017.
Step 2
Once Visual Studio Community 2017 has started, from the Menu choose File, then New then Project…
Step 3
From New Project choose Visual C# from Installed, Templates then choose Cross Platform App (Xamarin) and then type in a Name and select a Location and then select Ok to create the Project
Step 4
Then in New Cross Platform App you need to select the Blank App Template then from UI Technology select Xamarin.Forms and in Code Sharing Strategy select Shared Project and then select Ok to continue
Step 5
The Xamarin Mac Agent will be displayed allowing connection to an Apple Mac but this can be dismissed with Close if running solely on Windows 10
Step 6
Then in New Universal Windows Project you need to select the Target Version to be Windows 10 Creators Update (10.0; Build 15063) and the Minimum Version to Windows 10 Creators Update (10.0; Build 15063) and then select Ok
Step 7
From the Menu choose Project, then Add New Item…
From the Add New Item choose Visual C# from Installed then choose Code then select Code File and then in the Name as EllipseView.cs and then select Add to add the file to the Project
Step 8
Then in the Code View for EllipseView.cs the following should be entered:
// Based on EllipseView by Jeff Prosise // With contributions by Julia Boichentsova #if __ANDROID__ using Android.Graphics; using System.ComponentModel; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(EllipseView), typeof(AndroidEllipseViewRenderer))] public class AndroidEllipseViewRenderer : VisualElementRenderer<EllipseView> { public AndroidEllipseViewRenderer() { SetWillNotDraw(false); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == EllipseView.StrokeWidthProperty.PropertyName || e.PropertyName == EllipseView.ColorProperty.PropertyName) { Invalidate(); } } protected override void OnDraw(Canvas canvas) { EllipseView element = Element; Rect rect = new Rect(); GetDrawingRect(rect); rect.Left += ((int)element.StrokeWidth); rect.Right -= ((int)element.StrokeWidth); rect.Top += ((int)element.StrokeWidth); rect.Bottom -= ((int)element.StrokeWidth); Paint paint = new Paint() { StrokeWidth = element.StrokeWidth, Color = element.Color.ToAndroid(), AntiAlias = true }; paint.SetStyle(element.IsFilled ? Paint.Style.FillAndStroke : Paint.Style.Stroke); canvas.DrawOval(new RectF(rect), paint); } } #endif #if __IOS__ using CoreGraphics; using System.ComponentModel; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(EllipseView), typeof(AppleEllipseViewRenderer))] public class AppleEllipseViewRenderer : VisualElementRenderer<EllipseView> { protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == EllipseView.StrokeWidthProperty.PropertyName || e.PropertyName == EllipseView.ColorProperty.PropertyName) { SetNeedsDisplay(); } } public override void Draw(CGRect rect) { using (CGContext context = UIGraphics.GetCurrentContext()) { double delta = Element.StrokeWidth / 2.0; CGRect item = new CGRect( rect.Left + delta, rect.Top + delta, rect.Size.Width - Element.StrokeWidth, rect.Size.Height - Element.StrokeWidth); CGPath path = CGPath.EllipseFromRect(item); context.AddPath(path); context.SetLineWidth(Element.StrokeWidth); if (Element.IsFilled) { context.SetFillColor(Element.Color.ToCGColor()); } context.SetStrokeColor(Element.Color.ToCGColor()); context.DrawPath(Element.IsFilled ? CGPathDrawingMode.FillStroke : CGPathDrawingMode.Stroke); } } } #endif #if WINDOWS_UWP using Windows.UI.Xaml; using Windows.UI.Xaml.Shapes; using Xamarin.Forms; using Xamarin.Forms.Platform.UWP; [assembly: ExportRenderer(typeof(EllipseView), typeof(EllipseViewRenderer))] public class EllipseViewRenderer : ViewRenderer<EllipseView, Ellipse> { protected override void OnElementChanged(ElementChangedEventArgs<EllipseView> e) { base.OnElementChanged(e); if (Element != null) { Ellipse ellipse = new Ellipse() { DataContext = Element }; ellipse.SetBinding(Ellipse.StrokeProperty, new Windows.UI.Xaml.Data.Binding() { Path = new PropertyPath("Color"), Converter = new ColorConverter() }); if(Element.IsFilled) { ellipse.SetBinding(Ellipse.FillProperty, new Windows.UI.Xaml.Data.Binding() { Path = new PropertyPath("Color"), Converter = new ColorConverter() }); } ellipse.SetBinding(Ellipse.StrokeThicknessProperty, new Windows.UI.Xaml.Data.Binding() { Path = new PropertyPath("StrokeWidth") }); SetNativeControl(ellipse); } } } #endif namespace Xamarin.Forms { public class EllipseView : View { public static readonly BindableProperty ColorProperty = BindableProperty.Create<EllipseView, Color>(p => p.Color, Color.Accent); public static readonly BindableProperty StrokeWidthProperty = BindableProperty.Create<EllipseView, float>(p => p.StrokeWidth, 5f); public static readonly BindableProperty IsFilledProperty = BindableProperty.Create<EllipseView, bool>(p => p.IsFilled, false); public Color Color { get { return (Color)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } } public float StrokeWidth { get { return (float)GetValue(StrokeWidthProperty); } set { SetValue(StrokeWidthProperty, value); } } public bool IsFilled { get { return (bool)GetValue(IsFilledProperty); } set { SetValue(IsFilledProperty, value); } } } }
Step 9
Then again from the Menu choose Project, then Add New Item…
From the Add New Item choose Visual C# from Installed then choose Code then select Code File and then in the Name as Library.cs and then select Add to add the file to the Project
Step 10
Then in the Code View for Library.cs the following should be entered:
using System.Linq; using System.Threading.Tasks; using Xamarin.Forms; public class Library { private const string app_title = "Four In Row"; private const int size = 7; private ContentPage _page; private bool _won = false; private int[,] _board = new int[size, size]; private int _player = 0; public void Show(string content, string title) { Device.BeginInvokeOnMainThread(() => { _page.DisplayAlert(title, content, "Ok"); }); } private async Task<bool> ConfirmAsync(string content, string title, string ok, string cancel) { return await _page.DisplayAlert(title, content, ok, cancel); } public bool Winner(int column, int row) { int total = 3; // Total Excluding Current int value = 0; // Value in Line int amend = 0; // Add or Remove // Check Vertical do { value++; } while (row + value < size && _board[column, row + value] == _player); if (value > total) { return true; } value = 0; amend = 0; // Check Horizontal - From Left do { value++; } while (column - value >= 0 && _board[column - value, row] == _player); if (value > total) { return true; } value -= 1; // Deduct Middle - Prevent double count // Then Right do { value++; amend++; } while (column + amend < size && _board[column + amend, row] == _player); if (value > total) { return true; } value = 0; amend = 0; // Diagonal - Left Top do { value++; } while (column - value >= 0 && row - value >= 0 && _board[column - value, row - value] == _player); if (value > total) { return true; } value -= 1; // Deduct Middle - Prevent double count // To Right Bottom do { value++; amend++; } while (column + amend < size && row + amend < size && _board[column + amend, row + amend] == _player); if (value > total) { return true; } value = 0; amend = 0; // Diagonal - From Right Top do { value++; } while (column + value < size && row - value >= 0 && _board[column + value, row - value] == _player); if (value > total) { return true; } value -= 1; // Deduct Middle - Prevent double count // To Left Bottom do { value++; amend++; } while (column - amend >= 0 && row + amend < size && _board[column - amend, row + amend] == _player); if (value > total) { return true; } return false; } private Grid Piece() { Grid grid = new Grid() { HeightRequest = 30, WidthRequest = 30, }; if (_player == 1) { BoxView line1 = new BoxView() { Color = Color.Red, HeightRequest = 30, WidthRequest = 2, VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center, Rotation = 45, }; BoxView line2 = new BoxView() { Color = Color.Red, HeightRequest = 30, WidthRequest = 2, VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center, Rotation = 135, }; grid.Children.Add(line1); grid.Children.Add(line2); } else if (_player == 2) { EllipseView circle = new EllipseView() { Color = Color.Blue, HeightRequest = 30, WidthRequest = 30, StrokeWidth = 2, VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center }; grid.Children.Add(circle); } return grid; } private bool Full() { for (int row = 0; row < size; row++) { for (int column = 0; column < size; column++) { if (_board[column, row] == 0) { return false; } } } return true; } private void Place(Grid grid, int column, int row) { for (int i = size - 1; i > -1; i--) { if (_board[column, i] == 0) { _board[column, i] = _player; Grid element = (Grid)grid.Children.Single( w => Grid.GetRow((Grid)w) == i && Grid.GetColumn((Grid)w) == column); element.Children.Add(Piece()); row = i; break; } } if (Winner(column, row)) { _won = true; Show($"Player {_player} has won!", app_title); } else if (Full()) { Show("Board Full!", app_title); } _player = _player == 1 ? 2 : 1; // Set Player } private void Add(Grid grid, int row, int column) { Grid element = new Grid() { HeightRequest = 40, WidthRequest = 40, BackgroundColor = Color.WhiteSmoke, }; TapGestureRecognizer tapped = new TapGestureRecognizer(); tapped.Tapped += (sender, e) => { if (!_won) { element = ((Grid)(sender)); row = (int)element.GetValue(Grid.RowProperty); column = (int)element.GetValue(Grid.ColumnProperty); if (_board[column, 0] == 0) // Check Free Row { Place(grid, column, row); } } else { Show("Game Over!", app_title); } }; element.GestureRecognizers.Add(tapped); element.SetValue(Grid.ColumnProperty, column); element.SetValue(Grid.RowProperty, row); grid.Children.Add(element); } private void Layout(ref Grid grid) { _player = 1; grid.Children.Clear(); grid.ColumnDefinitions.Clear(); grid.RowDefinitions.Clear(); // Setup Grid for (int index = 0; (index < size); index++) { grid.RowDefinitions.Add(new RowDefinition()); grid.ColumnDefinitions.Add(new ColumnDefinition()); } // Setup Board for (int column = 0; (column < size); column++) { for (int row = 0; (row < size); row++) { Add(grid, row, column); _board[row, column] = 0; } } } public async void New(ContentPage page, Grid grid) { _page = page; Layout(ref grid); _won = false; _player = await ConfirmAsync("Who goes First?", app_title, "X", "O") ? 1 : 2; } }
Step 11
In the Solution Explorer select MainPage.xaml from the Shared Project
Step 12
From the Menu choose View and then Open
Step 13
The XAML View will be displayed, and in this remove the Label then between the ContentPage and /ContentPage elements, enter the following XAML:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid Grid.Row="0" x:Name="Display" HorizontalOptions="Center" VerticalOptions="Center"/> <Grid Grid.Row="1" Margin="10"> <Button Text="New" HorizontalOptions="Center" WidthRequest="100" Clicked="New_Clicked"/> </Grid> </Grid>
It should appear as such:
Step 14
In the Solution Explorer select the Expand arrow next to MainPage.xaml to open MainPage.cs, then select this from the Shared Project
Step 15
From the Menu choose View and then Open
Step 16
Once in the Code View, below the public MainPage() { … } the following Code should be entered:
Library library = new Library(); private void New_Clicked(object sender, EventArgs e) { library.New(this, Display); }
It should then appear as such:
Step 17
That completes the application, so from the Menu choose Build and then Build Solution
Step 18
Then from the Menu choose Debug then Start Debugging to run the Application in the Android Emulator
Step 19
Once started the Application should then appear in the Android Emulator
Step 20
After the Application has started running you select New, then choose X or O then can start playing, first can choose to play as X or O, you can win by getting four pieces in a horizontal, vertical or diagonal row!
Step 21
To Exit the Application select Stop in Visual Studio
Step 22
Another option is to run as a Universal Windows Application. From Solution Explorer select the Project ending with .UWP (Universal Windows), then from the Menu choose Project then Set as StartUp Project
Step 23
Then again from the Menu choose Build and then select the Deploy option ending with .UWP
Step 24
Then once again from the Menu choose Debug then Start Debugging to run the Application in Windows 10
Step 25
Once started the Application should then appear
Step 26
After the Application has started running you select New, then choose X or O then can start playing, first can choose to play as X or O, you can win by getting four pieces in a horizontal, vertical or diagonal row!
Step 27
To Exit the Application select Stop in Visual Studio
Step 28
Also if you have Mac with the Xamarin tools installed and have enabled and connected to the Xamarin Mac Agent. From Solution Explorer select the Project ending with .iOS, then from the Menu choose Project then Set as StartUp Project
Step 29
Then once again from the Menu choose Debug then Start Debugging to run the Application for iOS
Step 30
Once started the Application should then appear
Step 31
After the Application has started running you select New, then choose X or O then can start playing, first can choose to play as X or O, you can win by getting four pieces in a horizontal, vertical or diagonal row!
Step 32
To Exit the Application select Stop in Visual Studio
Thanks for Julia Boichentsova for helping with iOS screenshots