I am trying to write a game for myself using Xamarin. The problem is that I can't make the colors dynamically change after the start button is pressed. At first I tried to do it through the BackgroundColor property, but because it didn't work, I decided to use dynamic resources, but that doesn't work either. Please help me find a mistake in the code or algorithm.
P.s
The idea of the game is this: to reproduce a sequence of colors from memory, the difficulty gradually increases: at first only one button is highlighted, then there are already two buttons, and so on. Victory is achieved if you accurately reach the 20th stage (reproduce a sequence of 20 colors) and complete it.
P.p.s
I ran the app on my phone - Xiaomi Poco X3 NFC, my OS is MIUI 12 (Android 10).
MainPage.xaml.cs
using System;
using System.Threading;
using Xamarin.Forms;
namespace JustRepeat
{
public partial class MainPage : ContentPage
{
public readonly MethodsCollection methodsCollection;
public readonly Button[] buttonsMas;
public MainPage()
{
InitializeComponent();
methodsCollection = new MethodsCollection(this);
buttonsMas = new Button[] { one, two, three, four, five, six, seven, eight, nine };
}
private void Button_Clicked(object sender, EventArgs e)
{
methodsCollection.PlaySequence();
startstop.IsEnabled = false;
}
private void Buttons_Clicked(object sender, EventArgs e)
{
bool result = methodsCollection.CheckSequence((Button)sender, out int currStg);
if (!result)
{
DisplayAlert("Notification", $"You lose.\nThe last passed stage: {currStg}.", "OK");
startstop.IsEnabled = true;
return;
}
else if (result & currStg == 20)
{
DisplayAlert("Notification", "You won!", "OK");
startstop.IsEnabled = true;
}
}
}
public class MethodsCollection
{
private readonly MainPage mainPage;
private Color[] colors;
private int[] sequence;
private int currentStage = 1;
private int currentMember = 0;
public MethodsCollection(MainPage mP)
{
mainPage = mP;
}
private int[] GenerateSequence(int currentNumber)
{
Random random = new Random();
int[] posMas = new int[currentNumber];
for (int i = 0; i < posMas.Length; i )
{
posMas[i] = random.Next(1, 10);
}
return posMas;
}
public void PlaySequence()
{
Random random = new Random();
sequence = GenerateSequence(currentStage);
colors = new Color[currentStage];
for (int i = 0; i < sequence.Length; i )
{
colors[i] = Color.FromRgb(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256));
mainPage.Resources[string.Format("{0}", sequence[i])] = colors[i];
Thread.Sleep(1000);
mainPage.Resources[string.Format("{0}", sequence[i])] = Color.LightGray;
}
}
public bool CheckSequence(Button btn, out int stage)
{
int pos = 0;
for (int i = 0; i < mainPage.buttonsMas.Length; i )
{
if (btn == mainPage.buttonsMas[i])
{
pos = i;
}
}
if (currentStage == 20)
{
if (pos == sequence[currentMember])
{
SetColor();
ClearVariables();
stage = currentStage;
currentStage = 1;
return true;
}
ClearVariables();
stage = currentStage - 1;
currentStage = 1;
return false;
}
if (currentStage - currentMember == 1)
{
if (pos == sequence[currentMember])
{
SetColor();
ClearVariables();
stage = currentStage ;
return true;
}
ClearVariables();
stage = currentStage - 1;
currentStage = 1;
return false;
}
if (pos == sequence[currentMember])
{
currentMember ;
stage = currentStage;
return true;
}
else
{
ClearVariables();
stage = currentStage - 1;
currentStage = 1;
return false;
}
}
private void SetColor()
{
mainPage.Resources[string.Format("{0}", sequence[currentMember])] = colors[currentMember];
Thread.Sleep(1000);
mainPage.Resources[string.Format("{0}", sequence[currentMember])] = Color.LightGray;
}
private void ClearVariables()
{
currentMember = 0;
sequence = null;
colors = null;
}
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="JustRepeat.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="1">LightGray</Color>
<Color x:Key="2">LightGray</Color>
<Color x:Key="3">LightGray</Color>
<Color x:Key="4">LightGray</Color>
<Color x:Key="5">LightGray</Color>
<Color x:Key="6">LightGray</Color>
<Color x:Key="7">LightGray</Color>
<Color x:Key="8">LightGray</Color>
<Color x:Key="9">LightGray</Color>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Grid VerticalOptions="FillAndExpand" >
<Button BackgroundColor="{DynamicResource Key=1}" Clicked="Buttons_Clicked" Grid.Column="0" Grid.Row="0" x:Name="one"/>
<Button BackgroundColor="{DynamicResource Key=2}" Clicked="Buttons_Clicked" Grid.Column="1" Grid.Row="0" x:Name="two"/>
<Button BackgroundColor="{DynamicResource Key=3}" Clicked="Buttons_Clicked" Grid.Column="2" Grid.Row="0" x:Name="three"/>
<Button BackgroundColor="{DynamicResource Key=4}" Clicked="Buttons_Clicked" Grid.Column="0" Grid.Row="1" x:Name="four"/>
<Button BackgroundColor="{DynamicResource Key=5}" Clicked="Buttons_Clicked" Grid.Column="1" Grid.Row="1" x:Name="five"/>
<Button BackgroundColor="{DynamicResource Key=6}" Clicked="Buttons_Clicked" Grid.Column="2" Grid.Row="1" x:Name="six"/>
<Button BackgroundColor="{DynamicResource Key=7}" Clicked="Buttons_Clicked" Grid.Column="0" Grid.Row="2" x:Name="seven"/>
<Button BackgroundColor="{DynamicResource Key=8}" Clicked="Buttons_Clicked" Grid.Column="1" Grid.Row="2" x:Name="eight"/>
<Button BackgroundColor="{DynamicResource Key=9}" Clicked="Buttons_Clicked" Grid.Column="2" Grid.Row="2" x:Name="nine"/>
</Grid>
<Button BackgroundColor="DarkGray" Text="Старт" Clicked="Button_Clicked" VerticalOptions="Fill" x:Name="startstop"/>
</StackLayout>
</ContentPage>
CodePudding user response:
Trying to do "anything" like setting the color of a button to displaying an error in xamarin needs to be done in the correct place in the correct way. In your code setting the color of the buttons is irrelevant to your Application because you do not invoke the changes.
Setting the color with:
Device.BeginInvokeOnMainThread(() =>
{
// Your method to change the color.
button.BackgroundColor = Color.Black; // Or whatever.
});
If you need a DisplayAlert for example you can do this async too.
Device.BeginInvokeOnMainThread(async () => // <-----
{
// You await the message
await DisplayAlert("Attention", "Color changed!", "OK);
});
Resolved this to the comments, but i posted an answer for future reference.
CodePudding user response:
Changing the value of a resource AFTER the page has been loaded won't affect the button color - it has already been read from the resource.
Instead, give the button an x:Name, so you can set its background color directly.
xaml:
<Button x:Name="button1" ... />
cs:
button1.BackgroundColor = ...;
OR might Device.BeginInvokeOnMainThread, as shown in stersym's answer.
(I added a new answer because that answer did not show x:Name.)
IMPORTANT: Intellisense won't know about button1 until after you build. What I do is add x:Name to XAML, build project, then add cs code that uses the name.
