C
C#14mo ago
Mekasu0124

✅ Trying to figure out how to loop game Avalonia

<Button Classes="GameButton" Grid.Column="2" Content="Next Question" Command="{Binding NextQuestion}" IsVisible="{Binding QuestionFinished}" />
<Button Classes="GameButton" Grid.Column="2" Content="Next Question" Command="{Binding NextQuestion}" IsVisible="{Binding QuestionFinished}" />
GameEngineViewModel.cs => https://pastebin.com/36Sjrpnf I have a button that I want to trigger showing the next question to the user. On the previous screen, they select how many questions they want to answer which is passed to the GameEngineViewModel through the Game game => game.TotalQuestions parameter/variable. I'm trying to figure out how to loop the game for as many times as the game.TotalQuestions number is and have the next question shown based off of a button click. This button is only visible after the user has submitted their answer and the WinLoseText shows to the user. How would I go about doing this? Thanks
Pastebin
public class GameEngineViewModel : ViewModelBase{ public string?...
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
642 Replies
JakenVeina
JakenVeina14mo ago
private async Task RunGame(CancellationToken cancellationToken)
{
for(var i = 0; i < game.TotalQuestions; ++i)
{
...
}
}
private async Task RunGame(CancellationToken cancellationToken)
{
for(var i = 0; i < game.TotalQuestions; ++i)
{
...
}
}
Mekasu0124
Mekasu0124OP14mo ago
I tried doing that, but how would I get the loop to know when the user clicked the Next Question button so that they can progress through the questions at their own pace
public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {Score} / {game.TotalQuestions}";
NumberOne = GetNumber(game.Difficulty);
NumberTwo = GetNumber(game.Difficulty);
Operation = GetOperation(game.GameType);
GameSolution = GetSolution(game.GameType);

WinLoseText = IsCorrect
? "Correct Answer! Great Job!"
: $"Incorrect Answer! Expected: {GameSolution}";
}
public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {Score} / {game.TotalQuestions}";
NumberOne = GetNumber(game.Difficulty);
NumberTwo = GetNumber(game.Difficulty);
Operation = GetOperation(game.GameType);
GameSolution = GetSolution(game.GameType);

WinLoseText = IsCorrect
? "Correct Answer! Great Job!"
: $"Incorrect Answer! Expected: {GameSolution}";
}
this is the function that runs the game. I tried wrapping the inner code in a for loop that would iterate for the number of game.TotalQuestions but I couldn't get the button to work. I tried to do
public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {Score} / {game.TotalQuestions}";
NumberOne = GetNumber(game.Difficulty);
NumberTwo = GetNumber(game.Difficulty);
Operation = GetOperation(game.GameType);
GameSolution = GetSolution(game.GameType);

WinLoseText = IsCorrect
? "Correct Answer! Great Job!"
: $"Incorrect Answer! Expected: {GameSolution}";

if (NextQuestion())
{
continue;
}
}

public bool NextQuestion()
{
return true;
}
public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {Score} / {game.TotalQuestions}";
NumberOne = GetNumber(game.Difficulty);
NumberTwo = GetNumber(game.Difficulty);
Operation = GetOperation(game.GameType);
GameSolution = GetSolution(game.GameType);

WinLoseText = IsCorrect
? "Correct Answer! Great Job!"
: $"Incorrect Answer! Expected: {GameSolution}";

if (NextQuestion())
{
continue;
}
}

public bool NextQuestion()
{
return true;
}
so that NextQuestion was bound to the button and would return true when clicked so that the loop could continue after the button was clicked, but it wouldn't work right. I have zero idea. This is the only thing holding up progression of this application
JakenVeina
JakenVeina14mo ago
var nextQuestionRequested = new SemaphoreSlim();

void OnNextQuestionClick (object sender, MouseEventArgs e)
=> nextQuestionRequested.Release();

_nextQuestion.Click += OnNextQuestionClick;

for(var i = 0; i < questionCount; ++i)
{
await nextQuestionRequested.WaitAsync();
}

_nextQuestion.Click -= OnNextQuestionClick;
var nextQuestionRequested = new SemaphoreSlim();

void OnNextQuestionClick (object sender, MouseEventArgs e)
=> nextQuestionRequested.Release();

_nextQuestion.Click += OnNextQuestionClick;

for(var i = 0; i < questionCount; ++i)
{
await nextQuestionRequested.WaitAsync();
}

_nextQuestion.Click -= OnNextQuestionClick;
Mekasu0124
Mekasu0124OP14mo ago
😕 I have zero idea how to use that in my current code. Even looking at the docs, I'm lost
JakenVeina
JakenVeina14mo ago
alright, what does your View consist of?
Mekasu0124
Mekasu0124OP14mo ago
I'm actually going to hold off on this portion of the code. I realized that I've just been building by the fly of my pants and slapping things together instead of actually following a plan. I do apologize for getting back to you so late, however, I've spent the last 2 hours putting a build plan together of what each screen is supposed to do and how it's supposed to do it, and I'm going to start the project over. I appreciate your help and I'll be back when I come into another problem, or am ready to continue with this portion of the code. ok I'm back to this part now
public void StartGameEngine(Game game)
{
var vm = new GameEngineViewModel(game);

Observable.Merge(vm.Submit, vm.GoHome, vm.NextQuestion)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
// something here
}
else
{
SelectUser();
}
});

Content = vm;
}
public void StartGameEngine(Game game)
{
var vm = new GameEngineViewModel(game);

Observable.Merge(vm.Submit, vm.GoHome, vm.NextQuestion)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
// something here
}
else
{
SelectUser();
}
});

Content = vm;
}
in the main window view model, this is what controls the game screen. How would I loop this for as many times as SelectedTotalQuestions or would I loop it inside of the view model?
JakenVeina
JakenVeina14mo ago
see prior question
Mekasu0124
Mekasu0124OP14mo ago
Pastebin
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
JakenVeina
JakenVeina14mo ago
alright I see some labels some more labels a text box another label and three buttons
public class GameEngineViewModel
{
public string GameText { get; }
public string ScoreInfo { get; }
public string NumOne { get; }
public string Operation { get; }
public string NumTwo { get; }
public string Solution { get; set; }
public string WinLoseText { get; }
public bool IsVisible { get; }
public ICommand GoHome { get; }
public ICommand Submit { get; }
public ICOmmand NextQuestion { get; }
}
public class GameEngineViewModel
{
public string GameText { get; }
public string ScoreInfo { get; }
public string NumOne { get; }
public string Operation { get; }
public string NumTwo { get; }
public string Solution { get; set; }
public string WinLoseText { get; }
public bool IsVisible { get; }
public ICommand GoHome { get; }
public ICommand Submit { get; }
public ICOmmand NextQuestion { get; }
}
so, we're interested in the Submit command, yes?
Mekasu0124
Mekasu0124OP14mo ago
first I'm interested in getting the game to loop for the number of questions the user selected to answer. I have a button "Next Question" that is bound to public ReactiveCommand<Unit, Unit> NextQuestion { get; } in the view model. This button allows the next question to be shown upon user click so if the user elects to answer 5 questions, then the first question shows, the user answers, and then the Next Question button becomes visible to go to next question on user click so this is the MainWindowViewModel control of the screen https://pastebin.com/JfEdFW2g this is the view axaml file https://pastebin.com/wtR5Gg1v this is the view model for that screen so it's supposed to be like
for (int i = 0; i < game.TotalQuestions; i++)
{
// show the game
// get user input
// display right or wrong
// show next question button
// upon next button click, continue loop for next question
}
for (int i = 0; i < game.TotalQuestions; i++)
{
// show the game
// get user input
// display right or wrong
// show next question button
// upon next button click, continue loop for next question
}
JakenVeina
JakenVeina14mo ago
okay, so, we're interested in the NextQuestion command
Mekasu0124
Mekasu0124OP14mo ago
like in a console based application, it woul dbe
int maxQuestions = 10;
for (int i = 0; i < maxQuestions; i++)
{
// show question
// get answer and check it
// display results
Console.WriteLine("Press any key to go to next question");
Console.ReadLine();
}
int maxQuestions = 10;
for (int i = 0; i < maxQuestions; i++)
{
// show question
// get answer and check it
// display results
Console.WriteLine("Press any key to go to next question");
Console.ReadLine();
}
JakenVeina
JakenVeina14mo ago
right this isn't a Console application
Mekasu0124
Mekasu0124OP14mo ago
right. I was using that to relay what I was meaning in an easier way
JakenVeina
JakenVeina14mo ago
so stop trying to jump around the problem and work the problem you have a ViewModel to implement the piece of it that you're having trouble with is NextQuestion focus on that what is NextQuestion supposed to do?
Mekasu0124
Mekasu0124OP14mo ago
go to the next question upon user click
JakenVeina
JakenVeina14mo ago
private void ExecuteNextQuestion()
{

}

private bool CanExecuteNextQuestion()
{

}
private void ExecuteNextQuestion()
{

}

private bool CanExecuteNextQuestion()
{

}
Mekasu0124
Mekasu0124OP14mo ago
do I put the loop in the main window view model or in the game engine view model?
JakenVeina
JakenVeina14mo ago
is there a scenario where the user can't go to the next question?
Mekasu0124
Mekasu0124OP14mo ago
no
JakenVeina
JakenVeina14mo ago
k
private void ExecuteNextQuestion()
{

}

private bool CanExecuteNextQuestion()
=> true;
private void ExecuteNextQuestion()
{

}

private bool CanExecuteNextQuestion()
=> true;
go to the next question
how do we do this? what assumptions does this imply?
Mekasu0124
Mekasu0124OP14mo ago
that we have two functions and one always returns true?
JakenVeina
JakenVeina14mo ago
I meant the text that I quoted
Mekasu0124
Mekasu0124OP14mo ago
but I still don't know if this is going to be controlled by the main window view model or the game engine view model
JakenVeina
JakenVeina14mo ago
stop asking questions that you don't even understand in the first place we have a method to implement focus on the problem
go to the next question
how do we do this? what assumptions does this imply?
Mekasu0124
Mekasu0124OP14mo ago
excuse me? I'm just simply asking where we're implementing the method. Jesus christ it means to start the loop and pause it until the user clicks the next question button to continue the loop
JakenVeina
JakenVeina14mo ago
what loop? where is the loop in the context of the method we're implementing? spoiler: there isn't one if my goal is to "move to the next question", what does that imply? how about "that there's a current question" how about "that there is a set of questions"
Mekasu0124
Mekasu0124OP14mo ago
no there isn't one yet because I don't know where to put it.
JakenVeina
JakenVeina14mo ago
why do you assume there will be one?
Mekasu0124
Mekasu0124OP14mo ago
so that there can be as many questions as the user wants to answer
JakenVeina
JakenVeina14mo ago
why does that imply that you need a for loop?
Mekasu0124
Mekasu0124OP14mo ago
because that's the only thing I know to do. Unless you're talking about creating a backend class that gets passed the total number of questions that the user wants to answer, generating all the questions, and then displaying the screen with the first question in the list of questions and having the button refresh the screen and go to the next index in the list of questions
JakenVeina
JakenVeina14mo ago
private void ExecuteNextQuestion()
{
++_currentQuestionIndex;
if (_currentQuestionIndex < _questions.Count)
{
NumOne = _questions[_currentQuestionIndex].NumOne;
Operation = _questions[_currentQuestionIndex].Operation;
NumTwo = _questions[_currentQuestionIndex].NumTwo;
Solution = _questions[_currentQuestionIndex].Solution;
WinLoseText = null;
IsVisible = false;
}
else
{
// No more questions, go back home or whatever
}
}
private void ExecuteNextQuestion()
{
++_currentQuestionIndex;
if (_currentQuestionIndex < _questions.Count)
{
NumOne = _questions[_currentQuestionIndex].NumOne;
Operation = _questions[_currentQuestionIndex].Operation;
NumTwo = _questions[_currentQuestionIndex].NumTwo;
Solution = _questions[_currentQuestionIndex].Solution;
WinLoseText = null;
IsVisible = false;
}
else
{
// No more questions, go back home or whatever
}
}
because that's the only thing I know to do
which is why I'm trying to help you think through the problem, rather than try and work backwards from a solution that isn't actually applicable
Mekasu0124
Mekasu0124OP14mo ago
so then I need to get the number of questions the user wants to answer, go ahead and generate all of them into a list, and then display
JakenVeina
JakenVeina14mo ago
that would be the most straightforward thing to do at the very least, you need SOME way to determine how many questions you want there to be
Mekasu0124
Mekasu0124OP14mo ago
well it would be a list of lists since I display the numOne, operator, and numTwo in separate items on the screen
JakenVeina
JakenVeina14mo ago
maybe you generate them all ahead of time, maybe you just keep a count of how many you want, and generate them on the fly why does that imply a list of lists?
Mekasu0124
Mekasu0124OP14mo ago
at the very least, you need SOME way to determine how many questions you want there to be
that's determined on the screen before this one. The home screen view model is where the user selects the difficulty and total questions to answer, and the game they want to play and that's pushed back to the main window view model, stored, and then passed to the game screen
JakenVeina
JakenVeina14mo ago
great that info gets passed to the GameEngineViewModel
Mekasu0124
Mekasu0124OP14mo ago
it would be like
List<List<int, string, int>> questions = new()
{
new question { numOne: NumberOne, oper: Operation, numTwo: NumberTwo },
new question { numOne: NumberOne, oper: Operation, numTwo: NumberTwo }
}
List<List<int, string, int>> questions = new()
{
new question { numOne: NumberOne, oper: Operation, numTwo: NumberTwo },
new question { numOne: NumberOne, oper: Operation, numTwo: NumberTwo }
}
and I say that because I display NumOne in Label A, Operation in Label B, and NumTwo in Label C, but there could be a better way to do it. This is just what I thought of since I display the 3 items separately instead of in the same label correct
public class MainWindowViewModel : ViewModelBase
{
public string? SelectedUser;
public string? SelectedDifficulty;
public string? SelectedGameType;
public int? SelectedTotalQuestions;

public void GetGameInformation()
{
var vm = new HomeScreenViewModel();

Observable.Merge(vm.Play, vm.PreviousGames, vm.BackButton)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
if (model.GameType == "Previous Games")
{
// ShowPreviousGames();
}
else if (model.GameType == "Back Button")
{
SelectUser();
}
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
});

Content = vm;
}
}
public class MainWindowViewModel : ViewModelBase
{
public string? SelectedUser;
public string? SelectedDifficulty;
public string? SelectedGameType;
public int? SelectedTotalQuestions;

public void GetGameInformation()
{
var vm = new HomeScreenViewModel();

Observable.Merge(vm.Play, vm.PreviousGames, vm.BackButton)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
if (model.GameType == "Previous Games")
{
// ShowPreviousGames();
}
else if (model.GameType == "Back Button")
{
SelectUser();
}
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
});

Content = vm;
}
}
JakenVeina
JakenVeina14mo ago
...nooooooooooooo...
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}

...

var questions = new List<Question>()
{
new Question() { NumOne = 1, NumTwo = 2, Operation = Operation.Addition },
new Question() { NumOne = 3, NumTwo = 4, Operation = Operation.Subtraction },
new Question() { NumOne = 5, NumTwo = 6, Operation = Operation.Multiplication }
}
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}

...

var questions = new List<Question>()
{
new Question() { NumOne = 1, NumTwo = 2, Operation = Operation.Addition },
new Question() { NumOne = 3, NumTwo = 4, Operation = Operation.Subtraction },
new Question() { NumOne = 5, NumTwo = 6, Operation = Operation.Multiplication }
}
oooooooo, now you didn't tell me we're in RX-land
Mekasu0124
Mekasu0124OP14mo ago
rx-land?
JakenVeina
JakenVeina14mo ago
Reactive Extensions
Mekasu0124
Mekasu0124OP14mo ago
yea it's reactive UI. I didn't know I needed to specify that? I thought that's what everyone used
JakenVeina
JakenVeina14mo ago
if only
Mekasu0124
Mekasu0124OP14mo ago
ok so here's what I've done so far and I want your thoughts
public class GameEngineViewModel : ViewModelBase
{
public string? _gameText;
public string? _operation;
public string? _finishText;
public string? _scoreInfo;

public int? _numOne;
public int? _numTwo;
public int? _userScore = 0;

public double? _userSolution;
public double? _gameSolution;

public bool? _isCorrect;
public bool? _isVisible = false;

public static List<Question> _allQuestions;

public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {UserScore} / {game.TotalQuestions}";
AllQuestions = BuildQuestions.BuildList(game.Difficulty, game.GameType);
}

public List<Question> AllQuestions
{
get => _allQuestions;
set => this.RaiseAndSetIfChanged(ref _allQuestions, value);
}
}
public class GameEngineViewModel : ViewModelBase
{
public string? _gameText;
public string? _operation;
public string? _finishText;
public string? _scoreInfo;

public int? _numOne;
public int? _numTwo;
public int? _userScore = 0;

public double? _userSolution;
public double? _gameSolution;

public bool? _isCorrect;
public bool? _isVisible = false;

public static List<Question> _allQuestions;

public GameEngineViewModel(Game game)
{
GameText = $"Game: {game.GameType} - Difficulty: {game.Difficulty}";
ScoreInfo = $"Score: {UserScore} / {game.TotalQuestions}";
AllQuestions = BuildQuestions.BuildList(game.Difficulty, game.GameType);
}

public List<Question> AllQuestions
{
get => _allQuestions;
set => this.RaiseAndSetIfChanged(ref _allQuestions, value);
}
}
I've updated the game engine to this so far
JakenVeina
JakenVeina14mo ago
questions
.ToObservable()
.Throttle(_nextQuestionExecuted.Prepend(Unit.Default))
.Subscribe(
onNext: question =>
{
NumOne = question.NumOne;
Operation = question.Operation;
NumTwo = question.NumTwo;
Solution = null;
WinLoseText = null;
IsVisible = false;
},
onCompleted: () => _gameComplete.OnNext(Unit.Default));
questions
.ToObservable()
.Throttle(_nextQuestionExecuted.Prepend(Unit.Default))
.Subscribe(
onNext: question =>
{
NumOne = question.NumOne;
Operation = question.Operation;
NumTwo = question.NumTwo;
Solution = null;
WinLoseText = null;
IsVisible = false;
},
onCompleted: () => _gameComplete.OnNext(Unit.Default));
Mekasu0124
Mekasu0124OP14mo ago
using System;
using System.Collections.Generic;

namespace MeksMathGame.Services;

public class BuildQuestions
{
public List<string?>? operations = new() { "➕", "➖", "✖️", "➗" };

public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() { NumOne = GetNumber(diff, gameType), NumTwo = GetNumber(diff, gameType), Operation = }
}
}

public static int GetNumber(string diff, string gameType)
{
Random rng = new();

if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300)
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, operations.Count);

return gameType switch
{
"Addition" => operations[0],
"Subtraction" => operations[1],
"Multiplication" => operations[2],
"Division" => operations[3],
"Random" => operations[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
using System;
using System.Collections.Generic;

namespace MeksMathGame.Services;

public class BuildQuestions
{
public List<string?>? operations = new() { "➕", "➖", "✖️", "➗" };

public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() { NumOne = GetNumber(diff, gameType), NumTwo = GetNumber(diff, gameType), Operation = }
}
}

public static int GetNumber(string diff, string gameType)
{
Random rng = new();

if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300)
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, operations.Count);

