Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
timunie committed Dec 9, 2024
1 parent a468a4b commit 69f39fb
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
Expand All @@ -11,35 +12,34 @@ public partial class SnowFlakeGameViewModel : ViewModelBase
{
public SnowFlakeGameViewModel()
{
_gameTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(50), DispatcherPriority.Background, OnGameTimerTick);
_gameTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(100), DispatcherPriority.Background, OnGameTimerTick);
}

private void OnGameTimerTick(object? sender, EventArgs e)
{
OnPropertyChanged(nameof(TimeRemaining));
OnPropertyChanged(nameof(SecondsRemaining));
OnPropertyChanged(nameof(MilliSecondsRemaining));

if (TimeRemaining < TimeSpan.Zero)
if (MilliSecondsRemaining <= 0)
{
_gameTimer.Stop();
_stopwatch.Stop();
IsGameRunning = false;
}
}

private readonly DispatcherTimer _gameTimer;
private DateTime _gameStartTime = DateTime.MinValue;

private readonly Stopwatch _stopwatch = new Stopwatch();
public ObservableCollection<SnowFlake> SnowFlakes { get; } = new();

[ObservableProperty] private bool _isGameRunning;
[ObservableProperty] private int _Score;
[ObservableProperty] private int _score;

[ObservableProperty] [NotifyPropertyChangedFor(nameof(SecondsGameDuration))]
TimeSpan _GameDuration = TimeSpan.Zero;
[ObservableProperty] [NotifyPropertyChangedFor(nameof(MilliSecondsGameDuration))]
private TimeSpan _gameDuration = TimeSpan.Zero;

public TimeSpan TimeRemaining => _gameStartTime + GameDuration - DateTime.Now;
public double SecondsRemaining => TimeRemaining.TotalSeconds;
public double SecondsGameDuration => GameDuration.TotalSeconds;
public double MilliSecondsRemaining => (GameDuration - _stopwatch.Elapsed).TotalMilliseconds;
public double MilliSecondsGameDuration => GameDuration.TotalMilliseconds;

[RelayCommand]
private void StartGame()
Expand All @@ -56,7 +56,7 @@ private void StartGame()
Random.Shared.NextDouble() / 5 + 0.1));
}

_gameStartTime = DateTime.Now;
_stopwatch.Restart();
GameDuration = TimeSpan.FromMinutes(1);
IsGameRunning = true;
_gameTimer.Start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,46 @@
Icon="/Assets/avalonia-logo.ico"
Title="SnowFlakesControl">

<Window.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0%,100%">
<GradientStop Offset="0" Color="DarkBlue" />
<GradientStop Offset=".85" Color="LightSkyBlue" />
<GradientStop Offset=".85" Color="LimeGreen" />
<GradientStop Offset="1" Color="DarkGreen" />
</LinearGradientBrush>
</Window.Background>

<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:SnowFlakeGameViewModel />
</Design.DataContext>

<Panel>
<!-- Just some Background for a nicer UI -->
<Panel.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0%,100%">
<GradientStop Offset="0" Color="DarkBlue" />
<GradientStop Offset=".85" Color="LightSkyBlue" />
<GradientStop Offset=".85" Color="LimeGreen" />
<GradientStop Offset="1" Color="DarkGreen" />
</LinearGradientBrush>
</Panel.Background>

<!-- This is our SnowFlakesControl. It can be used like any other Control. -->
<controls:SnowFlakesControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
IsRunning="{Binding IsGameRunning}"
SnowFlakes="{Binding SnowFlakes}"
Score="{Binding Score}"/>

<ProgressBar Maximum="{Binding SecondsGameDuration}"
IsRunning="{Binding IsGameRunning}"
SnowFlakes="{Binding SnowFlakes}"
Score="{Binding Score}" />

<!-- This ProgressBar is rendered above our SnowFlakesControl -->
<ProgressBar Maximum="{Binding MilliSecondsGameDuration}"
VerticalAlignment="Top"
Foreground="Goldenrod"
Margin="5"
Height="3"
Value="{Binding SecondsRemaining}" />
Value="{Binding MilliSecondsRemaining}" />

<!-- This Button is only visible when the game is not running. It has a Button to start the game -->

<Panel Background="#11FFFFFF"
IsVisible="{Binding !IsGameRunning}">
<Button Command="{Binding StartGameCommand}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content=""
FontSize="50"
CornerRadius="25" />
</Panel>
<Button IsVisible="{Binding !IsGameRunning}"
Command="{Binding StartGameCommand}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content=""
FontSize="50"
CornerRadius="25" />

</Panel>
</Window>
4 changes: 3 additions & 1 deletion src/Avalonia.Samples/MVVM/BasicMvvmSample/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ Remember that the `View` implements the User Interface. Our view will only consi
public SimpleViewModel SimpleViewModel { get; } = new SimpleViewModel();
```

NOTE:: Depending on the template and setting used to create your project you may find a class called `MainViewModel` instead of `MainWindowViewModel`. In this case please make sure the correct name.

=== Step 4: Setup the View

Now we can start with the UI layout. Our View will be written in [https://docs.avaloniaui.net/guides/basics/introduction-to-xaml[`XAML`,window=_blank]] which is an XML-based markup language that is used by many UI frameworks. The code modifications shown below will be applied to `MainWindow.axaml`.
Expand Down Expand Up @@ -260,7 +262,7 @@ image::_docs/result.png[Result]

== Approach 2 : Using ReactiveUI

We don't need to implement all the boilerplate code on our own over and over again. Instead, we can use any existing `MVVM` framework out there. [https://www.reactiveui.net[ReactiveUI,window=_blank]] is a popular MVVM framework designed for `.NET`. If you create a new Avalonia MVVM Project, you will have `ReactiveUI` installed by default. So let's see how we can use `ReactiveUI` to achieve the same results as **Approach 1** above.
We don't need to implement all the boilerplate code on our own over and over again. Instead, we can use any existing `MVVM` framework out there. [https://www.reactiveui.net[ReactiveUI,window=_blank]] is a popular MVVM framework designed for `.NET`. If you create a new Avalonia MVVM Project, you will have `ReactiveUI` (or `CommunityToolkit.Mvvm` based on your choice) installed by default. So let's see how we can use `ReactiveUI` to achieve the same results as **Approach 1** above.

=== Step 1: Create ReactiveViewModel

Expand Down

0 comments on commit 69f39fb

Please sign in to comment.