return gameType switch
{
"Addition" => operations[0],
"Subtraction" => operations[1],
"Multiplication" => operations[2],
"Division" => operations[3],
"Random" => operations[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
and I've started a class in my services folder to build the list of questions what do I do with this?
JakenVeina
JakenVeina14mo ago
public List<Question> AllQuestions is not something your View layer needs that would be an RX-style way to implement your "loop" build a stream of the questions each time the stream emits a quesiton, push it to the View throttle the stream to only emit one question each time the _nextQuestionExecuted event fires, I.E. when the button is clicked when the stream completes, I.E. there are no more questions, fire an event to the owner VM that the game is done
Mekasu0124
Mekasu0124OP14mo ago
ok before I implement that, do you mind to help me get the BuildQuestions class finished? I'm not sure how to get the operation into the list
JakenVeina
JakenVeina14mo ago
you don't have to implement that, especially if you don't understand it just demonstrating possibilities BuildQuestions is a poor name for a class maybe QuestionBuilder or QuestionsService
Mekasu0124
Mekasu0124OP14mo ago
tbh I like what I've done starting this class. If it's not the proper way to do it, then I can scratch it, but I like it lol
JakenVeina
JakenVeina14mo ago
sure regardless, your BuildList() method is what you want what are diff and gameType?
Mekasu0124
Mekasu0124OP14mo ago
public class QuestionBuilder
{
public List<string?>? operations = new() { "➕", "➖", "✖️", "➗" };
}
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}
public class QuestionBuilder
{
public List<string?>? operations = new() { "➕", "➖", "✖️", "➗" };
}
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}
how would I get the Operation enum to know which item belongs to it's item in the list? diff is the selected difficulty the user chose to play on and gameType is the game they chose to play
JakenVeina
JakenVeina14mo ago
those should probably also be enums
Mekasu0124
Mekasu0124OP14mo ago
this game is for my niece who's in 3rd or 4th grade, so it's set to where if it's Multiplication or Division then it generates the NumOne and NumTwo in a different range than Addition and Subtraction
JakenVeina
JakenVeina14mo ago
certainly
private Dictionary<Operation, string> _operationSymbols
= new()
{
[Operation.Addition] = "➕",
[Operation.Subtraction] = "➖",
[Operation.Multiplication] = "✖️",
[Operation.Division] = "➗"
};
private Dictionary<Operation, string> _operationSymbols
= new()
{
[Operation.Addition] = "➕",
[Operation.Subtraction] = "➖",
[Operation.Multiplication] = "✖️",
[Operation.Division] = "➗"
};
and, strictly-speaking, this belongs in your View layer now, to be clear I'm definitely going to illustrate an "idealized" way of doing all this stuff but you are free to do what works
Mekasu0124
Mekasu0124OP14mo ago
the dictionary belongs in the GameEngine?
JakenVeina
JakenVeina14mo ago
if you want to just put these sybols straight into Question.Operation as strings that's fine this isn't an enterprise application is GameEngine a View? the View for GameEngineViewModel?
Mekasu0124
Mekasu0124OP14mo ago
that's called GameEngineView.axaml
JakenVeina
JakenVeina14mo ago
either there, or within an IValueConverter depending on how you're doing bindings
Mekasu0124
Mekasu0124OP14mo ago
oh so the dictionary goes in the GameEngineView.axaml.cs file
JakenVeina
JakenVeina14mo ago
and you seem to be doing XAML bindings, so, an IValueConverter if that makes sense to you and lets you use it in your bindings
Mekasu0124
Mekasu0124OP14mo ago
I've never used the code behind file for anything, so if that's where it goes then I'll put it there
JakenVeina
JakenVeina14mo ago
that isn't "where it goes" put it where you need it to be or where it makes sense to you
Mekasu0124
Mekasu0124OP14mo ago
I'll leave it in the QuestionBuilder class then
JakenVeina
JakenVeina14mo ago
well I mean like, do that but my point is that what that dictionary is is a mapping of how to DISPLAY those enum values it's specifically a VIEW layer concern the proper place for it is somewhere in the View layer
Mekasu0124
Mekasu0124OP14mo ago
then i'll put it in the game engines view model
JakenVeina
JakenVeina14mo ago
which could be a lot of places, but not on the backend QuestionBuilder class, and not on a VM
Mekasu0124
Mekasu0124OP14mo ago
if it's neither of those then the only other place is the axaml.cs file
JakenVeina
JakenVeina14mo ago
not necessarily example
public static class ValueConverters
{
public static IValueConverter OperationToString
= new OperationValueConverter();

private class OperationValueConverter
: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value switch
{
Operation.Addition => "➕",
Operation.Subtraction => "➖",
Operation.Multiplication => "✖️",
Operation.Division => "➗",
_ => null
};

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
}
public static class ValueConverters
{
public static IValueConverter OperationToString
= new OperationValueConverter();

private class OperationValueConverter
: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value switch
{
Operation.Addition => "➕",
Operation.Subtraction => "➖",
Operation.Multiplication => "✖️",
Operation.Division => "➗",
_ => null
};

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
}
{Binding Operation, Converter={x:Static Utilities:ValueConverters.OperationToString}}
{Binding Operation, Converter={x:Static Utilities:ValueConverters.OperationToString}}
Mekasu0124
Mekasu0124OP14mo ago
and doing that negates the dictionary altogether right?
JakenVeina
JakenVeina14mo ago
in this case, yeah sort of you could still use a dictionary for miniscule performance boost
Mekasu0124
Mekasu0124OP14mo ago
I put this into a ValueConverters.cs file and implemented the converter into the view where the operation is shown
JakenVeina
JakenVeina14mo ago
public static class ValueConverters
{
public static IValueConverter OperationToString
= new OperationValueConverter();

private class OperationValueConverter
: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is Operation operation && _operationSymbols.TryGetValue(operation, out var symbol)
? symbol
: null;

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();

private Dictionary<Operation, string> _operationSymbols
= new()
{
[Operation.Addition] = "➕",
[Operation.Subtraction] = "➖",
[Operation.Multiplication] = "✖️",
[Operation.Division] = "➗"
};
}
}
public static class ValueConverters
{
public static IValueConverter OperationToString
= new OperationValueConverter();

private class OperationValueConverter
: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
=> value is Operation operation && _operationSymbols.TryGetValue(operation, out var symbol)
? symbol
: null;

public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();

private Dictionary<Operation, string> _operationSymbols
= new()
{
[Operation.Addition] = "➕",
[Operation.Subtraction] = "➖",
[Operation.Multiplication] = "✖️",
[Operation.Division] = "➗"
};
}
}
Mekasu0124
Mekasu0124OP14mo ago
ok I made those changes
public class QuestionBuilder
{
public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() { NumOne = GetNumber(diff, gameType), NumTwo = GetNumber(diff, gameType), Operation = }
};

return questions;
}

public static int GetNumber(string diff, string gameType)
{
Random rng = new();

if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300)
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, _operationSymbols.Values.ToList().Count);

return gameType switch
{
"Addition" => _operationSymbols.Values.ToList()[0],
"Subtraction" => _operationSymbols.Values.ToList()[1],
"Multiplication" => _operationSymbols.Values.ToList()[2],
"Division" => _operationSymbols.Values.ToList()[3],
"Random" => _operationSymbols.Values.ToList()[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
public class QuestionBuilder
{
public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() { NumOne = GetNumber(diff, gameType), NumTwo = GetNumber(diff, gameType), Operation = }
};

return questions;
}

public static int GetNumber(string diff, string gameType)
{
Random rng = new();

if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300)
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, _operationSymbols.Values.ToList().Count);

return gameType switch
{
"Addition" => _operationSymbols.Values.ToList()[0],
"Subtraction" => _operationSymbols.Values.ToList()[1],
"Multiplication" => _operationSymbols.Values.ToList()[2],
"Division" => _operationSymbols.Values.ToList()[3],
"Random" => _operationSymbols.Values.ToList()[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
and this is where I'm at on the question builder class.
JakenVeina
JakenVeina14mo ago
sweet
Mekasu0124
Mekasu0124OP14mo ago
public static List<Question> BuildList(string diff, string gameType)
{
CultureInfo culture = CultureInfo.InvariantCulture;

var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = ValueConverters.OperationToString.Convert(gameType, string, ,culture)
}
};

return questions;
}
public static List<Question> BuildList(string diff, string gameType)
{
CultureInfo culture = CultureInfo.InvariantCulture;

var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = ValueConverters.OperationToString.Convert(gameType, string, ,culture)
}
};

return questions;
}
ok so when trying to get the operation based off the gameType, it doesn't like string and I don't know what to put for parameter
JakenVeina
JakenVeina14mo ago
okay, so don't do that
Mekasu0124
Mekasu0124OP14mo ago
oh. I was happy I got that far 😂
JakenVeina
JakenVeina14mo ago
the point of making a ValueConverter was to keep the conversion logic in the View layer this isn't the View Layer if you're just going to do it here, use the dictionary or a switch or just forget the enum
Mekasu0124
Mekasu0124OP14mo ago
ok I have to get the operation back to the game engine view model somehow what would you suggest with having the value converter?
JakenVeina
JakenVeina14mo ago
where I suggested earlier what does that have to do with whether or not you do a conversion for display here?
new Question()
{
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(diff, gameType)
};
new Question()
{
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(diff, gameType)
};
done or is it only one operation per game-type? oh, I see now
Mekasu0124
Mekasu0124OP14mo ago
public static string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, _operationSymbols.Values.ToList().Count);

return gameType switch
{
"Addition" => _operationSymbols.Values.ToList()[0],
"Subtraction" => _operationSymbols.Values.ToList()[1],
"Multiplication" => _operationSymbols.Values.ToList()[2],
"Division" => _operationSymbols.Values.ToList()[3],
"Random" => _operationSymbols.Values.ToList()[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
public static string? GetOperation(string gameType)
{
Random rng = new();
int rndIndex = rng.Next(0, _operationSymbols.Values.ToList().Count);

return gameType switch
{
"Addition" => _operationSymbols.Values.ToList()[0],
"Subtraction" => _operationSymbols.Values.ToList()[1],
"Multiplication" => _operationSymbols.Values.ToList()[2],
"Division" => _operationSymbols.Values.ToList()[3],
"Random" => _operationSymbols.Values.ToList()[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
JakenVeina
JakenVeina14mo ago
one game type per operation, plus a random
Mekasu0124
Mekasu0124OP14mo ago
right
JakenVeina
JakenVeina14mo ago
absolutely do not call .ToList()
Mekasu0124
Mekasu0124OP14mo ago
so if on the home screen they click the Addition button, then it'll be all addition questions
JakenVeina
JakenVeina14mo ago
sure personally, I think you should split this stuff apart a bit well, nevermind next point absolutely do not do this
Mekasu0124
Mekasu0124OP14mo ago
do you mean build 5 different screens for the 5 game plays?
JakenVeina
JakenVeina14mo ago
Random rng = new();
Random rng = new();
I was thinking more like 5 different methods on QuestionBuilder() one for each difficulty
Mekasu0124
Mekasu0124OP14mo ago
I can do that. I don't mind whatever is more efficient
JakenVeina
JakenVeina14mo ago
even if they're just private and BuildList() just chooses which one to use
Mekasu0124
Mekasu0124OP14mo ago
and proper practice
JakenVeina
JakenVeina14mo ago
but I changed my mind I wouldn't say either way is "proper" for this one both are valid
Mekasu0124
Mekasu0124OP14mo ago
okie dokie. so with getting the operation in the GetOperation function, how do I need to call the list from the value converter and why shouldn't I do random like that?
JakenVeina
JakenVeina14mo ago
proper usage of the Random class is to create one instance, and reuse it as much as possible
Mekasu0124
Mekasu0124OP14mo ago
so put it at the top of the class => constructor and just call it from there
JakenVeina
JakenVeina14mo ago
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
or
public class QuestionBuilder
{
public QuestionBuilder(Random rng)
=> _rng = rng;

private readonly Random _rng;
}
public class QuestionBuilder
{
public QuestionBuilder(Random rng)
=> _rng = rng;

private readonly Random _rng;
}
this second one would allow you to create the Random instance in your Main method, or whatever and share it across many different classes in the app that need to pull random numbers each time you create a new instance of Random, it creates a new sequence with a new seed cryptographic pseudo-randomness is only guaranteed across numbers generated from the same sequence
Mekasu0124
Mekasu0124OP14mo ago
random is only used within this file. To generate the numbers to display and the random operation if the user selects to play a random game. As far as the rest of what you post, I'm beyond confused and don't know what half of that means so I have zero clue where to put this random thing.
JakenVeina
JakenVeina14mo ago
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
one instance reuse it
Mekasu0124
Mekasu0124OP14mo ago
I don't have a question builder function. so I'm assuming I need to make one
JakenVeina
JakenVeina14mo ago
you already have, I just didn't copy it here then whoever is using QuestionBuilder, same thing. Create just one and reuse it
Mekasu0124
Mekasu0124OP14mo ago
yea I'm lost. I have zero idea.
JakenVeina
JakenVeina14mo ago
then re-orient what is the problem right now? that is ALWAYS the first question to ask yourself what is the problem you are trying to solve? great
Mekasu0124
Mekasu0124OP14mo ago
no that's not wher eI"m at I'm fighting with this stupid keyboard one moent please
JakenVeina
JakenVeina14mo ago
well, it's how you got there where you're ACTUALLY at is writing the OperationBuilder.BuildList() method and you're there because that's what GameEngineViewModel currently needs
Mekasu0124
Mekasu0124OP14mo ago
GameEngineViewModel => https://pastebin.com/kkBw832q QuestionBuilder => https://pastebin.com/ikQZRXRS ValueConverter => https://pastebin.com/23iftTjC This is where I'm at with what has been built so far. 1. I have zero idea where or what I'm supposed to do with random. Completely lost there. 2. I have zero idea how to get the GetOperation function to work 3. I just feel beyond dumb
JakenVeina
JakenVeina14mo ago
so, we're on GetOperation() what's the problem with GetOperation?
Mekasu0124
Mekasu0124OP14mo ago
tbh I'm stuck on where and how to put random.
JakenVeina
JakenVeina14mo ago
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
public class QuestionBuilder
{
public QuestionBuilder()
=> _rng = new();

private readonly Random _rng;
}
Mekasu0124
Mekasu0124OP14mo ago
with get operation, I can't fgure out how to call the dang list from the value converter and whta to pass it
JakenVeina
JakenVeina14mo ago
I can't figure out how to call the dang list from the value converter
as I said, don't ValueConverter doesn't belong here QuestionBuilder is not part of the View layer
Mekasu0124
Mekasu0124OP14mo ago
public class QuestionBuilder
{
public QuestionBuilder() => _rng = new();
private readonly Random _rng;

public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(gameType)
}
};

return questions;
}

public static int GetNumber(string diff, string gameType)
{
if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public static string? GetOperation(string gameType)
{
int rndIndex = rng.Next(0, _operationSymbols.Values);

return gameType switch
{
"Addition" => _operationSymbols.Values[0],
"Subtraction" => _operationSymbols.Values[1],
"Multiplication" => _operationSymbols.Values[2],
"Division" => _operationSymbols.Values[3],
"Random" => _operationSymbols.Values[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
public class QuestionBuilder
{
public QuestionBuilder() => _rng = new();
private readonly Random _rng;

public static List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(gameType)
}
};

return questions;
}

public static int GetNumber(string diff, string gameType)
{
if (gameType == "Addition" || gameType == "Subtraction")
{
return diff switch
{
"Easy" => rng.Next(0, 300),
"Medium" => rng.Next(0, 600),
"Hard" => rng.Next(0, 900),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
else
{
return diff switch
{
"Easy" => rng.Next(0, 100),
"Medium" => rng.Next(0, 200),
"Hard" => rng.Next(0, 300),
_ => throw new Exception("Unable To Determine Difficulty")
};
}
}

public static string? GetOperation(string gameType)
{
int rndIndex = rng.Next(0, _operationSymbols.Values);

return gameType switch
{
"Addition" => _operationSymbols.Values[0],
"Subtraction" => _operationSymbols.Values[1],
"Multiplication" => _operationSymbols.Values[2],
"Division" => _operationSymbols.Values[3],
"Random" => _operationSymbols.Values[rndIndex],
_ => throw new Exception("Unable To Determine Operation")
};
}
}

public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
how do I get access to that rng variable because QuestionBuilder doesn't have it as a property
JakenVeina
JakenVeina14mo ago
uhh yes it does I mean it's not a property, no but... it's right there where you put it
private readonly Random _rng;
private readonly Random _rng;
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
so, there's an error what is it?
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
what does that mean to you?
Mekasu0124
Mekasu0124OP14mo ago
had to make the priavte variable static
JakenVeina
JakenVeina14mo ago
yes, but no make the method not static
Mekasu0124
Mekasu0124OP14mo ago
ok. did that I Have access to it just fine now
JakenVeina
JakenVeina14mo ago
or make the entire class static but really, until you can explain in detail what static means, don't use it as a general recommendation
Mekasu0124
Mekasu0124OP14mo ago
that's fine. So now onto the GetOperation function
JakenVeina
JakenVeina14mo ago
with the one exception of your Main() method which HAS to be static
Mekasu0124
Mekasu0124OP14mo ago
correct otherwise it doesn't have a property main entry point
JakenVeina
JakenVeina14mo ago
😉 so, what's the issue with GetOperation() currently?
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
why is GetOperation() returning string??
Mekasu0124
Mekasu0124OP14mo ago
because that's what it was previously set to. I haven't changed it yet, but I don't know what I'm returning out of the function yet since I can't figure out what to return
JakenVeina
JakenVeina14mo ago
excellent answer leads you right to the next question where are you using GetOperation()
Mekasu0124
Mekasu0124OP14mo ago
public List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(gameType)
}
};

return questions;
}
public List<Question> BuildList(string diff, string gameType)
{
var questions = new List<Question>()
{
new Question() {
NumOne = GetNumber(diff, gameType),
NumTwo = GetNumber(diff, gameType),
Operation = GetOperation(gameType)
}
};

return questions;
}
JakenVeina
JakenVeina14mo ago
so, the return value is being assigned to Question.Operation what is Question.Operation?
Mekasu0124
Mekasu0124OP14mo ago
Operation?
JakenVeina
JakenVeina14mo ago
that's what GetOperation() needs to return
Mekasu0124
Mekasu0124OP14mo ago
I thought it was supposed to be a string
JakenVeina
JakenVeina14mo ago
why?
Mekasu0124
Mekasu0124OP14mo ago
because it's a string when displayed is it an enum?
JakenVeina
JakenVeina14mo ago
that's what Operation is, yes and that's what my recommendation was for the Question class because the fact that it's going to be displayed as a string is irrelevant in this layer it's only relevant to the View layer that was my point earlier the View's job is to display things
Mekasu0124
Mekasu0124OP14mo ago
public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
public class Question
{
public required decimal NumOne { get; init; }
public required decimal NumTwo { get; init; }
public required Operation Operation { get; init; }
}
JakenVeina
JakenVeina14mo ago
the ViewModel/Business layer's job is to understand logic
Mekasu0124
Mekasu0124OP14mo ago
right. In the question class, it's a required Operation
JakenVeina
JakenVeina14mo ago
if the View wants to take that Operation enum value and display it as a string then it's free to do so, and the ViewModel/Business layer doesn't need to know or care
Mekasu0124
Mekasu0124OP14mo ago
but as a side question, why are the numbers set to decimals instead of integers when I have them as integers in teh view model?
JakenVeina
JakenVeina14mo ago
because I didn't have the full picture of your ViewModel when I wrote that
Mekasu0124
Mekasu0124OP14mo ago
makes sense so I can change them to int's and be fine
JakenVeina
JakenVeina14mo ago
if you only want to generate ints feel free to make those ints, yes
Mekasu0124
Mekasu0124OP14mo ago
ok cool so what am I not understanding with the get operation function because I'm still lost there
JakenVeina
JakenVeina14mo ago
while we're at it, I'm going to recommend the exact same thing for difficulty and gametype
public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameType
{
Addition,
Subtraction,
Multiplication,
Division,
Random
}
public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameType
{
Addition,
Subtraction,
Multiplication,
Division,
Random
}
Mekasu0124
Mekasu0124OP14mo ago
so on this, the difficulty is just a simple string list in the home screen view model that the user selects from a combobox Difficulties = new() { "Select One", "Easy", "Medium", "Hard" };. As far as the game type, those are command parameters from the buttons on the home screen view
<Border Classes="HomeBorder" Grid.Column="1">
<Grid RowDefinitions="*,*,*,*,*,*,*">
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="Addition"/>
<Button Classes="HomeButton" Grid.Row="1" Content="Subtraction" Command="{Binding Play}" CommandParameter="Subtraction"/>
<Button Classes="HomeButton" Grid.Row="2" Content="Multiplication" Command="{Binding Play}" CommandParameter="Multiplication"/>
<Button Classes="HomeButton" Grid.Row="3" Content="Division" Command="{Binding Play}" CommandParameter="Division"/>
<Button Classes="HomeButton" Grid.Row="4" Content="Random" Command="{Binding Play}" CommandParameter="Random"/>
<Button Classes="HomeButton" Grid.Row="5" Content="Previous Games" Command="{Binding PreviousGames}" CommandParameter="Previous Games" />
<Button Classes="HomeButton" Grid.Row="6" Content="Back" Command="{Binding BackButton}" CommandParameter="Back Button" />
</Grid>
</Border>
<Border Classes="HomeBorder" Grid.Column="1">
<Grid RowDefinitions="*,*,*,*,*,*,*">
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="Addition"/>
<Button Classes="HomeButton" Grid.Row="1" Content="Subtraction" Command="{Binding Play}" CommandParameter="Subtraction"/>
<Button Classes="HomeButton" Grid.Row="2" Content="Multiplication" Command="{Binding Play}" CommandParameter="Multiplication"/>
<Button Classes="HomeButton" Grid.Row="3" Content="Division" Command="{Binding Play}" CommandParameter="Division"/>
<Button Classes="HomeButton" Grid.Row="4" Content="Random" Command="{Binding Play}" CommandParameter="Random"/>
<Button Classes="HomeButton" Grid.Row="5" Content="Previous Games" Command="{Binding PreviousGames}" CommandParameter="Previous Games" />
<Button Classes="HomeButton" Grid.Row="6" Content="Back" Command="{Binding BackButton}" CommandParameter="Back Button" />
</Grid>
</Border>
which is how the main window view model knows what screen to show
public void GetGameInformation()
{
var vm = new HomeScreenViewModel();
Observable.Merge(vm.Play, vm.PreviousGames, vm.BackButton)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
if (model.GameType == "Previous Games")
{
// ShowPreviousGames();
}
else if (model.GameType == "Back Button")
{
SelectUser();
}
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
});

Content = vm;
}
public void GetGameInformation()
{
var vm = new HomeScreenViewModel();
Observable.Merge(vm.Play, vm.PreviousGames, vm.BackButton)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
if (model.GameType == "Previous Games")
{
// ShowPreviousGames();
}
else if (model.GameType == "Back Button")
{
SelectUser();
}
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
});

Content = vm;
}
JakenVeina
JakenVeina14mo ago
okay ?
Mekasu0124
Mekasu0124OP14mo ago
ok
JakenVeina
JakenVeina14mo ago
is that relevant to having difficulty and gameType be enums?
Mekasu0124
Mekasu0124OP14mo ago
I thought it was which is why I explained what they're purpose was, but I'm guessing I was off on that. sorry
JakenVeina
JakenVeina14mo ago
no need to apologize, I'm not criticizing you I'm trying to make you challenge your assumptions in the same way that Operation being an enum is independent of how the View wants to display that value as a string symbol
Mekasu0124
Mekasu0124OP14mo ago
so I'm making them enums for the logic, not for what I already have them for
JakenVeina
JakenVeina14mo ago
how the View chooses to present commands to the user is irrelevant of how they're modeled in the business layer you could say that enums are standard practice for something like this for two main reasons A) it's compiler-safe if I am referencing the game type Addition in 7 different places, as a string but in one place I accidentally type Adition that's a bug that I won't find until that code runs and even then, I might not figure it out right away
Mekasu0124
Mekasu0124OP14mo ago
I got you
JakenVeina
JakenVeina14mo ago
as an enum, if I type Adition the compiler immediately tells me that's an error, because Adition is undefined
Mekasu0124
Mekasu0124OP14mo ago
makes sense
JakenVeina
JakenVeina14mo ago
B) it clarifies your intent if I see
public class Question
{
public required string Operation { get; init; }
}
public class Question
{
public required string Operation { get; init; }
}
I really have no idea what possible operations that could be the only way I know is by having a greater contextual understanding of the whole application with an enum, I can see EXACTLY what all possible values are supposed to be and they're all defined in one central place and that can actually help INFORM me about the greater context of the whole app as far as mechanics, in the View layer....
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="local:GameType.Addition"/>
<Button Classes="HomeButton" Grid.Row="1" Content="Subtraction" Command="{Binding Play}" CommandParameter="local:GameType.Subtraction"/>
<Button Classes="HomeButton" Grid.Row="2" Content="Multiplication" Command="{Binding Play}" CommandParameter="local:GameType.Multiplication"/>
<Button Classes="HomeButton" Grid.Row="3" Content="Division" Command="{Binding Play}" CommandParameter="local:GameType.Division"/>
<Button Classes="HomeButton" Grid.Row="4" Content="Random" Command="{Binding Play}" CommandParameter="local:GameType.Random"/>
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="local:GameType.Addition"/>
<Button Classes="HomeButton" Grid.Row="1" Content="Subtraction" Command="{Binding Play}" CommandParameter="local:GameType.Subtraction"/>
<Button Classes="HomeButton" Grid.Row="2" Content="Multiplication" Command="{Binding Play}" CommandParameter="local:GameType.Multiplication"/>
<Button Classes="HomeButton" Grid.Row="3" Content="Division" Command="{Binding Play}" CommandParameter="local:GameType.Division"/>
<Button Classes="HomeButton" Grid.Row="4" Content="Random" Command="{Binding Play}" CommandParameter="local:GameType.Random"/>
I believe this should work might be a bit off with the syntax
Mekasu0124
Mekasu0124OP14mo ago
what's the difference with that and just using a string? That seems to overcomplicate what it's passing / usage for no reason
JakenVeina
JakenVeina14mo ago
CommandParameter="local:GameType.Addition"
CommandParameter="local:GameType.Addition"
should be the equivalent of
button.CommandParameter = GameType.Addition;
button.CommandParameter = GameType.Addition;
as opposed to
button.CommandParameter = "Addition";
button.CommandParameter = "Addition";
which is the difference between your command in the ViewModel being....
private void ExecutePlay(GameType gameType)
{

}
private void ExecutePlay(GameType gameType)
{

}
versus
private void ExecutePlay(string gameType)
{
var parsedGameType = gameType switch
{
"Addition" => GameType.Addition,
"Subtraction" => GameType.Subtraction,
"Multiplication" => GameType.Multiplication,
"Division" => GameType.Division,
"Random" => GameType.Random,
_ => throw new Exception("Invalid GameType")
};
}
private void ExecutePlay(string gameType)
{
var parsedGameType = gameType switch
{
"Addition" => GameType.Addition,
"Subtraction" => GameType.Subtraction,
"Multiplication" => GameType.Multiplication,
"Division" => GameType.Division,
"Random" => GameType.Random,
_ => throw new Exception("Invalid GameType")
};
}
Mekasu0124
Mekasu0124OP14mo ago
public HomeScreenViewModel()
{
Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType), okEnabled);
PreviousGames = ReactiveCommand.Create<string, Game>(gameType => ShowPreviousGames(gameType));
BackButton = ReactiveCommand.Create<string, Game>(gameType => SendBackButton(gameType));
}

public Game PlayGame(string gameType)
{
int? totalQues = Convert.ToInt32(MaxQues);

return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}

public Game ShowPreviousGames(string gameType)
{
return new Game
{
GameType = "Previous Games"
};
}

public Game SendBackButton(string gameType)
{
return new Game
{
GameType = "Back Button"
};
}

public ReactiveCommand<string, Game> Play { get; }
public ReactiveCommand<string, Game> PreviousGames { get; }
public ReactiveCommand<string, Game> BackButton { get; }
public HomeScreenViewModel()
{
Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType), okEnabled);
PreviousGames = ReactiveCommand.Create<string, Game>(gameType => ShowPreviousGames(gameType));
BackButton = ReactiveCommand.Create<string, Game>(gameType => SendBackButton(gameType));
}

public Game PlayGame(string gameType)
{
int? totalQues = Convert.ToInt32(MaxQues);

return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}

public Game ShowPreviousGames(string gameType)
{
return new Game
{
GameType = "Previous Games"
};
}

public Game SendBackButton(string gameType)
{
return new Game
{
GameType = "Back Button"
};
}

public ReactiveCommand<string, Game> Play { get; }
public ReactiveCommand<string, Game> PreviousGames { get; }
public ReactiveCommand<string, Game> BackButton { get; }
if this makes any difference, this is how those buttons are setup
JakenVeina
JakenVeina14mo ago
point being if you're GOING to have an enum in the business layer it should be the View's job to convert strings to that enum, if it needs to use strings in the View layer but in this case, that's not even necessary the View layer can use the enum directly, and there's no need for parsing/conversion logic
Mekasu0124
Mekasu0124OP14mo ago
at this point I might as well either just scratch it and find her one that's already built, or literally just show you every single file of this project and redo the entire thing
JakenVeina
JakenVeina14mo ago
A) I said earlier, I'm going to present an idealized way of structuring this, as a learning experience, but you are free to not implement it that way. So long as you understand the concepts. B) Why is scrapping chunks of this and redoing them a bad thing? That's quite normal, even for experienced developers. you're still learning, either way
Mekasu0124
Mekasu0124OP14mo ago
it's nothing to do with, or against, you. I just can't get away from feeling like I'm always dumb with this stuff. Programming is my favorite thing to do. I can spend hours doing it and never get burnt out. Literally. I've been at it for 3+ years now. I've learned Python, Web Dev with ReactJS, and now I'm on C#, but in every language, I always feel like I'm more stupid than I'm actually doing anything right and it's no one fault but my own. It's a pride issue. I'll write out my code and be so proud of it and feel like I did the best work I can do, and then I'll share it and I've done it in such a newb way that I feel like I never learned anything and just threw plaster on the wall. What I've learned, I've learned from YouTube because my best friend who taught me Python has practically abandoned me because he'd rather spend all of his time doing debates and working with other people than with me anymore and so I lost that person that was there for me to program with all the time. I don't know how to read most documentation. With avalonia alone, the only documentation I know how to read is to pull up the control, click it's link and go to it's properties, and look at its styling file on the repo. I'm great with front end stuff. Always have been since I started with react js, but with this logic crap and backend stuff, it's just so difficult for me to grasp the concept of and I always feel stupid regardless to what I learn. TL:DR => the problem is me
JakenVeina
JakenVeina14mo ago
I'm sorry to hear that but this is also, still, rather normal there will always be things you don't know and techniques you've never thought of
Mekasu0124
Mekasu0124OP14mo ago
so given that I've changed the command parameters to this, how does my view model need to change?
JakenVeina
JakenVeina14mo ago
that's why programming IS a collaborative profession
Mekasu0124
Mekasu0124OP14mo ago
I know and you're right. I just can't get past my own self with it
JakenVeina
JakenVeina14mo ago
assuming I'm right about that syntax, each of your commands now takes GameType instead of string as its input parameter bbl
Mekasu0124
Mekasu0124OP14mo ago
https://pastebin.com/KeYRcgtF this is the view model for those buttons ok so with making changes I now have changed my
public class Game
{
public int? Id { get; set; }
public string? Username { get; set; }
public string? Date { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? Score { get; set; }
public int? TotalQuestions { get; set; }
public string? Difficulty { get; set; }
public GameTypes? GameType { get; set; }
}
public class Game
{
public int? Id { get; set; }
public string? Username { get; set; }
public string? Date { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? Score { get; set; }
public int? TotalQuestions { get; set; }
public string? Difficulty { get; set; }
public GameTypes? GameType { get; set; }
}
game model, and I have changed all the strings that were associated with the game type to GameTypes in the HomeScreenViewModel now to make the changes with the Difficulties so since my game model no longer takes Difficulty as a string, how do I need to do my list Difficulties = new() { "Select One", "Easy", "Medium", "Hard" };?
public class HomeScreenViewModel : ViewModelBase
{
public string? _information;
public Difficulties? _selectedDiff;
public string? _maxQues;

public List<Difficulties?>? _difficulties;
public List<string?>? _numberRange;
public class HomeScreenViewModel : ViewModelBase
{
public string? _information;
public Difficulties? _selectedDiff;
public string? _maxQues;

public List<Difficulties?>? _difficulties;
public List<string?>? _numberRange;
so I've changed these
Difficulties = new List<Difficulties>() { Difficulties.Easy, Difficulties.Medium, Difficulties.Hard }; // this one

var numbers = Enumerable.Range(1, 25);
NumberRange = new() { "Select One" };
NumberRange.AddRange(numbers.Select(x => x.ToString()));

SelectedDiff = Difficulties[0]; // this one
MaxQues = NumberRange[0];
Difficulties = new List<Difficulties>() { Difficulties.Easy, Difficulties.Medium, Difficulties.Hard }; // this one

var numbers = Enumerable.Range(1, 25);
NumberRange = new() { "Select One" };
NumberRange.AddRange(numbers.Select(x => x.ToString()));

SelectedDiff = Difficulties[0]; // this one
MaxQues = NumberRange[0];
I dont 'know how to fix these
public Game ShowPreviousGames(GameTypes gameType)
{
return new Game { GameType = "Previous Games" };
}

public Game SendBackButton(GameTypes gameType)
{
return new Game { GameType = "Back Button" };
}
public Game ShowPreviousGames(GameTypes gameType)
{
return new Game { GameType = "Previous Games" };
}

public Game SendBackButton(GameTypes gameType)
{
return new Game { GameType = "Back Button" };
}
or these
public string? Information
{
get => _information;
set => this.RaiseAndSetIfChanged(ref _information, value);
}
public Difficulties? SelectedDiff
{
get => _selectedDiff;
set => this.RaiseAndSetIfChanged(ref _selectedDiff, value);
}
public string? MaxQues
{
get => _maxQues;
set => this.RaiseAndSetIfChanged(ref _maxQues, value);
}
public List<Difficulties?>? Difficulties
{
get => _difficulties;
set => this.RaiseAndSetIfChanged(ref _difficulties, value);
}
public List<string?>? NumberRange
{
get => _numberRange;
set => this.RaiseAndSetIfChanged(ref _numberRange, value);
}

public ReactiveCommand<GameTypes, Game> Play { get; }
public ReactiveCommand<GameTypes, Game> PreviousGames { get; }
public ReactiveCommand<GameTypes, Game> BackButton { get; }
public string? Information
{
get => _information;
set => this.RaiseAndSetIfChanged(ref _information, value);
}
public Difficulties? SelectedDiff
{
get => _selectedDiff;
set => this.RaiseAndSetIfChanged(ref _selectedDiff, value);
}
public string? MaxQues
{
get => _maxQues;
set => this.RaiseAndSetIfChanged(ref _maxQues, value);
}
public List<Difficulties?>? Difficulties
{
get => _difficulties;
set => this.RaiseAndSetIfChanged(ref _difficulties, value);
}
public List<string?>? NumberRange
{
get => _numberRange;
set => this.RaiseAndSetIfChanged(ref _numberRange, value);
}

public ReactiveCommand<GameTypes, Game> Play { get; }
public ReactiveCommand<GameTypes, Game> PreviousGames { get; }
public ReactiveCommand<GameTypes, Game> BackButton { get; }
I changed these to match as well
Mekasu0124
Mekasu0124OP14mo ago
https://pastebin.com/MQYj5tfr I think I got it figured out
Pastebin
using ReactiveUI;using System;using System.Collections.Generic;usin...
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
JakenVeina
JakenVeina14mo ago
well, uhh gg, then, I guess
public List<Difficulties?>? _difficulties;
public List<Difficulties?>? _difficulties;
Mekasu0124
Mekasu0124OP14mo ago
do I have it wrong?
JakenVeina
JakenVeina14mo ago
this should be List<Difficulties>, should it not? certainly, the list itself shouldn't be null and it shouldn't includenull within the list, right?
Mekasu0124
Mekasu0124OP14mo ago
it's not null. I removed the ?'s
JakenVeina
JakenVeina14mo ago
k trick...
_difficulties = Enum.GetVaues<Difficulties>();
_difficulties = Enum.GetVaues<Difficulties>();
also, generally, enum names should be singular I.E. Difficulty not Difficulties except if it's a Flags enum
Mekasu0124
Mekasu0124OP14mo ago
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameTypes
{
Addition,
Subtraction,
Multiplication,
Division,
Random,
PreviousQuestions,
BackButton
}
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameTypes
{
Addition,
Subtraction,
Multiplication,
Division,
Random,
PreviousQuestions,
BackButton
}
so these are now these
JakenVeina
JakenVeina14mo ago
GameType versus GameTypes and as a side note, I would probaly argue that PreviousQuestions and BackButton should not be in there or else GameType should be renamed to better reflect what it's purpose is like PreviousQuestions isn't really a "type" of game it's what you're calling a different page in your app an additional note you can cheat a little bit
Mekasu0124
Mekasu0124OP14mo ago
so much to change it's getting a bit overloading. It's not you it's me
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameTypes
{
Addition,
Subtraction,
Multiplication,
Division,
Random
}
public enum Operation
{
Addition,
Subtraction,
Multiplication,
Division
}

public enum Difficulty
{
Easy,
Medium,
Hard
}

public enum GameTypes
{
Addition,
Subtraction,
Multiplication,
Division,
Random
}
ok so I have these
JakenVeina
JakenVeina14mo ago
public enum Operation
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3
}

public enum Difficulty
{
Easy = 0,
Medium = 1,
Hard = 2
}

public enum GameTypes
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3,
Random = 4,
PreviousQuestions = 5,
BackButton = 6
}
public enum Operation
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3
}

public enum Difficulty
{
Easy = 0,
Medium = 1,
Hard = 2
}

public enum GameTypes
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3,
Random = 4,
PreviousQuestions = 5,
BackButton = 6
}
Mekasu0124
Mekasu0124OP14mo ago
public class HomeScreenViewModel : ViewModelBase
{
public string? _information;
public Difficulty? _selectedDiff;
public string? _maxQues;

public Difficulty[] _allDifficulties;
public List<string?>? _numberRange;
public class HomeScreenViewModel : ViewModelBase
{
public string? _information;
public Difficulty? _selectedDiff;
public string? _maxQues;

public Difficulty[] _allDifficulties;
public List<string?>? _numberRange;
I have this
JakenVeina
JakenVeina14mo ago
if you do this.... with the explicit number definitions you can make the RNG stuff a lot easier
public Operation GetOperation(GameType gameType)
=> (gameType is GameType.Random)
? (Operation)_rng.Next(0, 3)
: (Operation)gameType;
public Operation GetOperation(GameType gameType)
=> (gameType is GameType.Random)
? (Operation)_rng.Next(0, 3)
: (Operation)gameType;
Mekasu0124
Mekasu0124OP14mo ago
I'm just going to start from the beginning
JakenVeina
JakenVeina14mo ago
if you want to
Mekasu0124
Mekasu0124OP14mo ago
what do I have wrong here
JakenVeina
JakenVeina14mo ago
that seems a bit silly from here
Mekasu0124
Mekasu0124OP14mo ago
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MeksMathGame.App"
xmlns:local="using:MeksMathGame"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>

<Application.Styles>
<FluentTheme />
</Application.Styles>

<Application.Resources>
<SolidColorBrush x:Key="Purple">#5503AC</SolidColorBrush>
<SolidColorBrush x:Key="Pink">#AE03C6</SolidColorBrush>
<SolidColorBrush x:Key="DarkGray">#929192</SolidColorBrush>
</Application.Resources>
</Application>
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MeksMathGame.App"
xmlns:local="using:MeksMathGame"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>

<Application.Styles>
<FluentTheme />
</Application.Styles>

<Application.Resources>
<SolidColorBrush x:Key="Purple">#5503AC</SolidColorBrush>
<SolidColorBrush x:Key="Pink">#AE03C6</SolidColorBrush>
<SolidColorBrush x:Key="DarkGray">#929192</SolidColorBrush>
</Application.Resources>
</Application>
JakenVeina
JakenVeina14mo ago
looks fine to me
Mekasu0124
Mekasu0124OP14mo ago
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MeksMathGame.Views.MainWindow"
Icon="/Assets/logo.ico"
Title="Mek's Math Game"
Width="1500"
Height="850"
Content="{Binding Content}"
WindowStartupLocation="CenterScreen">
</Window>
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MeksMathGame.Views.MainWindow"
Icon="/Assets/logo.ico"
Title="Mek's Math Game"
Width="1500"
Height="850"
Content="{Binding Content}"
WindowStartupLocation="CenterScreen">
</Window>
ok main window
JakenVeina
JakenVeina14mo ago
ignoring the stuff that's commented out what is Database() here? it's not an ACUTAL database, is it?
Mekasu0124
Mekasu0124OP14mo ago
my database System.Data.SQLite
JakenVeina
JakenVeina14mo ago
is there an SqLiteConnection in there?
Mekasu0124
Mekasu0124OP14mo ago
haven't shown the database file yet
JakenVeina
JakenVeina14mo ago
you have not
Mekasu0124
Mekasu0124OP14mo ago
I was taking it one file at the time for my brains sake. here comes the database file
Mekasu0124
Mekasu0124OP14mo ago
and the github service for the update
JakenVeina
JakenVeina14mo ago
okay, that all looks fine except for the liberal use of static what do you use github for?
Mekasu0124
Mekasu0124OP14mo ago
I have removed all static from the database file
JakenVeina
JakenVeina14mo ago
self-updating?
Mekasu0124
Mekasu0124OP14mo ago
when the application launches, it checks the github repo for the latest release, and if there is a release that is newer than the version that is in the version.txt file, then it prompts the user that there is an update and asks if they want to update
JakenVeina
JakenVeina14mo ago
mm-kay bit cheaty, but it works
Mekasu0124
Mekasu0124OP14mo ago
that's the only way I learned how to build an updater into my project. The installer project hasn't been built into this project yet.
Mekasu0124
Mekasu0124OP14mo ago
but the GithubServices pulls tid bits of info from the api call, and returns it for the program to know what to do and when. If the version.txt in the version that user has says v1.0.2 and I've pushed a release to the github for v2.0.1 then it'll alert the user that an update is availabe and ask if they want to update which is done here
JakenVeina
JakenVeina14mo ago
you'd probably be better off encoding the version into the assembly itself instead of a text file
Mekasu0124
Mekasu0124OP14mo ago
I don't know where/how I would do that. I did the file because when I'm re-publishing the application, I'll think to myself "fuck I forgot to update the version" and it'll remind me to go to the file and update it then re-publish the app
JakenVeina
JakenVeina14mo ago
Stack Overflow
How can I get the assembly file version
In AssemblyInfo there are two assembly versions: AssemblyVersion: Specify the version of the assembly being attributed. AssemblyFileVersion: Instructs a compiler to use a specific version number f...
Mekasu0124
Mekasu0124OP14mo ago
how is that going to know the version of my application?
JakenVeina
JakenVeina14mo ago
the version is baked into the assembly
Mekasu0124
Mekasu0124OP14mo ago
where though? like what file?
JakenVeina
JakenVeina14mo ago
with an attribute attached to the assembly which is generally applied within an AssemblyInfo.cs file
Mekasu0124
Mekasu0124OP14mo ago
ok I don't know what assembly means
JakenVeina
JakenVeina14mo ago
Stack Overflow
C#: how to set version number of assembly
I have written a DLL in C# using VS2005.
Currently the DLL is showing a version number of 1.0.0.0. How do I set this version number to something different?
JakenVeina
JakenVeina14mo ago
"Assemby" is "the thing your program compiles to" the DLL or EXE file
Mekasu0124
Mekasu0124OP14mo ago
I don't have that in my file explorer in visual studio so how do I access it?
JakenVeina
JakenVeina14mo ago
it's a file add it make it whatever you want or put it in an existing file AssemblyVersion is an attribute you attach to the asembly, which is code you can put it anywhere
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.0.0.0")]
Mekasu0124
Mekasu0124OP14mo ago
what's the difference in that and me having the version.txt file? like what makes that better?
JakenVeina
JakenVeina14mo ago
txt file is a little more fragile what if the file is missing? or if someone messes with it?
Mekasu0124
Mekasu0124OP14mo ago
ok I'm with you on that
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MeksMathGame
{
internal class AssemblyInfo
{
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MeksMathGame
{
internal class AssemblyInfo
{
}
}
I made an AssemblyInfo.cs file so what do I do with it?
JakenVeina
JakenVeina14mo ago
I already told you put the code in it
Mekasu0124
Mekasu0124OP14mo ago
No description
Mekasu0124
Mekasu0124OP14mo ago
I did and that's what it told me
JakenVeina
JakenVeina14mo ago
okay
Mekasu0124
Mekasu0124OP14mo ago
and I put that file in the root of the game project itself not the root of the whole project since I'll be adding in a second project for the installer
No description
JakenVeina
JakenVeina14mo ago
right, that's correct within the project, not the solution
Mekasu0124
Mekasu0124OP14mo ago
right so how come it's giving me a duplication error when that's the only file with that code that I believe I have?
JakenVeina
JakenVeina14mo ago
because that's not the only file with that code
Mekasu0124
Mekasu0124OP14mo ago
I double clicked my project
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
<Content Include="Assets\logo.ico" />
<Folder Include="Styles\" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.2" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.2" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
<Content Include="Assets\logo.ico" />
<Folder Include="Styles\" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.2" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.2" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>
</Project>
and I'm not seeing another file that would cause the dupe yea I do app.manifest
JakenVeina
JakenVeina14mo ago
sweet
Mekasu0124
Mekasu0124OP14mo ago
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="MeksMathGame.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embeded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="MeksMathGame.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
JakenVeina
JakenVeina14mo ago
:/
Mekasu0124
Mekasu0124OP14mo ago
so if it has it there, then I don't need the cs file. I can just do it here then
JakenVeina
JakenVeina14mo ago
that's interesting yeah I guess that's an Avalonia thing sort of I mean, the app.manifest file is a requirement if you want to deploy to the Windows store
Mekasu0124
Mekasu0124OP14mo ago
I doubt I'll ever do that honeslty lol
JakenVeina
JakenVeina14mo ago
not sure if it's relevant for you deploying manually what's probably happening then is Avalonia is automatically generating a .cs file from that XML, with the AssemblyVersion attribute in it
Mekasu0124
Mekasu0124OP14mo ago
public class GithubService
{
private static HttpClient _client = new();
private static readonly string? _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
private static readonly string? versionFile = Path.Combine(Directory.GetCurrentDirectory(), "version.txt");
public class GithubService
{
private static HttpClient _client = new();
private static readonly string? _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
private static readonly string? versionFile = Path.Combine(Directory.GetCurrentDirectory(), "version.txt");
so here where I call the version file, I would replace that?
JakenVeina
JakenVeina14mo ago
you can right-click on AssemblyVersion where you had it written and click "Find All References" and see more like you would get rid of what, and wherever you have the code that reads from that file, you'd read from the assembly instead via what I linked earlier
Mekasu0124
Mekasu0124OP14mo ago
oh I already deleted that cs file I was just going to use the version in the app.manifest
JakenVeina
JakenVeina14mo ago
you'll find that much more difficult to read and also, that's not guaranteed to be there, same as the txt file like, parsing that out of XML is doable, but it's cumbersome compared to a one-line reflection call
Mekasu0124
Mekasu0124OP14mo ago
ok so I put the AssemblyInfo.cs file back
JakenVeina
JakenVeina14mo ago
no
Mekasu0124
Mekasu0124OP14mo ago
sure. I deleted it
JakenVeina
JakenVeina14mo ago
Stack Overflow
How can I get the assembly file version
In AssemblyInfo there are two assembly versions: AssemblyVersion: Specify the version of the assembly being attributed. AssemblyFileVersion: Instructs a compiler to use a specific version number f...
Mekasu0124
Mekasu0124OP14mo ago
so what specifically do I need to do
Mekasu0124
Mekasu0124OP14mo ago
No description
No description
Mekasu0124
Mekasu0124OP14mo ago
so I'm guessing I need to make the entire class static?
JakenVeina
JakenVeina14mo ago
no you need to move logic into methods in this case, the constructor or just get rid of those fields
Mekasu0124
Mekasu0124OP14mo ago
the public record version datat too?
JakenVeina
JakenVeina14mo ago
that's not logic that's a class definition
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
there's no need for it to be any more complicated than that
Mekasu0124
Mekasu0124OP14mo ago
ok so now that is set to be the VersionData's current version
public class GithubService
{
private static HttpClient _client = new();
private static readonly string? _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
public record VersionData(string? CurrentVersion, FileVersionInfo LatestVersion, string? ReleaseDate, bool isNewer);

public async Task<VersionData?>? CheckVersion()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseData?>(_url);
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
string? date = data.ReleaseDate.ToString();

return new VersionData(data.TagName, version, date, data.TagName != version);
}
catch (HttpRequestException ex)
{
return new VersionData("Error: ", ex.Message, DateTimeOffset.Now.ToString("MM/dd/yyyy"), false);
}
}
}
public class GithubService
{
private static HttpClient _client = new();
private static readonly string? _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
public record VersionData(string? CurrentVersion, FileVersionInfo LatestVersion, string? ReleaseDate, bool isNewer);

public async Task<VersionData?>? CheckVersion()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");

try
{
var data = await _client.GetFromJsonAsync<ReleaseData?>(_url);
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
string? date = data.ReleaseDate.ToString();

return new VersionData(data.TagName, version, date, data.TagName != version);
}
catch (HttpRequestException ex)
{
return new VersionData("Error: ", ex.Message, DateTimeOffset.Now.ToString("MM/dd/yyyy"), false);
}
}
}
ok here's where I'm at so far my first problem is the bool check data.TagName != version => can't do that
JakenVeina
JakenVeina14mo ago
why not?
Mekasu0124
Mekasu0124OP14mo ago
Operator != cannot be applied to operands of type string and FileVersionInfo
JakenVeina
JakenVeina14mo ago
and what does that mean?
Mekasu0124
Mekasu0124OP14mo ago
I don't know how to fix that because the Tag Name that is pulled from the api call is a [JsonPropertyName("tag_name")] and is a string when it comes from the api
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }

[JsonPropertyName("published_at")]
public DateTimeOffset? ReleaseDate { get; set; }
}
public class ReleaseData
{
[JsonPropertyName("tag_name")]
public string? TagName { get; set; }

[JsonPropertyName("published_at")]
public DateTimeOffset? ReleaseDate { get; set; }
}
Mekasu0124
Mekasu0124OP14mo ago
what do I do with that
JakenVeina
JakenVeina14mo ago
read it
Mekasu0124
Mekasu0124OP14mo ago
so I can do string vName = version.FileVersion; and fix it
JakenVeina
JakenVeina14mo ago
possibly they're strings, so that doesn't guarantee that they'll be the same value, when they represent the same version if the documentation doesn't specify what FileVersion looks like, you'll want to test and make sure in theory, it's just going to return whatever string you gave to [AssemblyVersion()] so as long as you use the exact same string as the tag in github, it'll work
Mekasu0124
Mekasu0124OP14mo ago
when I hover over it to read it's snippet, it shows it as a string
No description
Mekasu0124
Mekasu0124OP14mo ago
I can do 1.0.0.0 instead of v1.0.0 for my release names. That's not a problem
JakenVeina
JakenVeina14mo ago
oh actually okay, so there's a bit of nuance here there's actually three different version IDs you can attach to your assembly AssemblyVersion, AssemblyFileVersion, and AssemblyInformationVersion each of those is retrieved slightly differently
Mekasu0124
Mekasu0124OP14mo ago
which one do I need because in the app.manifest it has <assemblyIdentity version="1.0.0.0" name="MeksMathGame.Desktop" /> and I thought that's what we were working with
JakenVeina
JakenVeina14mo ago
that one refers to AssemblyVersion at least, that's what you told me looks like best practice is actually to use AssemblyFileVersion for deployments
Mekasu0124
Mekasu0124OP14mo ago
ok so then how do I use that
JakenVeina
JakenVeina14mo ago
the code you've got for retrieving the version is right for setting it...
Mekasu0124
Mekasu0124OP14mo ago
I'm so lost
JakenVeina
JakenVeina14mo ago
how so?
Mekasu0124
Mekasu0124OP14mo ago
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MeksMathGame.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MeksMathGame.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
app.manifest
JakenVeina
JakenVeina14mo ago
right that has an assemblyIdentity tag
Mekasu0124
Mekasu0124OP14mo ago
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
this should pull that version number
JakenVeina
JakenVeina14mo ago
no it doesn't that's what I'm saying you said earlier that assemblyIdentity in app.manifest was the cause of your duplicate AssemblyVersion error presumably, that XML file is source-generated into a .cs file that has that version value in an AssemblyVersion attribute what you should possibly actually set and use for deployments is AssemblyFileVersion
Mekasu0124
Mekasu0124OP14mo ago
ok wait that's my bad wait it's in the Debug > net7.0 folder. that's why didn't see it
Mekasu0124
Mekasu0124OP14mo ago
that's the duplication cause
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
right as it says auto-generated now, we can see that AssemblyFileVersionAttribute and AssemblyVersionAttribute are both there
Mekasu0124
Mekasu0124OP14mo ago
right
JakenVeina
JakenVeina14mo ago
Generated by the MSBuild WriteCodeFragment class.
is that a class you have?
Mekasu0124
Mekasu0124OP14mo ago
not that I'm aware of. I didn't create it if that's what you're asking it likely came pre-gen'd with the framework when I created the project
JakenVeina
JakenVeina14mo ago
if you change the value in app.manifest do the values in this generated file change?
Mekasu0124
Mekasu0124OP14mo ago
I don't know. I have errors in all of my other files that we've been working on fixing that I can't rebuild the project at this moment
JakenVeina
JakenVeina14mo ago
check your .csproj file, are they set there?
Mekasu0124
Mekasu0124OP14mo ago
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
<Content Include="Assets\logo.ico" />
<Folder Include="Styles\" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.2" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.2" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>false</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<Folder Include="Models\" />
<AvaloniaResource Include="Assets\**" />
<Content Include="Assets\logo.ico" />
<Folder Include="Styles\" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.2" />
<PackageReference Include="Avalonia.Desktop" Version="11.0.2" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.2" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.0.2" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.2" />
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.2" />
<PackageReference Include="System.Data.SQLite" Version="1.0.118" />
</ItemGroup>
</Project>
no
JakenVeina
JakenVeina14mo ago
mm-kay well, I'll wager they're being set from the app.manifest, then put a pin in that, I guess ultimately, just make sure the versions are gonna match
Mekasu0124
Mekasu0124OP14mo ago
I can try but if I don't know where to set them then I don't know how to update them to make them match
JakenVeina
JakenVeina14mo ago
FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location)
FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location)
pulls the file version
Assembly.GetExecutingAssembly().Version
Assembly.GetExecutingAssembly().Version
pulls the assembly version
Mekasu0124
Mekasu0124OP14mo ago
so change it in my github service to this Assembly.GetExecutingAssembly().Version
JakenVeina
JakenVeina14mo ago
whichever you want to use so long as you're consistent
Mekasu0124
Mekasu0124OP14mo ago
so the full line is what? var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Version);? oh wait no I misunderstood
public class GithubService
{
private static HttpClient _client = new();
private static readonly string _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
public record VersionData(string CurrentVersion, string LatestVersion, string? ReleaseDate, bool isNewer);

public async Task<VersionData> CheckVersion()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
string vName = version.FileVersion;

try
{
var data = await _client.GetFromJsonAsync<ReleaseData?>(_url);
string date = data.ReleaseDate.ToString();

return new VersionData(data.TagName, vName, date, data.TagName != vName);
}
catch (HttpRequestException ex)
{
return new VersionData("Error: ", ex.Message, DateTimeOffset.Now.ToString("MM/dd/yyyy"), false);
}
}
}
public class GithubService
{
private static HttpClient _client = new();
private static readonly string _url = "https://api.github.com/repos/mekasu0124/MeksMathGame/releases/latest";
public record VersionData(string CurrentVersion, string LatestVersion, string? ReleaseDate, bool isNewer);

public async Task<VersionData> CheckVersion()
{
_client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");
var version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
string vName = version.FileVersion;

try
{
var data = await _client.GetFromJsonAsync<ReleaseData?>(_url);
string date = data.ReleaseDate.ToString();

return new VersionData(data.TagName, vName, date, data.TagName != vName);
}
catch (HttpRequestException ex)
{
return new VersionData("Error: ", ex.Message, DateTimeOffset.Now.ToString("MM/dd/yyyy"), false);
}
}
}
here's what I have
JakenVeina
JakenVeina14mo ago
okay
Mekasu0124
Mekasu0124OP14mo ago
so since we changed the Game model from difficulty and game type of being strings to enum's, when I read that information from the database, how am I supposed to do it?
public List<Game> GetAllGames()
{
using SQLiteConnection sqlite = new SQLiteConnection(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<Game> allGames = new();

sqlite.Open();

reader = cmd.ExecuteReader();

while(reader.Read())
{
Game game = new()
{
Id = int.Parse(reader["Id"].ToString()),
Username = reader["Username"].ToString(),
Date = reader["Today"].ToString(),
StartTime = reader["StartTime"].ToString(),
EndTime = reader["EndTime"].ToString(),
Duration = double.Parse(reader["Duration"].ToString()),
TotalQuestions = int.Parse(reader["TotalQuestions"].ToString()),
Difficulty = reader["Difficulty"].ToString(),
GameType = reader["GameType"].ToString()
};

allGames.Add(game);
}

return allGames;
}
public List<Game> GetAllGames()
{
using SQLiteConnection sqlite = new SQLiteConnection(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<Game> allGames = new();

sqlite.Open();

reader = cmd.ExecuteReader();

while(reader.Read())
{
Game game = new()
{
Id = int.Parse(reader["Id"].ToString()),
Username = reader["Username"].ToString(),
Date = reader["Today"].ToString(),
StartTime = reader["StartTime"].ToString(),
EndTime = reader["EndTime"].ToString(),
Duration = double.Parse(reader["Duration"].ToString()),
TotalQuestions = int.Parse(reader["TotalQuestions"].ToString()),
Difficulty = reader["Difficulty"].ToString(),
GameType = reader["GameType"].ToString()
};

allGames.Add(game);
}

return allGames;
}
right now, Difficulty and GaemType are giving errors that Possible null reference argument for parameter 's' in 'int int.Parse(String s)'
JakenVeina
JakenVeina14mo ago
uhm no
Mekasu0124
Mekasu0124OP14mo ago
public enum Operation { Addition, Subtraction, Multiplication, Division }
public enum Difficulty { Easy, Medium, Hard }
public enum GameTypes { Addition, Subtraction, Multiplication, Division, Random }
public enum Operation { Addition, Subtraction, Multiplication, Division }
public enum Difficulty { Easy, Medium, Hard }
public enum GameTypes { Addition, Subtraction, Multiplication, Division, Random }
i left these as-is
JakenVeina
JakenVeina14mo ago
okay
Mekasu0124
Mekasu0124OP14mo ago
when the table's for the database are created, the difficulty and game type are VARCHAR(2048) so since we're dealing with enums now instead of string, do I need to change that too? and how do I read it back from the database since I can't do the .ToString() on the reader index?
JakenVeina
JakenVeina14mo ago
you've got two options SQLite doesn't have any kinda special support for enum types, so you have to store them as one of the 5 types it DOES support, and do a conversion A) keep them as VARCHAR, I.E. strings, and you can use .ToString() and Enum.Parse<T>()
Mekasu0124
Mekasu0124OP14mo ago
public void CreateNewGame(Game game)
{
using SQLiteConnection? sqlite = new(dbFile);
using SQLiteCommand? cmd = sqlite.CreateCommand();

sqlite.Open();

cmd.CommandText = @"INSERT INTO games(
Username, TodaysDate, StartTime, EndTime, Duration, TotalQuestions, Difficulty, GameType)
VALUES ($un, $date, $start, $end, $duration, $total, $difficulty, $game)";
cmd.Parameters.AddWithValue("$un", game.Username);
cmd.Parameters.AddWithValue("$date", game.Date);
cmd.Parameters.AddWithValue("$start", game.StartTime);
cmd.Parameters.AddWithValue("$end", game.EndTime);
cmd.Parameters.AddWithValue("$duration", game.Duration);
cmd.Parameters.AddWithValue("$total", game.TotalQuestions);
cmd.Parameters.AddWithValue("$difficulty", game.Difficulty.ToString());
cmd.Parameters.AddWithValue("$game", game.GameType.ToString());

try
{
cmd.ExecuteNonQuery();
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
}
public void CreateNewGame(Game game)
{
using SQLiteConnection? sqlite = new(dbFile);
using SQLiteCommand? cmd = sqlite.CreateCommand();

sqlite.Open();

cmd.CommandText = @"INSERT INTO games(
Username, TodaysDate, StartTime, EndTime, Duration, TotalQuestions, Difficulty, GameType)
VALUES ($un, $date, $start, $end, $duration, $total, $difficulty, $game)";
cmd.Parameters.AddWithValue("$un", game.Username);
cmd.Parameters.AddWithValue("$date", game.Date);
cmd.Parameters.AddWithValue("$start", game.StartTime);
cmd.Parameters.AddWithValue("$end", game.EndTime);
cmd.Parameters.AddWithValue("$duration", game.Duration);
cmd.Parameters.AddWithValue("$total", game.TotalQuestions);
cmd.Parameters.AddWithValue("$difficulty", game.Difficulty.ToString());
cmd.Parameters.AddWithValue("$game", game.GameType.ToString());

try
{
cmd.ExecuteNonQuery();
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
}
ok I make them strings when they're written to the database ok I'll try the enum.parse
JakenVeina
JakenVeina14mo ago
Enum.Parse<T>() you can use in the UI layer as well, if you had the opportunity, but it has to be an EXACT match if you're only ever inserting into the database from .ToString() then that's not a problem option B) cast to int and store and retrieve as NUMBER conversion is simpler in this case, it's just a plan cast both ways since enums are already ints under the hood it's a liiiiiiiiiiiittle risky if you're not explicitly defining WHAT int value each enum has so, probably go with the string version
Mekasu0124
Mekasu0124OP14mo ago
public enum Operation
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3
}

public enum Difficulty
{
Easy = 0,
Medium = 1,
Hard = 2
}

public enum GameTypes
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3,
Random = 4
}
public enum Operation
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3
}

public enum Difficulty
{
Easy = 0,
Medium = 1,
Hard = 2
}

public enum GameTypes
{
Addition = 0,
Subtraction = 1,
Multiplication = 2,
Division = 3,
Random = 4
}
I did this so with doing that, I need to change the tables to be INTEGERS instead of VARCHAR
JakenVeina
JakenVeina14mo ago
you can if you want both ways are valid both have advantages and disadvantages
Mekasu0124
Mekasu0124OP14mo ago
public List<Game> GetAllGames()
{
using SQLiteConnection sqlite = new(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<Game> allGames = new();

sqlite.Open();

reader = cmd.ExecuteReader();

while(reader.Read())
{
Game game = new()
{
Id = int.Parse(reader["Id"].ToString()),
Username = reader["Username"].ToString(),
Date = reader["Today"].ToString(),
StartTime = reader["StartTime"].ToString(),
EndTime = reader["EndTime"].ToString(),
Duration = double.Parse(reader["Duration"].ToString()),
TotalQuestions = int.Parse(reader["TotalQuestions"].ToString()),
Difficulty = Enum.Parse<int>(reader["Difficulty"].ToString()),
GameType = Enum.Parse<T>(reader["GameType"])
};

allGames.Add(game);
}

return allGames;
}
public List<Game> GetAllGames()
{
using SQLiteConnection sqlite = new(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<Game> allGames = new();

sqlite.Open();

reader = cmd.ExecuteReader();

while(reader.Read())
{
Game game = new()
{
Id = int.Parse(reader["Id"].ToString()),
Username = reader["Username"].ToString(),
Date = reader["Today"].ToString(),
StartTime = reader["StartTime"].ToString(),
EndTime = reader["EndTime"].ToString(),
Duration = double.Parse(reader["Duration"].ToString()),
TotalQuestions = int.Parse(reader["TotalQuestions"].ToString()),
Difficulty = Enum.Parse<int>(reader["Difficulty"].ToString()),
GameType = Enum.Parse<T>(reader["GameType"])
};

allGames.Add(game);
}

return allGames;
}
ok I'm struggling a bit to read it back from the database. I changed them to be set to integers, but it's casting that integer back from the db to match the enum oh it's not EnumParse<int> it's Enum.Parse<Difficulty>
JakenVeina
JakenVeina14mo ago
right also that's the string approach if you're going to use INTEGER as the underlying type
Mekasu0124
Mekasu0124OP14mo ago
I need to do int.parse
JakenVeina
JakenVeina14mo ago
yes, but also no you can just to .ToInt32() on the reader instead of ToString() or something to that effect yeah, it should actually be....
Mekasu0124
Mekasu0124OP14mo ago
reader["Difficulty"] doesn't have a .ToInt32() but I can wrap it in an Convert.ToInt32()
JakenVeina
JakenVeina14mo ago
reader.GetInt32(reader.GetOrdinal("Difficulty"))
reader.GetInt32(reader.GetOrdinal("Difficulty"))
alternatively, just ((int)reader["Difficulty"]) (I think)
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
right you chose not to store them as strings
Mekasu0124
Mekasu0124OP14mo ago
so then which one do I use because nothing I am trying is working
JakenVeina
JakenVeina14mo ago
step back and asses your problem what is the problem?
Mekasu0124
Mekasu0124OP14mo ago
the same problem we've been working on the last 20 messages or so. reading it back from teh db wait my cmd doesn't have a command text so I'm not reading anything
JakenVeina
JakenVeina14mo ago
I mean true but also unrelated to the immediate problem
reading it back from teh db
reading what?
Mekasu0124
Mekasu0124OP14mo ago
you know what the problem is and so do I. I'm just trying to find the reason the thing won't convert or read it right
JakenVeina
JakenVeina14mo ago
and the reason you can't find that is because you don't actually know what the problem is you haven't thought it through, thoroughly what are you reading? the Difficulty column? what type is that?
Mekasu0124
Mekasu0124OP14mo ago
it's not anything. I don't have a database created yet. As stated earlier, it was changed from a varchar to an integer. That's already been established
JakenVeina
JakenVeina14mo ago
so it's an integer how do you read an integer out of that reader?
Mekasu0124
Mekasu0124OP14mo ago
nothing I'm trying is working so obviously I don't know. If you know then please share
JakenVeina
JakenVeina14mo ago
you do know though
Mekasu0124
Mekasu0124OP14mo ago
ok
JakenVeina
JakenVeina14mo ago
because I already did share also, you were doing it long before I showed up
Mekasu0124
Mekasu0124OP14mo ago
I'm telling you that I cannot figure it out. EVERYTHING I have tried of what I have been doing is NOT working
JakenVeina
JakenVeina14mo ago
there is a question on the table I'm not asking you to try everything and get all the errors to magically disappear I'm asking you to answer a question a question whose answer I have in fact already given to you
Mekasu0124
Mekasu0124OP14mo ago
ok well apparently I don't know what that is so I'm just stuck and it's just becoming more obviously that I'm not that intelligent so I'll just keep trying things until I figure it out or whatever
JakenVeina
JakenVeina14mo ago
or instead of flailing randomly you could answer the question on the table
Mekasu0124
Mekasu0124OP14mo ago
and what would that question be
JakenVeina
JakenVeina14mo ago
how do you read an int out of an SqliteDataReader?
Mekasu0124
Mekasu0124OP14mo ago
ok so as I have stated several times already but it apparently keeps getting missed. Everything that I have tried is not working. You stated that I've been doing it long before you showed up, which would be like Id = int.Parse(reader["Id"].ToString()), that, however, for the nth time, nothing I am doing is not working. So obviously either I've answered your question, haven't given a sufficient enough answer, or I just don't know how to answer the question at all which I don't understand how I haven't I'm starting to honestly get frustrated and it's not with you whatsoever. you're just trying to help, but I've answered that question multiple times already
JakenVeina
JakenVeina14mo ago
Id = int.Parse(reader["Id"].ToString())
so, in fact, you do know? you have made very clear that "nothing I am doing is working" I can, in fact, read can you? because what I have made repeatedly clear is that you need to step back from trying to do EVERYTHING you don't solve problems by just randomly trying "everything" you think might work you identify the problem, and break it down into smaller solvable pieces that you can put together if you constantly try to solve "everything" you're going to get nowhere right now, we are working on reading data from the database and there are quite a few errors here so, we're going to pick one and focus on that we've been talking about the Difficulty column, so that's our focus that's one single line of code
Difficulty = Enum.Parse<int>(reader["Difficulty"].ToString()),
Difficulty = Enum.Parse<int>(reader["Difficulty"].ToString()),
obviously this is wrong, and you can't see what the right solution is, so we're going to continue breaking it down what is involved in reading the Difficulty column from the database? well, it's an INTEGER in the database so, how do we read integers?
int.Parse(reader["Id"].ToString())
int.Parse(reader["Id"].ToString())
or, as I suggested, two other possibilities
reader.GetInt32(reader.GetOrdinal("Difficulty"))
reader.GetInt32(reader.GetOrdinal("Difficulty"))
or
((int)reader["Difficulty"])
((int)reader["Difficulty"])
let's go with the one I know is correct
Mekasu0124
Mekasu0124OP14mo ago
I don't have this. I have Difficulty = Enum.Parse<Difficulty>()
JakenVeina
JakenVeina14mo ago
I don't care what you have are we solving the problem or not? what you have is wrong, why would you keep going back to it we're trying to figure out what you NEED part one of that is
reader.GetInt32(reader.GetOrdinal("Difficulty"))
reader.GetInt32(reader.GetOrdinal("Difficulty"))
now we have an int what do we need to do with it? we need to put it into Difficulty what is Difficulty? it's a Difficulty enum value so, we nee to take an int and convert it to Difficulty how do we do that? I mentioned earlier, enums are int already, under the hood all we need is a cast
Difficulty = (Difficulty)reader.GetInt32(reader.GetOrdinal("Difficulty"))
Difficulty = (Difficulty)reader.GetInt32(reader.GetOrdinal("Difficulty"))
notice how there is no Enum.Parse<> anywhere in there? that's because Enum.Parse<>() is... for parsing which we're not doing because you chose to take option B of the two options earlier, the one where Difficulty is INTEGER in the database, rather than VARCHAR
Mekasu0124
Mekasu0124OP14mo ago
if I would have known anything about doing it this way, I would have said so. I didn't know that this was a thing, although I've learned the basics and have been practicing with console based applications before attempting avalonia.
JakenVeina
JakenVeina14mo ago
you did though you just couldn't put the pieces together which is fine what's not fine is just throwing up your hands and calling yourself stupid the way you work a problem you don't understand is by breaking it down that allows you to identify pieces that you DO understand and occasionally those that you don't you already knew how to retrieve int values from the database the fact that I suggested a better way to do it is doesn't change that and I told you that you could do a cast to convert int to an enum if you don't know what casting is say that "I don't know what casting is"
Mekasu0124
Mekasu0124OP14mo ago
public void CreateNewGame(Game game)
{
using SQLiteConnection? sqlite = new(dbFile);
using SQLiteCommand? cmd = sqlite.CreateCommand();

sqlite.Open();

cmd.CommandText = @"INSERT INTO games(
Username, TodaysDate, StartTime, EndTime, Duration, TotalQuestions, Difficulty, GameType)
VALUES ($un, $date, $start, $end, $duration, $total, $difficulty, $game)";
cmd.Parameters.AddWithValue("$un", game.Username);
cmd.Parameters.AddWithValue("$date", game.Date);
cmd.Parameters.AddWithValue("$start", game.StartTime);
cmd.Parameters.AddWithValue("$end", game.EndTime);
cmd.Parameters.AddWithValue("$duration", game.Duration);
cmd.Parameters.AddWithValue("$total", game.TotalQuestions);
cmd.Parameters.AddWithValue("$difficulty", game.Difficulty);
cmd.Parameters.AddWithValue("$game", game.GameType);

try
{
cmd.ExecuteNonQuery();
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
}
public void CreateNewGame(Game game)
{
using SQLiteConnection? sqlite = new(dbFile);
using SQLiteCommand? cmd = sqlite.CreateCommand();

sqlite.Open();

cmd.CommandText = @"INSERT INTO games(
Username, TodaysDate, StartTime, EndTime, Duration, TotalQuestions, Difficulty, GameType)
VALUES ($un, $date, $start, $end, $duration, $total, $difficulty, $game)";
cmd.Parameters.AddWithValue("$un", game.Username);
cmd.Parameters.AddWithValue("$date", game.Date);
cmd.Parameters.AddWithValue("$start", game.StartTime);
cmd.Parameters.AddWithValue("$end", game.EndTime);
cmd.Parameters.AddWithValue("$duration", game.Duration);
cmd.Parameters.AddWithValue("$total", game.TotalQuestions);
cmd.Parameters.AddWithValue("$difficulty", game.Difficulty);
cmd.Parameters.AddWithValue("$game", game.GameType);

try
{
cmd.ExecuteNonQuery();
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
}
since I have to convert it when reading it back from the database, do I need to do any conversions when writing it to the database?
JakenVeina
JakenVeina14mo ago
or "I don't know how to cast an int to an enum" that is a specific, solvable problem I'm not sure what SQLite will do, by default, for enum values
Mekasu0124
Mekasu0124OP14mo ago
I didn't say that because I thought I knew how to do it, and misunderstood. That is my fault and I'll take that
JakenVeina
JakenVeina14mo ago
PLEASE focus on that point about breaking down problems that is the SINGLE MOST SIGNIFICANT skill that people here often lack, when they come for help which is why I focus on it instead of just handing out code so, yeah, I dunno what SQLite will do by default, so you should be explicit
Mekasu0124
Mekasu0124OP14mo ago
I understand that, and often times I try to do that and google and find solutions on my own before coming here for help. I typically treat this as a last resort
JakenVeina
JakenVeina14mo ago
(int)game.Difficulty
(int)game.Difficulty
honestly, don't I mean, don't come here FIRST but we're more powerful than google google can answer questions we can help you understand what questions you need to ask
Mekasu0124
Mekasu0124OP14mo ago
understandable ok so now that the database has been situated, and the github service along with the app.axaml and it's cs file and the main window, I'd like to send the next pages
JakenVeina
JakenVeina14mo ago
you got all the other columns changed over? at least, GameType?
Mekasu0124
Mekasu0124OP14mo ago
correct. Once we got the difficulty column handled, I applied the same directives to the game type
JakenVeina
JakenVeina14mo ago
great
JakenVeina
JakenVeina14mo ago
so, if you removed those two pages from GameType then you need to handle them some other way, yeah? me, I would just make two separate commands
Mekasu0124
Mekasu0124OP14mo ago
wait removed what pages from GameTypes
JakenVeina
JakenVeina14mo ago
PreviousGames and... whatever the other one was
Mekasu0124
Mekasu0124OP14mo ago
oh back button ok so you're back on the home screen view model now I was a page before that with the page where the player selects/creates their user
JakenVeina
JakenVeina14mo ago
I thought that's what you were referring to you said next pages
Mekasu0124
Mekasu0124OP14mo ago
yes so the order the program is setup si 1. game launches and checks for update => if update available goes to UpdateAvailableView/ViewModel 2. if no update available => launch SelectUser()
public async Task CheckForUpdate()
{
VersionData version = await _gvs.CheckVersion();

if (version.isNewer)
{
Update(version);
}
else
{
SelectUser();
}
}

public void Update(VersionData version)
{
var vm = new UpdateAvailableViewModel(version);

Observable.Merge(vm.GoHome)
.Take(1)
.Subscribe(model =>
{
if (model == null)
{
SelectUser();
}
});

Content = vm;
}

public void SelectUser()
{
var vm = new SelectUserViewModel();

Observable.Merge(vm.SendSelectUser, vm.SendNewUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
SelectedUser = model.Username;
GetGameInformation();
}
else
{
vm.IsValid = false;

Dispatcher.UIThread.InvokeAsync(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(3));
SelectUser();
});
}
});

Content = vm;
}
public async Task CheckForUpdate()
{
VersionData version = await _gvs.CheckVersion();

if (version.isNewer)
{
Update(version);
}
else
{
SelectUser();
}
}

public void Update(VersionData version)
{
var vm = new UpdateAvailableViewModel(version);

Observable.Merge(vm.GoHome)
.Take(1)
.Subscribe(model =>
{
if (model == null)
{
SelectUser();
}
});

Content = vm;
}

public void SelectUser()
{
var vm = new SelectUserViewModel();

Observable.Merge(vm.SendSelectUser, vm.SendNewUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
SelectedUser = model.Username;
GetGameInformation();
}
else
{
vm.IsValid = false;

Dispatcher.UIThread.InvokeAsync(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(3));
SelectUser();
});
}
});

Content = vm;
}
this part of the main window view model
Mekasu0124
Mekasu0124OP14mo ago
since my database functions are no longer static, I've got to learn a new way of calling the database as in the SelectUserViewModel where I call for the list of usernames, it's not liking it
JakenVeina
JakenVeina14mo ago
create an instance
Mekasu0124
Mekasu0124OP14mo ago
it's telling me that for CurrentUsers = Database.GetUsernames(); I need an object reference
JakenVeina
JakenVeina14mo ago
right make one
Mekasu0124
Mekasu0124OP14mo ago
create an instance of database? do I do that in the constructor?
JakenVeina
JakenVeina14mo ago
or get one from somewhere else that would be the most sensible place either create it there, or inject it there
Mekasu0124
Mekasu0124OP14mo ago
ok so now question
Mekasu0124
Mekasu0124OP14mo ago
in my MWVM, I have a private instance of the database, and I have in the first function _db = db; but I never do anything with it. Can I use that? If yes, do I just need to pass it down through the functions parameters?
JakenVeina
JakenVeina14mo ago
yes
Mekasu0124
Mekasu0124OP14mo ago
ok cool
JakenVeina
JakenVeina14mo ago
general practice is to inject dependencies via constructor, but parameter injection is also valid
Mekasu0124
Mekasu0124OP14mo ago
ok if I go the constructor route, what would that look like? I'm familiar with using parameters. and I have come across another problem too
public SelectUserViewModel(Database db)
{
CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser(db),
newUserOk);
}

public User ReturnSelectedUser()
{
return new User { Username = SelectedUsername };
}

public User ReturnNewUser(Database db)
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
public SelectUserViewModel(Database db)
{
CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser(db),
newUserOk);
}

public User ReturnSelectedUser()
{
return new User { Username = SelectedUsername };
}

public User ReturnNewUser(Database db)
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
passing database through the parameter works for the first function, but when I need to use it ReturnNewUser where it writes the user to the database, I can't access it
JakenVeina
JakenVeina14mo ago
public class SelectUserViewModel
{
public SelectUserViewModel(Database database)
=> _database = database;

public void DoSomething()
{
_database.DoSomething();
}

private readonly Database _database;
}

public class MainWindowViewModel
{
public MainWindowViewModel(Database database)
=> _database = database;

public void DoSomething()
{
new SelectUserViewModel(_database);
}

private readonly Database _database;
}
public class SelectUserViewModel
{
public SelectUserViewModel(Database database)
=> _database = database;

public void DoSomething()
{
_database.DoSomething();
}

private readonly Database _database;
}

public class MainWindowViewModel
{
public MainWindowViewModel(Database database)
=> _database = database;

public void DoSomething()
{
new SelectUserViewModel(_database);
}

private readonly Database _database;
}
Mekasu0124
Mekasu0124OP14mo ago
so I'm going to guess that going the constructor route will fix that? I have no idea how to put that into this
JakenVeina
JakenVeina14mo ago
what part is confusing?
Mekasu0124
Mekasu0124OP14mo ago
my public SelectUserViewModel is already a function that does stuff. If I change it to
public SelectUserViewModel(Database db) => _db = db;
public SelectUserViewModel(Database db) => _db = db;
and try to keep my code block attached to that, it's going error
JakenVeina
JakenVeina14mo ago
okay so why would you get rid of everything that's already there, for that constructor? do you not know what that lambda => syntax is
Mekasu0124
Mekasu0124OP14mo ago
I don't? or at least I don't think I do. I didn't even use them in Python if I didn't have too because they were too confusing.
JakenVeina
JakenVeina14mo ago
the lambda can actually be used for a handful of different things in C# in this case it's literally just shorthand
public SelectUserViewModel(Database database)
=> _database = database;
public SelectUserViewModel(Database database)
=> _database = database;
is semantically identical to
public SelectUserViewModel(Database database)
{
_database = database;
}
public SelectUserViewModel(Database database)
{
_database = database;
}
"lambda-bodied method"
Mekasu0124
Mekasu0124OP14mo ago
public SelectUserViewModel(Database db)
{
CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser(db),
newUserOk);
}
public SelectUserViewModel(Database db)
{
CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser(db),
newUserOk);
}
ok but I have code that goes with that function, so how do I incorporate that into this. Trying to pass db to return new user through the reactive command is my current problem that I'm trying to break down
JakenVeina
JakenVeina14mo ago
I mean, that works
Mekasu0124
Mekasu0124OP14mo ago
it says that I can't convert MeksMathGame.Models.User to System.Action so that's what I'm trying to break down to figure out and solve
JakenVeina
JakenVeina14mo ago
oh, right you could do one of two things
Mekasu0124
Mekasu0124OP14mo ago
public ReactiveCommand<Unit, User> SendNewUser { get; } this is the reactive command if that helps
JakenVeina
JakenVeina14mo ago
SendNewUser = ReactiveCommand.Create(
() => ReturnNewUser(db),
newUserOk);
SendNewUser = ReactiveCommand.Create(
() => ReturnNewUser(db),
newUserOk);
this use of a lambda is an "anonymous delegate" the compiler translates this into a method on the class
Mekasu0124
Mekasu0124OP14mo ago
wow that simple lol
JakenVeina
JakenVeina14mo ago
public void CompilerGeneratedMethod()
{
ReturnNewUser(db);
}
public void CompilerGeneratedMethod()
{
ReturnNewUser(db);
}
actually, in this case it translates that entire lambda into its own class because it has to "capture" that db value something like
public class GeneratedAnonymousClass
{
public GeneratedAnonymousClass(
SelectUserViewModel @this,
Database db)
{
_this = @this;
_db = db;
}

public void GeneratedAnonymousMethod()
{
_this.ReturnNewUser(_db);
}

private readonly SelectUserViewModel _this;
private readonly Database _db;
}

...

var closure = new GeneratedAnonymousClass(this, db);

SendNewUser = ReactiveCommand.Create(
closure.GeneratedAnonymousMethod,
newUserOk);
public class GeneratedAnonymousClass
{
public GeneratedAnonymousClass(
SelectUserViewModel @this,
Database db)
{
_this = @this;
_db = db;
}

public void GeneratedAnonymousMethod()
{
_this.ReturnNewUser(_db);
}

private readonly SelectUserViewModel _this;
private readonly Database _db;
}

...

var closure = new GeneratedAnonymousClass(this, db);

SendNewUser = ReactiveCommand.Create(
closure.GeneratedAnonymousMethod,
newUserOk);
Mekasu0124
Mekasu0124OP14mo ago
that's complicated lol
JakenVeina
JakenVeina14mo ago
little bit but it should look familiar
Mekasu0124
Mekasu0124OP14mo ago
it doesn't to me. I don't know like the @this part. Like I've never dove that deep into the semantics of a language
JakenVeina
JakenVeina14mo ago
the @ allows me to use reserved keywords for variable/parameter/whatever names this is a language keyword
Mekasu0124
Mekasu0124OP14mo ago
https://pastebin.com/dVQUQ1cc offset question, how does the axaml file for this screen look
JakenVeina
JakenVeina14mo ago
I called the parameter @this because the value I'm going to pass for that parameter is this
Mekasu0124
Mekasu0124OP14mo ago
just like I use this.RaiseAndSetIfChanged();
JakenVeina
JakenVeina14mo ago
fine, I guess? right
Mekasu0124
Mekasu0124OP14mo ago
I just wanted to ensure that I couldn't do something here that would be more efficient or like make sure I wasn't missing something with a binding that the view should be doing and the logic not be doing or something
JakenVeina
JakenVeina14mo ago
this is the keyword that refers to "the current instance of a class, upon which a method is executing" there is you want to do what the closure does but better
Mekasu0124
Mekasu0124OP14mo ago
that makes sense
JakenVeina
JakenVeina14mo ago
again, let's work the problem you have a ReturnNewUser() method it requires a Database instance to make calls on
Mekasu0124
Mekasu0124OP14mo ago
correct
JakenVeina
JakenVeina14mo ago
it also can't have parameters, otherwise it isn't compatible with ReactiveCommand.Create()
Mekasu0124
Mekasu0124OP14mo ago
I was told that the view model should handle writing new users to the database instead of the main window view model as it's specific to that screen and that the main window view model should only handle navigation
JakenVeina
JakenVeina14mo ago
that sounds fair to me
Mekasu0124
Mekasu0124OP14mo ago
oh I'm in left field. never mind
JakenVeina
JakenVeina14mo ago
so
public User ReturnNewUser()
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
public User ReturnNewUser()
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
where do we get db from?
Mekasu0124
Mekasu0124OP14mo ago
it's through the parameter which we figured out here
JakenVeina
JakenVeina14mo ago
no we said we can't use the parameter
Mekasu0124
Mekasu0124OP14mo ago
public User ReturnNewUser(Database db)
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
public User ReturnNewUser(Database db)
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

db.InsertNewUsername(user);

return user;
}
JakenVeina
JakenVeina14mo ago
at least, we shouldn't
Mekasu0124
Mekasu0124OP14mo ago
oh
JakenVeina
JakenVeina14mo ago
I took it out for a reason cause it makes the method incompatible with ReactiveCommand.Create()
Mekasu0124
Mekasu0124OP14mo ago
right you did say that
JakenVeina
JakenVeina14mo ago
we have to get state into that method without a parameter how do we do that? hint: you're doing it already
Mekasu0124
Mekasu0124OP14mo ago
constructor?
JakenVeina
JakenVeina14mo ago
well, sort of indirectly
Mekasu0124
Mekasu0124OP14mo ago
ok if I'm doing it already, let me take a look
JakenVeina
JakenVeina14mo ago
the constructor initializes state upon the class instance and the method has access to the class instance
Mekasu0124
Mekasu0124OP14mo ago
ok I'm not seeing it
JakenVeina
JakenVeina14mo ago
within that method where does NewUser come from? it wasn't passed in as a parameter
Mekasu0124
Mekasu0124OP14mo ago
I know that I pass the database through the parameters of SelectUserViewModel and use it to call the usernames from the database NewUser is bound to a TextBox on the view screen that captures the new username that the user enters if their username doesn't already exist in CurrentUsers
JakenVeina
JakenVeina14mo ago
yes, but what is it? how can you access it within the method?
Mekasu0124
Mekasu0124OP14mo ago
a string instantiated within the constructor
JakenVeina
JakenVeina14mo ago
well, that might be its value it would be any number of strings "instantiated" by the view as well
Mekasu0124
Mekasu0124OP14mo ago
and then used later as
public string NewUser
{
get => _newUser;
set => this.RaiseAndSetIfChanged(ref _newUser, value);
}
public string NewUser
{
get => _newUser;
set => this.RaiseAndSetIfChanged(ref _newUser, value);
}
JakenVeina
JakenVeina14mo ago
what that IS is a property an instance property on the class backed up by _newUser which is an instance field on the class
Mekasu0124
Mekasu0124OP14mo ago
so do I need to do the same thing with database? something like
JakenVeina
JakenVeina14mo ago
something like exactly what I posted above, yes
public class SelectUserViewModel
{
public SelectUserViewModel(Database database)
{
_database = database;
}

public void DoSomething()
{
_database.DoSomething();
}

private readonly Database _database;
}
public class SelectUserViewModel
{
public SelectUserViewModel(Database database)
{
_database = database;
}

public void DoSomething()
{
_database.DoSomething();
}

private readonly Database _database;
}
Mekasu0124
Mekasu0124OP14mo ago
private Database _db;

public SelectUserViewModel(Database db)
{
Db = db;
}

private Database Db
{
get => _db;
set => this.RaiseAndSetIfChanged(ref _Db, value);
}
private Database _db;

public SelectUserViewModel(Database db)
{
Db = db;
}

private Database Db
{
get => _db;
set => this.RaiseAndSetIfChanged(ref _Db, value);
}
whelp
JakenVeina
JakenVeina14mo ago
skip the property and absolutely skip the RaiseAndSetIfChanged() call
Mekasu0124
Mekasu0124OP14mo ago
public class SelectUserViewModel : ViewModelBase
{
public List<string> _currentUsers;
public string _selectedUsername;
public string _newUser;
public bool _isValid = true;

private readonly Database _db;

public SelectUserViewModel(Database db)
{
_db = db;

CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser,
newUserOk);
}

public User ReturnSelectedUser()
{
return new User { Username = SelectedUsername };
}

public User ReturnNewUser()
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

_db.InsertNewUsername(user);

return user;
}
public class SelectUserViewModel : ViewModelBase
{
public List<string> _currentUsers;
public string _selectedUsername;
public string _newUser;
public bool _isValid = true;

private readonly Database _db;

public SelectUserViewModel(Database db)
{
_db = db;

CurrentUsers = db.GetUsernames();
SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User";

IObservable<bool> selectedUserOk = this.WhenAnyValue(
x => x.SelectedUsername,
x => x != "Select One");

IObservable<bool> newUserOk = this.WhenAnyValue(
x => x.NewUser,
x => !string.IsNullOrEmpty(x));

SendSelectUser = ReactiveCommand.Create(
ReturnSelectedUser,
selectedUserOk);

SendNewUser = ReactiveCommand.Create(
ReturnNewUser,
newUserOk);
}

public User ReturnSelectedUser()
{
return new User { Username = SelectedUsername };
}

public User ReturnNewUser()
{
if (CurrentUsers.Contains(NewUser)) return null;

User user = new() { Username = NewUser };

_db.InsertNewUsername(user);

return user;
}
like that!😄
JakenVeina
JakenVeina14mo ago
like that
Mekasu0124
Mekasu0124OP14mo ago
I had to think about that one for a second lol ok great. Do I have any other issues that I'm not immediately seeing?
JakenVeina
JakenVeina14mo ago
¯\_(ツ)_/¯ well make all those fields private
Mekasu0124
Mekasu0124OP14mo ago
I do haev one that I know about
JakenVeina
JakenVeina14mo ago
you should basically never have public fields
Mekasu0124
Mekasu0124OP14mo ago
ok I did that SelectedUsername = CurrentUsers.Count > 0 ? CurrentUsers[0] : "Create New User"; on this line. If there are no usernames in the database, then it's supposed to show that combobox's index[0] to be "Create New User" instead of "Select One". This corresponds to the database here
public List<string> GetUsernames()
{
using SQLiteConnection sqlite = new(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<string> usernames = new() { "Select One" };

sqlite.Open();

cmd.CommandText = "SELECT Username FROM user";

reader = cmd.ExecuteReader();

while (reader.Read())
{
User user = new()
{
Username = reader["Username"].ToString()
};

usernames.Add(user.Username);
}

return usernames;
}
public List<string> GetUsernames()
{
using SQLiteConnection sqlite = new(dbFile);
using SQLiteCommand cmd = sqlite.CreateCommand();
SQLiteDataReader reader;

List<string> usernames = new() { "Select One" };

sqlite.Open();

cmd.CommandText = "SELECT Username FROM user";

reader = cmd.ExecuteReader();

while (reader.Read())
{
User user = new()
{
Username = reader["Username"].ToString()
};

usernames.Add(user.Username);
}

return usernames;
}
however, regardless to the state of usernames or not existing, it still shows "Select One". I'm thinking that I'm indexing wrong, but I could be wrong on that
JakenVeina
JakenVeina14mo ago
yeah, I dunno someone was in here not too long ago with another issue related to Avalonia's ComboBox, and I realized Avalonia's ComboBox has a really weird API at least compared to WPF/UWP/etc.
Mekasu0124
Mekasu0124OP14mo ago
oh ok. So I'll just put a pin in that for now
Mekasu0124
Mekasu0124OP14mo ago
https://pastebin.com/L1qM8ARG so I know I have some errors already to figure out, but it's back to the fact that I stored the difficulty and game type as integers in the database. In the ViewModel where I need to use those values, how do I properly convert?
Mekasu0124
Mekasu0124OP14mo ago
and I'm at this part now too
JakenVeina
JakenVeina14mo ago
my vote is just make separate commands
public ICommand ShowPreviousGames { get; }

public ICommand GoBack { get; }
public ICommand ShowPreviousGames { get; }

public ICommand GoBack { get; }
Mekasu0124
Mekasu0124OP14mo ago
I did go back and change my fields to private
JakenVeina
JakenVeina14mo ago
and then instead of Game you'd have a PreviousGamesViewModel and... whatever GoBack should do
Mekasu0124
Mekasu0124OP14mo ago
public ReactiveCommand<GameTypes, Game> PreviousGames { get; }
public ReactiveCommand<GameTypes, Game> BackButton { get; }
public ReactiveCommand<GameTypes, Game> PreviousGames { get; }
public ReactiveCommand<GameTypes, Game> BackButton { get; }
so get rid of these
JakenVeina
JakenVeina14mo ago
uhhh no keep those just what does the GameTypes parameter accomplish? that should just be Unit
Mekasu0124
Mekasu0124OP14mo ago
ok so question. From what I was told, MWVM should handle the navigation, so would it be better to have those commands return a value back to the MWVM so that it can continue to handle navigation?
JakenVeina
JakenVeina14mo ago
y'know, I honestly have never build an MVVM app with page-based navigation
Mekasu0124
Mekasu0124OP14mo ago
nothing now. I've removed those and change to Unit's
JakenVeina
JakenVeina14mo ago
I think a lot of folks delegate to something like a NavigationService that's constructor injected so in your child VM, you'd do
_navigationService.NavigateTo(nextViewModel);
_navigationService.NavigateTo(nextViewModel);
and NavigationService would have
public IObservable<ViewModelBase> NavigationRequested { get; }
public IObservable<ViewModelBase> NavigationRequested { get; }
and MainWindowViewModel can subscribe to that
Mekasu0124
Mekasu0124OP14mo ago
I honestly don't know anything about that. I've been doing the navigation the only way anyone has taught me and that's through the MWVM with the Observable.Merge()'s
JakenVeina
JakenVeina14mo ago
me, I would probably just put that NavigationRequested event on the child VM itself and have MainWindowViewModel subscribe to it directly maybe your way with Observable.Merge() is also fine, really it all kinda amounts to the same thing
Mekasu0124
Mekasu0124OP14mo ago
if it's ok, I'd like to keep it that way. It's something that I understand. I would not mind to learn the other way though on the next application I do which will be a diary application
JakenVeina
JakenVeina14mo ago
MainWindowViewModel is responsible for displaying the current VM, and each current VM is responsible for signaling to the MainWindowViewModel when a navigation to a new VM is needed with the exception of a Back navigation
Mekasu0124
Mekasu0124OP14mo ago
right and I do that through returning models
JakenVeina
JakenVeina14mo ago
that should really be implemented with something like a stack in the main window
Mekasu0124
Mekasu0124OP14mo ago
and my version of a back button that I've been doing is just returning a null model
JakenVeina
JakenVeina14mo ago
that's not completely crazy either way, the main window is responsible for keeping track of what the preivous VMs were, so it can go back to them
Mekasu0124
Mekasu0124OP14mo ago
I'm not going to lie to you
public void Update(VersionData version, Database db)
{
var vm = new UpdateAvailableViewModel(version);

Observable.Merge(vm.GoHome)
.Take(1)
.Subscribe(model =>
{
if (model == null)
{
SelectUser(db);
}
});

Content = vm;
}
public void Update(VersionData version, Database db)
{
var vm = new UpdateAvailableViewModel(version);

Observable.Merge(vm.GoHome)
.Take(1)
.Subscribe(model =>
{
if (model == null)
{
SelectUser(db);
}
});

Content = vm;
}
taking the update screen for example. The user is presented with two buttons. Yes and No. If they click the No button, it returns null No = ReactiveCommand.Create(() => { }); and returns a null model. to which the if statement checks that it's null and if so, then I just call the function that holds the Content that the main window view model is looking for which is in that function via var vm = new ...();
JakenVeina
JakenVeina14mo ago
uuhhhhhm okay
Mekasu0124
Mekasu0124OP14mo ago
so like with this screen, the HomeScreenViewModel, i'd do my back button the same way
public HomeScreenViewModel()
{
BackButton = ReactiveCommand.Create(() => { });
}
public ReactiveCommand<Unit, Unit> BackButton { get; }
public HomeScreenViewModel()
{
BackButton = ReactiveCommand.Create(() => { });
}
public ReactiveCommand<Unit, Unit> BackButton { get; }
so now that leaves my previous games button
JakenVeina
JakenVeina14mo ago
ahhh, okay, so MainWindowViewModel is subscribing to BackButton?
Mekasu0124
Mekasu0124OP14mo ago
correct
JakenVeina
JakenVeina14mo ago
sounds good I would say that PreviousGames needs to be its own View and ViewModel, separate from Game cause it's not a game it's a totally different page yes? or am I not understanding what that is?
Mekasu0124
Mekasu0124OP14mo ago
it is. I just haven't built that screen yet.
JakenVeina
JakenVeina14mo ago
okay last I saw, you were returning a Game for that so, beyond that, I dunno what you mean by "now that leaves my previous games button"
Mekasu0124
Mekasu0124OP14mo ago
I was, however, since Previous Games isn't in the GameTypes, I can't use that anymore as GameTypes is no long a string
JakenVeina
JakenVeina14mo ago
yup
Mekasu0124
Mekasu0124OP14mo ago
ok let me see if I can explain what I'm after better. one sec
public void GetGameInformation(Database db)
{
// MainWindowView shows this through the {Binding Content} in the axaml page
var vm = new HomeScreenViewModel();
// subscribe to the buttons in the HomeScreenViewModel
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
// if the model is not null, meaning the back button wasn't pressed
if (model != null)
{
// we check to see what button was clicked. Was it a game button or the previous games button
// if it was the previous games button, then we call the function that changes MWVM's Content to show that ViewModel
// so the HomeScreenViewModel has to return something inside of a model in order for this check to work
if (model.GameType == GameTypes.PreviousQuestions)
{
// ShowPreviousGames(db);
}
// if a game button was pressed, build what's needed for the GameEngineViewModel
// pass it to that view model and show that page for the Content
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
// if the back button was pressed, then the model is null and the SelectUser screen shows again
else
{
SelectUser(db);
}
});

Content = vm;
}
public void GetGameInformation(Database db)
{
// MainWindowView shows this through the {Binding Content} in the axaml page
var vm = new HomeScreenViewModel();
// subscribe to the buttons in the HomeScreenViewModel
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
// if the model is not null, meaning the back button wasn't pressed
if (model != null)
{
// we check to see what button was clicked. Was it a game button or the previous games button
// if it was the previous games button, then we call the function that changes MWVM's Content to show that ViewModel
// so the HomeScreenViewModel has to return something inside of a model in order for this check to work
if (model.GameType == GameTypes.PreviousQuestions)
{
// ShowPreviousGames(db);
}
// if a game button was pressed, build what's needed for the GameEngineViewModel
// pass it to that view model and show that page for the Content
else
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}
}
// if the back button was pressed, then the model is null and the SelectUser screen shows again
else
{
SelectUser(db);
}
});

Content = vm;
}
I have to figure out a way for the PreviousGames button to return some model that doesn't match GameTypes in order for the check to know that the user wants to view that page
JakenVeina
JakenVeina14mo ago
ah so vm.Play returns what?
Mekasu0124
Mekasu0124OP14mo ago
yay that made sense lol
JakenVeina
JakenVeina14mo ago
Game?
Mekasu0124
Mekasu0124OP14mo ago
public Game PlayGame(GameTypes gameType)
{
int? totalQues = Convert.ToInt32(MaxQues);

return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}
public Game PlayGame(GameTypes gameType)
{
int? totalQues = Convert.ToInt32(MaxQues);

return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}
JakenVeina
JakenVeina14mo ago
(really should be GameViewModel)
Mekasu0124
Mekasu0124OP14mo ago
game engine sounds cooler lol
JakenVeina
JakenVeina14mo ago
PreviousGames will return whatever you make for it PreviousGamesViewModel
Mekasu0124
Mekasu0124OP14mo ago
public Game ShowPreviousGames(GameTypes gameType)
{
return new Game { GameType = GameTypes.PreviousQuestions };
}
public Game ShowPreviousGames(GameTypes gameType)
{
return new Game { GameType = GameTypes.PreviousQuestions };
}
currently it's trying to return a game model, but it doesn't need to. I don't know how to return a model that I don't have a model for though
JakenVeina
JakenVeina14mo ago
so, that means that Observable.Merge<T>(vm.Play, vm.PreviousGames) should return IObservable<object> yes?
Mekasu0124
Mekasu0124OP14mo ago
but if I'm understanding my code correctly, it has to return a game model so that the model in the mainwindowviewmodel know's how to check it, right?
JakenVeina
JakenVeina14mo ago
it has to return a game model
why?
Mekasu0124
Mekasu0124OP14mo ago
I'm honestly not sure. I've either been returning the model that is needed on that screen, or returning a null model like I do with the back button
JakenVeina
JakenVeina14mo ago
so that the model in the mainwindowviewmodel know's how to check it
only because you've written it that way step back again it should return what it should return
Mekasu0124
Mekasu0124OP14mo ago
I'm sure that the model that is subscribed in the mwvm can be any type of model
JakenVeina
JakenVeina14mo ago
forget what the upstream is, we'll figure that out
public PreviousGamesViewModel ShowPreviousGames()
{
return new PreviousGamesViewModel(...);
}
public PreviousGamesViewModel ShowPreviousGames()
{
return new PreviousGamesViewModel(...);
}
Mekasu0124
Mekasu0124OP14mo ago
I didn't know I could return a view model like that however
JakenVeina
JakenVeina14mo ago
why not? it's a method make it return whatever you want
Mekasu0124
Mekasu0124OP14mo ago
with this setup, how would I check if model == to a view model?
JakenVeina
JakenVeina14mo ago
IObservable<object> currentModel = Observable.Merge(vm.Play, vm.PreviousQuestions);
IObservable<object> currentModel = Observable.Merge(vm.Play, vm.PreviousQuestions);
Game and PreviousGamesViewModel don't have any common ancestor at least other than object so, Observable.Merge() should resolve to return IObservable<object>
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
// if the model is not null, meaning the back button wasn't pressed
if (model != null)
{
}
else
{
SelectUser(db);
}
});
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
// if the model is not null, meaning the back button wasn't pressed
if (model != null)
{
}
else
{
SelectUser(db);
}
});
all this means is that model here is now just object so you have to check it to see what it is so, better yet
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
switch(model)
{
case Game game:
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
break;

case PreviousGames:
ShowPreviousGames(db);
break;

// Null means "go back"
default:
SelectUser(db);
break;
}
});
Observable.Merge(vm.Play, vm.PreviousGames)
.Take(1)
.Subscribe(model =>
{
switch(model)
{
case Game game:
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
break;

case PreviousGames:
ShowPreviousGames(db);
break;

// Null means "go back"
default:
SelectUser(db);
break;
}
});
Mekasu0124
Mekasu0124OP14mo ago
so I'm waiting to see what you show, but in the interum, I'm trying to return the view model, but it's not liking it
No description
JakenVeina
JakenVeina14mo ago
well, yeah you defined the PreviousGames command to return Unit if there's no actual data you need to return then you should probably keep that and you can do....
Observable.Merge(
vm.Play
Do(model =>
{
if (model is not null)
{
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
}
// Null means "go back"
else
{
SelectUser(db);
}
}),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();
Observable.Merge(
vm.Play
Do(model =>
{
if (model is not null)
{
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
}
// Null means "go back"
else
{
SelectUser(db);
}
}),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();
Mekasu0124
Mekasu0124OP14mo ago
I like this better so I changed it to this. great idea btw
JakenVeina
JakenVeina14mo ago
then you could break out GoBack as well
Observable.Merge(
vm.Play
.Do(model =>
{
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
}),
vm.GoBack
.Do(_ => SelectUser(db)),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();
Observable.Merge(
vm.Play
.Do(model =>
{
// Do whatever you need to do to fire up a GameEngine
StartGameEngine(...);
}),
vm.GoBack
.Do(_ => SelectUser(db)),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();
Mekasu0124
Mekasu0124OP14mo ago
I like that idea too. So I'm still hitting a wall with getting this right in the HSVM.
No description
JakenVeina
JakenVeina14mo ago
what is the GameTypes parameter for, in ShowPreviousGames? also, why is that returning PreviousGamesViewModel?
Mekasu0124
Mekasu0124OP14mo ago
oh I Meant to remove that
JakenVeina
JakenVeina14mo ago
what data does HSVM need to return to MWVM in order for MWVM to load up a "Previous Games" screen
Mekasu0124
Mekasu0124OP14mo ago
I'm guessing with the new Do methods on the subscribe, it doesn't need to anymore so I can have it return a null model as well, yea?
JakenVeina
JakenVeina14mo ago
Unit yeah I suggested returning PreviousGamesViewModel based on you returning Game from the other ones thinking Game was a View or ViewModel object if it's just a plain data container telling MWVM which game to play, then by extension PreviousGames doesn't need that there's no data it needs to pass up to MWVM only the fact that the PreviousGames button was click I.E. all it needs is Unit
Mekasu0124
Mekasu0124OP14mo ago
so I'm guessing the two buttons on the view itself, Previous Games and Back Button, no longer need COmmandParameter either
JakenVeina
JakenVeina14mo ago
yup
Mekasu0124
Mekasu0124OP14mo ago
ok bet 😄
JakenVeina
JakenVeina14mo ago
bed time
Mekasu0124
Mekasu0124OP14mo ago
ok have a good night. I'm going to put this up as a pin for where to start again when we both come back. I'm heading to bed myself
Mekasu0124
Mekasu0124OP14mo ago
so when I removed the ?'s from the items inside the Game model, it threw this massive error on the Do operation. The reason I removed them is because it said it couldn't convert int? to int so I removed the ? from the items in the Game model, and it threw that error. I also tried only removing the ? from the game model's total questions, and it threw the same error so starting point for when we get back again. Have a good night
No description
No description
Mekasu0124
Mekasu0124OP14mo ago
ok so right now I'm lost on how to get this fixed. I've tried applying a direct cast SelectedTotalQuestion = (int)model.TotalQuestions and that didn't work. I've tried going through each file that uses the TotalQuestions from the Game model and removing all the ?'s and that didn't work so I'm lost
JakenVeina
JakenVeina14mo ago
get what fixed?
Mekasu0124
Mekasu0124OP14mo ago
with the changes we've made so far, in the MainWindowViewModel on this portion
public void GetGameInformation(Database db)
{
var vm = new HomeScreenViewModel();

Observable.Merge(
vm.Play
.Do(model =>
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}),
vm.BackButton
.Do(_ => SelectUser(db)),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();

Content = vm;
}
public void GetGameInformation(Database db)
{
var vm = new HomeScreenViewModel();

Observable.Merge(
vm.Play
.Do(model =>
{
SelectedTotalQuestions = model.TotalQuestions;
SelectedGameType = model.GameType;
SelectedDifficulty = model.Difficulty;

Game game = new()
{
Username = SelectedUser,
Date = DateTimeOffset.Now.ToString("MM/dd/yyyy"),
TotalQuestions = SelectedTotalQuestions,
Difficulty = SelectedDifficulty,
GameType = SelectedGameType
};

StartGameEngine(game);
}),
vm.BackButton
.Do(_ => SelectUser(db)),
vm.PreviousGames
.Do(_ => ShowPreviousGames(db)))
.Take(1)
.Subscribe();

Content = vm;
}
on line SelectedTotalQuestions = model.TotalQuestions I get the error Cannot implicitly convert type 'int?' to 'int'. An explicit conversion exists (are you missing a cast?) so I thought, let me try doign SelectedTotalQuestions = (int)model.TotalQuestions, but when I do that, I get the error Argument 1: cannot convert from 'System.IObservable<MeksMathGame.Models.Game>' to 'System.Reactive.COncurrency.IScheduler' and so I removed the cast, and went to the game's model and removed the ? from
public class Game
{
public int? Id { get; set; }
public string? Username { get; set; }
public string? Date { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? Score { get; set; }
- public int? TotalQuestions { get; set; }
+ public int TotalQuestions { get; set; }
public Difficulty Difficulty { get; set; }
public GameTypes GameType { get; set; }
}
public class Game
{
public int? Id { get; set; }
public string? Username { get; set; }
public string? Date { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? Score { get; set; }
- public int? TotalQuestions { get; set; }
+ public int TotalQuestions { get; set; }
public Difficulty Difficulty { get; set; }
public GameTypes GameType { get; set; }
}
but int doing that, I get the same error Argument 1: cannot convert from 'System.IObservable<MeksMathGame.Models.Game>' to 'System.Reactive.COncurrency.IScheduler' again. So I thought, well let me go through all of the files that use the Game model and remove the ? from all the TotalQuestion conversions, and that and it resulted in the same error again. So I'm lost at this point on trying to fix this issue
JakenVeina
JakenVeina14mo ago
what is Game?
Mekasu0124
Mekasu0124OP14mo ago
a model
JakenVeina
JakenVeina14mo ago
for what?
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
I definitely don't need a screenshot of the exact code you just posted
Mekasu0124
Mekasu0124OP14mo ago
I use it to create the game models that when games are finished, or like in the instance of the HomeScreenViewModel where I have the function
public Game PlayGame(GameTypes gameType)
{
int totalQues = Convert.ToInt32(MaxQues);
return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}
public Game PlayGame(GameTypes gameType)
{
int totalQues = Convert.ToInt32(MaxQues);
return new Game
{
TotalQuestions = totalQues,
Difficulty = SelectedDiff,
GameType = gameType
};
}
it creates a new game model to send back to the MainWindowViewModel to pass over to the game engine so that the game engine has access to that information as it's already been selected and doesn't have to be selected again I also use them for data validation as well
JakenVeina
JakenVeina14mo ago
so, it represents a completed game? or a game that may or may not be completed?
Mekasu0124
Mekasu0124OP14mo ago
yes when a game is completed, but it's built throughout the process from selecting the difficulty, number of questions to answer, and the game type selected to pass to the game engine
JakenVeina
JakenVeina14mo ago
so the second one "yes when a game is completed" except no so, does it make sense for a game, completed or otherwise, to not have an associated number of questions?
Mekasu0124
Mekasu0124OP14mo ago
well, to me, it's a yes/no. It's started as an empty object. Then when the user is on the home screen, part of it's built: TotalQuestions, Difficulty, GameType and then that is pushed to the game engine where the user plays the game, and when the game is over, the rest is built afterwards
JakenVeina
JakenVeina14mo ago
well either way is valid pick one
Mekasu0124
Mekasu0124OP14mo ago
well the TotalQuestions is there for 2 reasons. 1) it's so the program knows how many questions to answer and 2) when the user goes to preview their previous games, it's also displayed there so that they can see how many questions they answered per game played. In the previous games played, it's also used to show the average score / total score possible this one sounds much better so I'll go with this one
JakenVeina
JakenVeina14mo ago
so, you want it to be nullable, or no?
Mekasu0124
Mekasu0124OP14mo ago
none of the information stored within the model is going to be null at all when it's passed to the database to be saved otherwise, I'm not entirely sure how to answer that question
JakenVeina
JakenVeina14mo ago
okay, reverse the question if you do allow for that value to be null, what does a null mean?
Mekasu0124
Mekasu0124OP14mo ago
there's nothing there.
JakenVeina
JakenVeina14mo ago
no
Mekasu0124
Mekasu0124OP14mo ago
it's not a string. it's not an int. it's nothing
JakenVeina
JakenVeina14mo ago
what semantic/business meaning are you giving to the value of null
Mekasu0124
Mekasu0124OP14mo ago
when I think null, I think empty. Like where it would usually expect a string, or an int, it gets nothing. Like telling someone you'll give them $5 but never do. It's nothing
JakenVeina
JakenVeina14mo ago
I'm not asking about null
Mekasu0124
Mekasu0124OP14mo ago
but I could very well be wrong on the definition of null
JakenVeina
JakenVeina14mo ago
I'm asking about TotalQuestions what does it mean for a game to have no "Total Questions" value
Mekasu0124
Mekasu0124OP14mo ago
it doens't. The screen where the user selected the difficulty, the number of questions to answer, and then clicks a button for the game type is setup that the game type buttons are not enabled unless both a difficulty and the number of questions to answer are selected so it's never null. it's never not answered like it's a requirement to be answered otherwise they cannot progress past that screen
JakenVeina
JakenVeina14mo ago
if there is no semantic reason for a game to not have a "Total Questions" value, then don't model it that way model your data to match your requirements
Mekasu0124
Mekasu0124OP14mo ago
so remove the ?
JakenVeina
JakenVeina14mo ago
yes
Mekasu0124
Mekasu0124OP14mo ago
ok I did that, but I'm still getting the error of game model versus IScheduler, to which I know you're aware
JakenVeina
JakenVeina14mo ago
Score for example, has a meaning for null it means "the game hasn't completed yet"
Mekasu0124
Mekasu0124OP14mo ago
I honestly can remove all the ?'s from the game model altogether. they don't need to be nullable
JakenVeina
JakenVeina14mo ago
although, you could also model that with an IsCompleted flag, and keep Score as just 0 initially if that's what makes sense for the purpose of the model if all of those values are semantically required for the thing that the model is modeling
Mekasu0124
Mekasu0124OP14mo ago
I do that with score in the game engine
No description
JakenVeina
JakenVeina14mo ago
okay so, this specifically is meant to model a completed game I would recommend renaming it CompletedGame for clarity
Mekasu0124
Mekasu0124OP14mo ago
ok I did that and changed it in all files too so now that we got the CompletedGame model situated, how can I go about figuring out how to solve this error?
JakenVeina
JakenVeina14mo ago
which one?
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
don't pass a value of type IObservable<CompletedGame> to something that requires a value of type IScheduler
Mekasu0124
Mekasu0124OP14mo ago
so then I have something wrong here and I don't know what
No description
JakenVeina
JakenVeina14mo ago
where?
Mekasu0124
Mekasu0124OP14mo ago
I'm assuming it's off the vm.Play button. That's the only part of the GetGameInformation function in the MWVM that's throwing an error like that
JakenVeina
JakenVeina14mo ago
stop working around the problem work the problem
don't pass a value of type IObservable<CompletedGame> to something that requires a value of type IScheduler
what there requires a value of type IScheduler?
Mekasu0124
Mekasu0124OP14mo ago
I literally have no idea
JakenVeina
JakenVeina14mo ago
great let's find out what's highlighted?
Mekasu0124
Mekasu0124OP14mo ago
the entire vm.Play portion
JakenVeina
JakenVeina14mo ago
so, that error applies to that entire expression
Mekasu0124
Mekasu0124OP14mo ago
yes
JakenVeina
JakenVeina14mo ago
what type is that expression?
Mekasu0124
Mekasu0124OP14mo ago
vm.Play? it's a reactive command
JakenVeina
JakenVeina14mo ago
the expression that's highlighted
Mekasu0124
Mekasu0124OP14mo ago
Observable.Merge()
JakenVeina
JakenVeina14mo ago
that's a method and also not highlighted
Mekasu0124
Mekasu0124OP14mo ago
I don't know
JakenVeina
JakenVeina14mo ago
find out
Mekasu0124
Mekasu0124OP14mo ago
sure. let me be scooby doo for a second vm.Play is a reactive command
JakenVeina
JakenVeina14mo ago
okay
Mekasu0124
Mekasu0124OP14mo ago
.Do is an IObservable<CompletedGame> IObservable<CompletedGame>.Do<CompletedGame>(Action<CompletedGame> onNext)
JakenVeina
JakenVeina14mo ago
true statement
Mekasu0124
Mekasu0124OP14mo ago
ok great I assume I still haven't answered the question
JakenVeina
JakenVeina14mo ago
I mean you kinda have .Do() is the last piece of that expression, right?
Mekasu0124
Mekasu0124OP14mo ago
yes
JakenVeina
JakenVeina14mo ago
so, the expression returns IObservable<CompletedGame> referring to the error, that's what the compiler can't convert to IScheduler so, what are we passing that expression to?
Mekasu0124
Mekasu0124OP14mo ago
the play button
JakenVeina
JakenVeina14mo ago
no
Mekasu0124
Mekasu0124OP14mo ago
the .Do expression is attach to vm.Play
JakenVeina
JakenVeina14mo ago
yes and the whole expression that's highlighted is being passed to what?
Mekasu0124
Mekasu0124OP14mo ago
Obervable.Merge
JakenVeina
JakenVeina14mo ago
what signature of Observable.Merge() is the compiler binding to?
Mekasu0124
Mekasu0124OP14mo ago
IObservable
JakenVeina
JakenVeina14mo ago
that's a type not a method signature
Mekasu0124
Mekasu0124OP14mo ago
The Merge then? I don't know
JakenVeina
JakenVeina14mo ago
Observable.Merge is a method what is its signature? specifically, which of its many signatures is the compiler binding to?
Mekasu0124
Mekasu0124OP14mo ago
i don't know
JakenVeina
JakenVeina14mo ago
what is a method signature?
Mekasu0124
Mekasu0124OP14mo ago
public
JakenVeina
JakenVeina14mo ago
no that is an access modifier a method signature consists of its name and parameter types those are the things that the compiler uses to determine which method to bind a method invocation to Observable.Merge() has many signatures
Mekasu0124
Mekasu0124OP14mo ago
makes sense
JakenVeina
JakenVeina14mo ago
Merge<TSource>(this IObservable<IObservable<TSource>> sources)
Merge<TSource>(this IObservable<Task<TSource>> sources)
Merge<TSource>(this IObservable<IObservable<TSource>> sources, int maxConcurrent)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, int maxConcurrent)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, int maxConcurrent, IScheduler scheduler)
Merge<TSource>(this IObservable<TSource> first, IObservable<TSource> second)
Merge<TSource>(this IObservable<TSource> first, IObservable<TSource> second, IScheduler scheduler)
Merge<TSource>(params IObservable<TSource>[] sources)
Merge<TSource>(IScheduler scheduler, params IObservable<TSource>[] sources)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, IScheduler scheduler)
Merge<TSource>(this IObservable<IObservable<TSource>> sources)
Merge<TSource>(this IObservable<Task<TSource>> sources)
Merge<TSource>(this IObservable<IObservable<TSource>> sources, int maxConcurrent)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, int maxConcurrent)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, int maxConcurrent, IScheduler scheduler)
Merge<TSource>(this IObservable<TSource> first, IObservable<TSource> second)
Merge<TSource>(this IObservable<TSource> first, IObservable<TSource> second, IScheduler scheduler)
Merge<TSource>(params IObservable<TSource>[] sources)
Merge<TSource>(IScheduler scheduler, params IObservable<TSource>[] sources)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources)
Merge<TSource>(this IEnumerable<IObservable<TSource>> sources, IScheduler scheduler)
the easiest way to tell what the compiler is thinking with regard to method binding (usually) is to hover over the method call it should tell you which method signature it's currently trying to bind to sometimes, this doesn't work, when an error is present, but it's the first thing you should check but in this case, it's rather obvious what's happening, now that we look at Observable.Merge
Mekasu0124
Mekasu0124OP14mo ago
No description
JakenVeina
JakenVeina14mo ago
in that case, it's actually clueing you into the fact that the compiler can't find a good one so it's not specifying a specific match it says "+ 11 overloads" it doesn't know which one to pick at least, Intellisense doesn't the compiler seems to be picking the one that takes an IScheduler as the first argument so which one do we WANT it to be picking?
Mekasu0124
Mekasu0124OP14mo ago
IObservable
JakenVeina
JakenVeina14mo ago
IObservable is a type not a method signature
Mekasu0124
Mekasu0124OP14mo ago
I don't know then
JakenVeina
JakenVeina14mo ago
well what are we trying to do with Observable.Merge()?
Mekasu0124
Mekasu0124OP14mo ago
use vm.play
JakenVeina
JakenVeina14mo ago
uhhh alright I'll bite use it how?
Mekasu0124
Mekasu0124OP14mo ago
I don't know. I'm just tired of saying that I don't know. so I gave a guess
JakenVeina
JakenVeina14mo ago
don't guess not knowing something is a solvable problem specify what you don't know so, you don't know why you want to call Observable.Merge()?
Mekasu0124
Mekasu0124OP14mo ago
idk about you, but I use it so that when the button is clicked on the corresponding vm, the mwvm knows what to do with that button clicked
JakenVeina
JakenVeina14mo ago
right, but the command does that the one that's bound to the button because you've setup each command with an empty action, (() => { }), each one just emits the moment it's executed so, subscribing to each command tells you when that button is clicked what do we need Observable.Merge() for?
Mekasu0124
Mekasu0124OP14mo ago
to merge the information or something from the subscribed vm back to the mwvm?
JakenVeina
JakenVeina14mo ago
what information?
Mekasu0124
Mekasu0124OP14mo ago
that comes from the button being clicked
JakenVeina
JakenVeina14mo ago
specifically multiple buttons we want Observable.Merge() because we have 3 different commands we're trying to listen to and more specifically, because it makes it easy to manage subscriptions
Mekasu0124
Mekasu0124OP14mo ago
right
JakenVeina
JakenVeina14mo ago
I mentioned, we don't need Observable.Merge() to subscribe to a command we could do that with...
vm.Play
.Take(1)
.Subscribe(...);

vm.PreviousGames
.Take(1)
.Subscribe(...);

vm.GoBack
.Take(1)
.Subscribe(...);
vm.Play
.Take(1)
.Subscribe(...);

vm.PreviousGames
.Take(1)
.Subscribe(...);

vm.GoBack
.Take(1)
.Subscribe(...);
but that leaves dangling subscriptions when play is clicked the other two listeners are still active that's memory that can potentially leak or even a bug that could manifest later, if that VM is reused
Mekasu0124
Mekasu0124OP14mo ago
understandable and we don't want that
JakenVeina
JakenVeina14mo ago
by merging them together, and then putting Take(1) on the merged stream, it means that everything gets torn down as soon as any of those commands emits so what we're trying to do with Observable.Merge() is merge together 3 different streams
Mekasu0124
Mekasu0124OP14mo ago
because it takes the first item clicked
JakenVeina
JakenVeina14mo ago
each one representing a button click and then tears down all the subscriptions Take(1) tears down the Merge() subscription after the first emission and Merge() tears down all of the merged subscriptions when the downstream tears down so we're trying to use Observable.Merge() to merge 3 observables which signature of Observable.Merge() do we want to call
Mekasu0124
Mekasu0124OP14mo ago
IObservable
JakenVeina
JakenVeina14mo ago
once again IObservable is a type not a method signature
Mekasu0124
Mekasu0124OP14mo ago
then I don't know. subscribe?
JakenVeina
JakenVeina14mo ago
subscribe is..... no why don't you know? (not being critical, legit question) which piece of the question is the part you don't understand?
which signature of Observable.Merge() do we want to call
Mekasu0124
Mekasu0124OP14mo ago
you said this
JakenVeina
JakenVeina14mo ago
indeed
Mekasu0124
Mekasu0124OP14mo ago
with this
JakenVeina
JakenVeina14mo ago
indeed
Mekasu0124
Mekasu0124OP14mo ago
so I said this and I'm still wrong. so therefore I don't know
JakenVeina
JakenVeina14mo ago
IObservable is literally present in every single one of those signatures
we're trying to use Observable.Merge() to merge 3 observables
which one of those overloads can we pass 3 observables to?
Mekasu0124
Mekasu0124OP14mo ago
idk which one is it?
JakenVeina
JakenVeina14mo ago
Merge<TSource>(this IObservable<IObservable<TSource>> sources)
Merge<TSource>(this IObservable<IObservable<TSource>> sources)
does this let me pass 3 observables to it?
Mekasu0124
Mekasu0124OP14mo ago
please do not ask me another question just so that I have to keep saying I don't know. I do not know that's why I didn't give an answer and stated idk which one is it?
JakenVeina
JakenVeina14mo ago
okay
Mekasu0124
Mekasu0124OP13mo ago
so which one is it expecting to be? or needs to be since the program can't figure it out for itself Hey. If you have a moment, I would like to apologize. I was not very open minded and was being stubborn with the code which is something I am heavily working on and I'm sorry. You were only trying to help, and I should have been more understand of that. I do apologize, and hope there are no hard feelings?
JakenVeina
JakenVeina13mo ago
A genuine apology is a hell of a lot more than I usually get, so yes, no hard feelings. I don't know what else I could ask for. Thank you.
Mekasu0124
Mekasu0124OP13mo ago
you're welcome. I just know that when I get frustrated with code, I get stubborn and beyond pissed off with it. I am really trying to work on this. As part of that working on it thing, I've decided to take on a much smaller project to learn Avalonia with instead. I'm building an address book.
JakenVeina
JakenVeina13mo ago
that sounds great
Mekasu0124
Mekasu0124OP13mo ago
thank you ❤️ so I do have a question for you if that's alright
<Grid RowDefinitions="Auto, *">
<Grid ColumnDefinitions="*,*,*" Grid.Row="0">
<Button Classes="NavButton" Grid.Column="0" Content="New Contact" Command="{Binding NewContact}" />
<Button Classes="NavButton" Grid.Column="1" Content="Edit Contact" />
<Button Classes="NavButton" Grid.Column="2" Content="Share Contact" />
</Grid>

<DataGrid Grid.Row="1"
Name="ContactsGrid"
ItemsSource="{Binding People}"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
SelectionMode="Extended"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedContact}">
<DataGrid.Columns>
<DataGridTextColumn Header="Prefix" Binding="{Binding Prefix}" />
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" />
<DataGridTextColumn Header="Middle Name" Binding="{Binding MiddleName}" />
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" />
<DataGridTextColumn Header="Suffix" Binding="{Binding Suffix}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
<Grid RowDefinitions="Auto, *">
<Grid ColumnDefinitions="*,*,*" Grid.Row="0">
<Button Classes="NavButton" Grid.Column="0" Content="New Contact" Command="{Binding NewContact}" />
<Button Classes="NavButton" Grid.Column="1" Content="Edit Contact" />
<Button Classes="NavButton" Grid.Column="2" Content="Share Contact" />
</Grid>

<DataGrid Grid.Row="1"
Name="ContactsGrid"
ItemsSource="{Binding People}"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
SelectionMode="Extended"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedContact}">
<DataGrid.Columns>
<DataGridTextColumn Header="Prefix" Binding="{Binding Prefix}" />
<DataGridTextColumn Header="First Name" Binding="{Binding FirstName}" />
<DataGridTextColumn Header="Middle Name" Binding="{Binding MiddleName}" />
<DataGridTextColumn Header="Last Name" Binding="{Binding LastName}" />
<DataGridTextColumn Header="Suffix" Binding="{Binding Suffix}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
WIth this being an address book, my first go to with displaying all the contacts was with a DataGrid, however, I don't like this because I allow the user to store at least 15 things per contact and that'll become overloading to view in this manor. I'm wanting to make it like the mobile phones are done and change it to where it just displays the first and last name sorted by last name first, then first name included if no last name is given like
John Apple
Jane Doe
Jane
Jennifer Sandwich
John Apple
Jane Doe
Jane
Jennifer Sandwich
however, I don't know how to go about doing that.
public class HomeScreenViewModel : ViewModelBase
{
private Database _db;
List<ContactModel> _people;

public HomeScreenViewModel(Database db)
{
_db = db;
People = db.GetContacts();
NewContact = ReactiveCommand.Create(() => { });
}

public List<ContactModel> People
{
get => _people;
set => this.RaiseAndSetIfChanged(ref _people, value);
}

public ReactiveCommand<Unit, Unit> NewContact { get; }
}
public class HomeScreenViewModel : ViewModelBase
{
private Database _db;
List<ContactModel> _people;

public HomeScreenViewModel(Database db)
{
_db = db;
People = db.GetContacts();
NewContact = ReactiveCommand.Create(() => { });
}

public List<ContactModel> People
{
get => _people;
set => this.RaiseAndSetIfChanged(ref _people, value);
}

public ReactiveCommand<Unit, Unit> NewContact { get; }
}
this is all I have for my home screen view model at the moment. How would I go about achieving this?
JakenVeina
JakenVeina13mo ago
for starters, you definitely don't want this
public List<ContactModel> People
{
get => _people;
set => this.RaiseAndSetIfChanged(ref _people, value);
}
public List<ContactModel> People
{
get => _people;
set => this.RaiseAndSetIfChanged(ref _people, value);
}
you don't want to be swapping out the entire collection every time the set of contacts changes you want to either A) load the contacts up-front or B) publish changes to the collection as they occur, rather than have the View layer need to completely rebuilt itself each time either way, it's pointless to have the People property implement change notification, as the collection object itself should never change A)
public IReadOnlyList<ContactModel> People
=> _people;

private readonly IReadOnlyList<ContactModel> _people;
public IReadOnlyList<ContactModel> People
=> _people;

private readonly IReadOnlyList<ContactModel> _people;
or B)
public ReadOnlyObservableCollection<ContactModel> People
=> _people;

private readonly ReadOnlyObservableCollection<ContactModel> _people;
public ReadOnlyObservableCollection<ContactModel> People
=> _people;

private readonly ReadOnlyObservableCollection<ContactModel> _people;
change it to where it just displays the first and last name
either expose that as a property on a ViewModel for each contact, or implement an IValueConverter to format each ContactModel into a string display name, according to whatever rules you want me personally, I'd lean towards the latter deciding how data is displayed in text form is a View layer concern the ViewModel's concern is to model and present the data to be viewed the fact that you want to combine two properties of the underlying data (First Name and Last Name) and display them together is, as the wording suggests, a "display" concern
Mekasu0124
Mekasu0124OP13mo ago
Ok. I’ll give it my best shot later today when I get online. I may need help implementing that though. I’ve never done this before. Apologies ok so I made this change. I now have
public class HomeScreenViewModel : ViewModelBase
{
private Database _db;
private readonly ReadOnlyObservableCollection<ContactModel> _people;

public HomeScreenViewModel(Database db)
{
_db = db;
NewContact = ReactiveCommand.Create(() => { });
}

public ReadOnlyObservableCollection<ContactModel> People => _people;
public ReactiveCommand<Unit, Unit> NewContact { get; }
}
public class HomeScreenViewModel : ViewModelBase
{
private Database _db;
private readonly ReadOnlyObservableCollection<ContactModel> _people;

public HomeScreenViewModel(Database db)
{
_db = db;
NewContact = ReactiveCommand.Create(() => { });
}

public ReadOnlyObservableCollection<ContactModel> People => _people;
public ReactiveCommand<Unit, Unit> NewContact { get; }
}
public List<ContactModel> GetContacts()
{
using SQLiteConnection conn = new(_dbFileName);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT * FROM contacts";
reader = cmd.ExecuteReader();

List<ContactModel> contacts = new();

while (reader.Read())
{
ContactModel contact = new()
{
Prefix = reader["Prefix"].ToString(),
FirstName = reader["FirstName"].ToString(),
MiddleName = reader["MiddleName"].ToString(),
LastName = reader["LastName"].ToString(),
Suffix = reader["Suffix"].ToString()
};

contacts.Add(contact);
}

return contacts;
}public List<ContactModel> GetContacts()
{
using SQLiteConnection conn = new(_dbFileName);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT * FROM contacts";
reader = cmd.ExecuteReader();

List<ContactModel> contacts = new();

while (reader.Read())
{
ContactModel contact = new()
{
Prefix = reader["Prefix"].ToString(),
FirstName = reader["FirstName"].ToString(),
MiddleName = reader["MiddleName"].ToString(),
LastName = reader["LastName"].ToString(),
Suffix = reader["Suffix"].ToString()
};

contacts.Add(contact);
}

return contacts;
}
public List<ContactModel> GetContacts()
{
using SQLiteConnection conn = new(_dbFileName);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT * FROM contacts";
reader = cmd.ExecuteReader();

List<ContactModel> contacts = new();

while (reader.Read())
{
ContactModel contact = new()
{
Prefix = reader["Prefix"].ToString(),
FirstName = reader["FirstName"].ToString(),
MiddleName = reader["MiddleName"].ToString(),
LastName = reader["LastName"].ToString(),
Suffix = reader["Suffix"].ToString()
};

contacts.Add(contact);
}

return contacts;
}public List<ContactModel> GetContacts()
{
using SQLiteConnection conn = new(_dbFileName);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT * FROM contacts";
reader = cmd.ExecuteReader();

List<ContactModel> contacts = new();

while (reader.Read())
{
ContactModel contact = new()
{
Prefix = reader["Prefix"].ToString(),
FirstName = reader["FirstName"].ToString(),
MiddleName = reader["MiddleName"].ToString(),
LastName = reader["LastName"].ToString(),
Suffix = reader["Suffix"].ToString()
};

contacts.Add(contact);
}

return contacts;
}
so since the People variable was changed from a List<string> to a readonly ReadOnlyObservableList<ContactModel> does that mean I have to change the way that the contacts are pulled from the database and returned from the function call?
JakenVeina
JakenVeina13mo ago
possibly really, what you need to do is define how you want to abstract-ify this like, you need to solidify the whole concept of this UI at least for me, maybe you have it in your head already it looks like this UI is supposed to be displaying all of the contacts that exist how do you intend the "New Contact" operation to work, from a high-level? or maybe, a more core question, what do you intend to be the "source of truth" for this UI? I could foresee 3 answers A) The database this would mean that when you create a new contact, you send it down to the database, and then pull it back up, in some fashion with the limitations of SQL, it really rather means that every time you do some kind of update to the set of contacts, in the database, you re-query the whole set SQL databases don't really have a mechanism for having the database issue notifications up to the higher layer for a small enough dataset, there's really nothing terribly wrong with this it's obviously inefficient to re-query data that you know you don't need to, but it does tend to keep your application logic simpler B) The ViewModel as in when the ViewModel initializes, it loads all the contacts from the database, and then becomes the central point of responsibility for managing them if a new contact is created, or an existing contact is updated, that's tracked FIRST in the ViewModel's collection of contacts, and then replicating those changes down to the database is a side-effect and you trust yourself to implement everything correctly-enough to avoid the database and VM becoming out-of-sync C) A Service Layer This is basically the same as B) except you move the logic for loading, saving, querying, etc. all the contacts out into a separate shared "service", which could then be reused by multiple different Views and ViewModels what makes the most sense to you?
Mekasu0124
Mekasu0124OP13mo ago
I've updated the repo. https://github.com/mekasu0124/MeksPhoneBook What I ended up doing was using ItemsControl. With my FAQ Page, I did this: FAQView.axaml: https://pastebin.com/HgEeTuvQ
// FAQViewModel.cs
public class FAQViewModel : ViewModelBase
{
private const string jsonFile = "faqs.json";
private Dictionary<string, string> _faqQuestions;

public FAQViewModel()
{
if (File.Exists(jsonFile))
{
string jsonData = File.ReadAllText(jsonFile);
_faqQuestions = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonData);
}
else
{
_faqQuestions = new();
}

Back = ReactiveCommand.Create(() => { });
}

public Dictionary<string, string> FaqQuestions
{
get => _faqQuestions;
set => this.RaiseAndSetIfChanged(ref _faqQuestions, value);
}

public ReactiveCommand<Unit, Unit> Back { get; }
}
// FAQViewModel.cs
public class FAQViewModel : ViewModelBase
{
private const string jsonFile = "faqs.json";
private Dictionary<string, string> _faqQuestions;

public FAQViewModel()
{
if (File.Exists(jsonFile))
{
string jsonData = File.ReadAllText(jsonFile);
_faqQuestions = JsonSerializer.Deserialize<Dictionary<string, string>>(jsonData);
}
else
{
_faqQuestions = new();
}

Back = ReactiveCommand.Create(() => { });
}

public Dictionary<string, string> FaqQuestions
{
get => _faqQuestions;
set => this.RaiseAndSetIfChanged(ref _faqQuestions, value);
}

public ReactiveCommand<Unit, Unit> Back { get; }
}
JakenVeina
JakenVeina13mo ago
I mean, same deal don't do that expose IReadOnlyList<T> or ReadOnlyObservableCollection<T> or ObservableCollection<T> and do not implement INPC for the property, regardless if the collection cannot be loaded before or during initialization, use whichever of the two observable collections is appropriate otherwise, the collection does not ever need to change, and implementing change notification for it is pointless
Accord
Accord13mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Mekasu0124
Mekasu0124OP13mo ago
hey do you have a moment for me to ask a question?
JakenVeina
JakenVeina13mo ago
if you have a question, ask it
Mekasu0124
Mekasu0124OP13mo ago
database file: https://pastebin.com/GYqZ9UF8 new shift entry: https://pastebin.com/uE3CiXdM helper file: https://pastebin.com/2NPHJgGi
public static void ShowMainMenu()
{
string totalHours = "00h 00m 00s";
int totalMiles = 0;
double totalMaintenanceDollars = 0.00f;
double totalGasDollars = 0.00f;
double totalPay = 0.00f;

string menu = $"""
Total Pay Earned: ${totalPay}
Total Miles Driven: {totalMiles}
Total Hours Worked: {totalHours}
Total Maintenance Dollars: ${totalMaintenanceDollars}
Total Gas Dollars: ${totalGasDollars}
public static void ShowMainMenu()
{
string totalHours = "00h 00m 00s";
int totalMiles = 0;
double totalMaintenanceDollars = 0.00f;
double totalGasDollars = 0.00f;
double totalPay = 0.00f;

string menu = $"""
Total Pay Earned: ${totalPay}
Total Miles Driven: {totalMiles}
Total Hours Worked: {totalHours}
Total Maintenance Dollars: ${totalMaintenanceDollars}
Total Gas Dollars: ${totalGasDollars}
on the inital screen that loads for the user, I'm wanting to display totals. Right now I'm trying to work out getting the total hours worked from the database. Each new shift entry auto tallies and stores the total time for each shift logged. What I'm trying to achieve is pulling those total times from the database, and adding them up together to show on the initial screen. Right now I have
// database file
internal List<ShiftModel> GetShiftTotals()
{
using SQLiteConnection conn = new(_dbFilePath);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT TotalTime, TotalMileage, Pay FROM records";
reader = cmd.ExecuteReader();

List<ShiftModel> shiftTotals = new();

while (reader.Read())
{
shiftTotals.Add(new ShiftModel()
{
TotalTime = reader["TotalTime"].ToString(),
TotalMileage = Convert.ToInt32(reader["TotalMileage"].ToString()),
Pay = Convert.ToDouble(reader["Pay"].ToString())
});
}

return shiftTotals;
}

// helper file with method to add times together
internal string GetTotalShiftTime(List<ShiftModel> list)
{
List<ShiftModel> shifts = _db.GetShiftTotals();
TimeSpan totalTime;

foreach (var shift in shifts)
{
TimeSpan time = TimeSpan.Parse(shift.TotalTime);
totalTime.Add(time);
}
}
// database file
internal List<ShiftModel> GetShiftTotals()
{
using SQLiteConnection conn = new(_dbFilePath);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT TotalTime, TotalMileage, Pay FROM records";
reader = cmd.ExecuteReader();

List<ShiftModel> shiftTotals = new();

while (reader.Read())
{
shiftTotals.Add(new ShiftModel()
{
TotalTime = reader["TotalTime"].ToString(),
TotalMileage = Convert.ToInt32(reader["TotalMileage"].ToString()),
Pay = Convert.ToDouble(reader["Pay"].ToString())
});
}

return shiftTotals;
}

// helper file with method to add times together
internal string GetTotalShiftTime(List<ShiftModel> list)
{
List<ShiftModel> shifts = _db.GetShiftTotals();
TimeSpan totalTime;

foreach (var shift in shifts)
{
TimeSpan time = TimeSpan.Parse(shift.TotalTime);
totalTime.Add(time);
}
}
my problem is getting the times to add. I was able to get it to work on a repl.it like this
class Program {
public static void Main (string[] args) {
TimeSpan a = TimeSpan.Parse("00:45:00");
TimeSpan b = TimeSpan.Parse("00:15:00");

Console.WriteLine(a.Add(b));
}
}
class Program {
public static void Main (string[] args) {
TimeSpan a = TimeSpan.Parse("00:45:00");
TimeSpan b = TimeSpan.Parse("00:15:00");

Console.WriteLine(a.Add(b));
}
}
but I can't figure out how to loop through the list, pull the TotalTime and then add them. I'm wanting to add the times up, and then return them as a string (or appropriate data type) in the totalTime variable on the initial screen
JakenVeina
JakenVeina13mo ago
you're having trouble adding a list of time spans? what's wrong with GetTotalShiftTime?
Mekasu0124
Mekasu0124OP13mo ago
my immediate thing is instantiating the total variable TimeSpan totalTime;
JakenVeina
JakenVeina13mo ago
oh, I see
Mekasu0124
Mekasu0124OP13mo ago
and being able to add to it. I don't know how to instantiate a blank TimeSpan variable
JakenVeina
JakenVeina13mo ago
same way you instantiate anything else
Mekasu0124
Mekasu0124OP13mo ago
TimeSpan totalTime = new();?
JakenVeina
JakenVeina13mo ago
that'll work also
var totalTime = TimeSpan.Zero;
var totalTime = TimeSpan.Zero;
also
totalTime += TimeSpan.Parse(shift.TotalTime);
totalTime += TimeSpan.Parse(shift.TotalTime);
totalTime.Add(time);
totalTime.Add(time);
does nothing
Mekasu0124
Mekasu0124OP13mo ago
you're awesome. thank you!
internal List<ShiftModel> GetShiftTotals()
{
using SQLiteConnection conn = new(_dbFilePath);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT TotalTime, TotalMileage, Pay FROM records";
reader = cmd.ExecuteReader();

List<ShiftModel> shiftTotals = new();

while (reader.Read())
{
shiftTotals.Add(new ShiftModel()
{
TotalTime = reader["TotalTime"].ToString(),
TotalMileage = Convert.ToInt32(reader["TotalMileage"].ToString()),
Pay = Convert.ToDouble(reader["Pay"].ToString())
});
}

return shiftTotals;
}
internal List<ShiftModel> GetShiftTotals()
{
using SQLiteConnection conn = new(_dbFilePath);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = "SELECT TotalTime, TotalMileage, Pay FROM records";
reader = cmd.ExecuteReader();

List<ShiftModel> shiftTotals = new();

while (reader.Read())
{
shiftTotals.Add(new ShiftModel()
{
TotalTime = reader["TotalTime"].ToString(),
TotalMileage = Convert.ToInt32(reader["TotalMileage"].ToString()),
Pay = Convert.ToDouble(reader["Pay"].ToString())
});
}

return shiftTotals;
}
since I'm already getting my values in the database file, would it be more efficient to just sum my totals from here and return them then to have a function in the helpers file that does it?
JakenVeina
JakenVeina13mo ago
uhhhhhhhhhhhhhh not sure what you're asking oh, I see I think you're asking whether you should roll your extension method into this query method no what you should ACTUALLY do is do this whole thing in SQL instead of querying for and retrieving 100 records, retrieve 1
SELECT SUM(TotalTime)
FROM records
SELECT SUM(TotalTime)
FROM records
Mekasu0124
Mekasu0124OP13mo ago
the database can do that? I don't have to parse and all that other jazz to add the times up? That's dope
JakenVeina
JakenVeina13mo ago
if you're doing things right do yourself a favor and read up on SQL basics
Mekasu0124
Mekasu0124OP13mo ago
ok I'll look into that none of the sql basics that I've found on google show anything to do with querying a SUM from the database. would you happen to have a link I can get?
JakenVeina
JakenVeina13mo ago
John Doe
SQL Tutorial
SQL Aggregate Functions
This tutorial introduces you to the most commonly used SQL aggregate functions including AVG, COUNT, MAX, MIN and SUM functions.
Mekasu0124
Mekasu0124OP13mo ago
tyvm
Want results from more Discord servers?
Add your server