C
C#14mo ago
Mekasu0124

✅ Getting Button Command Parameter Avalonia

public HomeViewModel()
{
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}

public void PlayGame() { }
public ReactiveCommand<Unit, Unit> Play { get; }
public HomeViewModel()
{
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}

public void PlayGame() { }
public ReactiveCommand<Unit, Unit> Play { get; }
I have 4 buttons on my home screen
<Button Command={Binding Play} CommandParameter="Something" />
<Button Command={Binding Play} CommandParameter="Something" />
when I click the button, how do I get the PlayGame() function to get access to that CommandParameter?
28 Replies
arion
arion14mo ago
The command parameter is an object passed to the reactive command, for your case, your command expects a Unit as a parameter and returns a Unit (sort of like void) An example of receiving a different parameter would be:
<Button Content="🔵 Notification" "{Binding ShowDebugNotificationCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
<Button Content="🔵 Notification" "{Binding ShowDebugNotificationCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
public ReactiveCommand<Window, Unit> ShowDebugNotificationCommand
public ReactiveCommand<Window, Unit> ShowDebugNotificationCommand
Setting the reactive command would be:
ShowDebugNotificationCommand = ReactiveCommand.Create<Window>(window =>
{
// Code Go Here
});
ShowDebugNotificationCommand = ReactiveCommand.Create<Window>(window =>
{
// Code Go Here
});
Mekasu0124
Mekasu0124OP14mo ago
so it would need to be
public ReactiveCommand<string, unit> Play { get; }
public ReactiveCommand<string, unit> Play { get; }
? one sec
arion
arion14mo ago
yes If you want to pass other object states from the UI side into the ReactiveCommand you can name your axaml elements eg.
<Button Name="PlayButton"/>
<Button Name="PlayButton"/>
Mekasu0124
Mekasu0124OP14mo ago
public class HomeScreenViewModel : ViewModelBase
{
public string? _selectedDifficulty;

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

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}
public void PlayGame()
{

}
public ReactiveCommand<Unit, Unit> Play { get; }
public class HomeScreenViewModel : ViewModelBase
{
public string? _selectedDifficulty;

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

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}
public void PlayGame()
{

}
public ReactiveCommand<Unit, Unit> Play { get; }
this is the HomeScreenViewModel. The reason I need access to the Command Parameter is so that I can return that and the selected difficulty. One second and I'll explain that
public void PlaySelectedGame()
{
var vm = new HomeScreenViewModel();
AdditionGame();
SubtractionGame();
MultiplicationGame();
DivisionGame();
RandomGame();

Observable.Merge(
vm.Play)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
switch (model)
{
case "Addition":
Content = AdditionGame(difficulty, username);
break;

case "Subtraction":
Content = SubtractionGame(difficulty, username);
break;

case "Multiplication":
Content = MultiplicationGame(difficulty, username);
break;

case "Division":
Content = DivisionGame(difficulty, username);
break;

case "Random":
Content = RandomGame(difficulty, username);
break;

default:
throw new ArgumentException("Cannot Determine Game Type");
}
}
});

Content = vm;
}
public void PlaySelectedGame()
{
var vm = new HomeScreenViewModel();
AdditionGame();
SubtractionGame();
MultiplicationGame();
DivisionGame();
RandomGame();

Observable.Merge(
vm.Play)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
switch (model)
{
case "Addition":
Content = AdditionGame(difficulty, username);
break;

case "Subtraction":
Content = SubtractionGame(difficulty, username);
break;

case "Multiplication":
Content = MultiplicationGame(difficulty, username);
break;

case "Division":
Content = DivisionGame(difficulty, username);
break;

case "Random":
Content = RandomGame(difficulty, username);
break;

default:
throw new ArgumentException("Cannot Determine Game Type");
}
}
});

Content = vm;
}
when I return that CommandParameter and the SelectedDifficulty back to the MainWindowViewModel, it can determine which game was selected to play, and it's difficulty level so tha the switch statement can launch the correct game screen. The functions that are undefinied will also have similar Obervable.Merge()'s which will route the user to the end game screen when they complete the game. At this moment, I'm just trying to get MWVM access to the CommandParameter of the button clicked, and the selected difficulty for the switch case to execute correctly and pass the difficulty and the username to the correct screen (which is a smaller second issue. How do I get access to the username that was selected from the SelectUserViewModel when it's done in a different function)?
arion
arion14mo ago
The question is a bit different from the original and has a lot to unpack, for the original question, if you'd like to select a single parameter to pass into a reactive command you can name the control in the axaml side and pass that via CommandParameter="{Binding #ControlName.WhateverPropertyYouNeed}" if you'd like to send multiple parameters, avalonia has some documentation regarding MultiBindings and converters. If you want to return an object from a dialog, then check out avalonia's InteractionContext, the documentation for this is sadly very limited to the barebones basics, though this tutorial of theirs actually shows a way better example on how to do this
Mekasu0124
Mekasu0124OP14mo ago
I don't really know how to explain it correctly so I'm going to try my best
public class Game
{
public int? Id { get; set; }
public string? TodaysDate { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? TotalQuestions { get; set; }
public string? Difficulty { get; set; }
public string? GameType { get; set; }
}
public class Game
{
public int? Id { get; set; }
public string? TodaysDate { get; set; }
public string? StartTime { get; set; }
public string? EndTime { get; set; }
public double? Duration { get; set; }
public int? TotalQuestions { get; set; }
public string? Difficulty { get; set; }
public string? GameType { get; set; }
}
this is my game model
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="Addition"/>
<Button Classes="HomeButton" Grid.Row="0" Content="Addition" Command="{Binding Play}" CommandParameter="Addition"/>
when a button is clicked,
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}

public Game PlayGame()
{
Game game = new()
{
TodaysDate = date,
StartTime = null,
EndTime = null,
Duration = null,
TotalQuestion = null,
Difficulty = SelectedDifficulty,
GameType = ""
};

GameQuery.CreateNewGame(model);
return game;
}
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create(
PlayGame,
selectionOk);
}

public Game PlayGame()
{
Game game = new()
{
TodaysDate = date,
StartTime = null,
EndTime = null,
Duration = null,
TotalQuestion = null,
Difficulty = SelectedDifficulty,
GameType = ""
};

GameQuery.CreateNewGame(model);
return game;
}
this part is activated. The first thing is setting the GameType equal to the button that is clicked command parameter and then that information creates a new game in the database with all null values except the Difficulty and GameType
arion
arion14mo ago
So you just want to pass the "Addition" as the parameter to the PlayCommand?
Mekasu0124
Mekasu0124OP14mo ago
the PlayGame command yes so that way it can write it to the database
arion
arion14mo ago
- public ReactiveCommand<Unit, Unit> Play { get; }
+ public ReactiveCommand<string, Unit> Play { get; }
- public ReactiveCommand<Unit, Unit> Play { get; }
+ public ReactiveCommand<string, Unit> Play { get; }
- public Game PlayGame()
+ public Game PlayGame(string gameType)
- public Game PlayGame()
+ public Game PlayGame(string gameType)
Mekasu0124
Mekasu0124OP14mo ago
No description
arion
arion14mo ago
- Play = ReactiveCommand.Create(PlayGame, selectionOk);
+ Play = ReactiveCommand.Create<string>(PlayGame, selectionOk);
- Play = ReactiveCommand.Create(PlayGame, selectionOk);
+ Play = ReactiveCommand.Create<string>(PlayGame, selectionOk);
Mekasu0124
Mekasu0124OP14mo ago
No description
arion
arion14mo ago
Try this one,
Play = ReactiveCommand.Create<string>(gameType => PlayGame(gameType), selectionOk);
Play = ReactiveCommand.Create<string>(gameType => PlayGame(gameType), selectionOk);
arion
arion14mo ago
Shows no errors on my side
Mekasu0124
Mekasu0124OP14mo ago
that's not working either and it's becoming frustrating
arion
arion14mo ago
Whats the newest error?
Mekasu0124
Mekasu0124OP14mo ago
cannot implicitly conver type ReactiveUI.ReactiveCommand<string, System.Reactive.Unit> to ReactiveUI.ReactiveCommand<string, MathGame.Models.Game>
arion
arion14mo ago
Where is this error thrown? Mind screenshotting it? Did you change your
public ReactiveCommand<string, Unit> Play { get; }
public ReactiveCommand<string, Unit> Play { get; }
to be something else instead?
Mekasu0124
Mekasu0124OP14mo ago
it's not a unit. it's a game model public ReactiveCommand<string, Game> Play { get; } I think I have myself backwards. I don't need to create a new blank game when the game is selected. I just need to return the command parameter of the button clicked and the selected difficulty back to the main window view model so the difficulty the user selects from the combo box and the command parameter from the button clicked for the game type is what needs to be returned
arion
arion14mo ago
You should have told me you've changed things, if you want that, then do
Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType), selectionOk);
Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType), selectionOk);
Mekasu0124
Mekasu0124OP14mo ago
public class HomeScreenViewModel : ViewModelBase
{
public HomeScreenViewModel()
{
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType),selectionOk);
}

public Game PlayGame(string gameType)
{
return new Game
{
Difficulty = SelectedDifficulty,
GameType = gameType
};
}
public ReactiveCommand<string, Game> Play { get; }
}
public class HomeScreenViewModel : ViewModelBase
{
public HomeScreenViewModel()
{
IObservable<bool> selectionOk = this.WhenAnyValue(
x => x.SelectedDifficulty,
x => x != "Select One");

Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType),selectionOk);
}

public Game PlayGame(string gameType)
{
return new Game
{
Difficulty = SelectedDifficulty,
GameType = gameType
};
}
public ReactiveCommand<string, Game> Play { get; }
}
so here's where I'm at and I'm not getting any errors I do apologize. I'm sorry. I just had a moment to think about what I was trying to do and then made the change.
arion
arion14mo ago
nod let me know if u need any more assistance
Mekasu0124
Mekasu0124OP14mo ago
public void CreateNewUser()
{
var vm = new SelectUserViewModel();

Observable.Merge(
vm.Login,
vm.CreateUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
Content = PlaySelectedGame();
}
else
{
Content = vm;
}
});

Content = vm;
}

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

Observable.Merge(
vm.Play
.Select(_ => (Game)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
// run switch statement for model.GameType
}
else
{
Content = vm;
}
});

Content = vm;
}
public void CreateNewUser()
{
var vm = new SelectUserViewModel();

Observable.Merge(
vm.Login,
vm.CreateUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
Content = PlaySelectedGame();
}
else
{
Content = vm;
}
});

Content = vm;
}

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

Observable.Merge(
vm.Play
.Select(_ => (Game)null))
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
// run switch statement for model.GameType
}
else
{
Content = vm;
}
});

Content = vm;
}
so I've started the GameEnginView and GameEngineViewModel files. I now need these to launch correctly. My first problem is where I call PlaySelectedGame in the SelectUserViewModel. It's saying Cannot implicitly conver type 'void' to MathGame.ViewModels.ViewModelBase. All I know is that if I change it to
public void CreateNewUser()
{
var vm = new SelectUserViewModel();
+ var vm2 = new HomeScreenViewModel();

Observable.Merge(
vm.Login,
vm.CreateUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
- Content = PlaySelectedGame();
+ Content = vm2;
}
else
{
Content = vm;
}
});

Content = vm;
}
public void CreateNewUser()
{
var vm = new SelectUserViewModel();
+ var vm2 = new HomeScreenViewModel();

Observable.Merge(
vm.Login,
vm.CreateUser)
.Take(1)
.Subscribe(model =>
{
if (model != null)
{
- Content = PlaySelectedGame();
+ Content = vm2;
}
else
{
Content = vm;
}
});

Content = vm;
}
it works fine, but I need it to call the function that handles the view model instead of how the bottom code snippet shows what I mean is that when the MainWIndowViewModel loads, it calls the CreateNewUser() function and works fine, but when I use the CreateNewUser() function to call PlaySelectedGame() to show the home screen and get the values back that I need for screen navigation, it gives that error
arion
arion14mo ago
It might be that it expects a void and instead gets a game? from: Play = ReactiveCommand.Create<string, Game>(gameType => PlayGame(gameType),selectionOk); <:zerotwo_shrug:841954087238107186> i've never had game logic intertwined in ui logic so its a bit out of my area of expertise
Mekasu0124
Mekasu0124OP14mo ago
in my SelectUserViewModel it returns a <Unit, User> instead of like the HomeScreenViewModel which returns a <string, Game>
No description
Mekasu0124
Mekasu0124OP14mo ago
and I don't have any game logic yet. This is all just getting information and returning it back to the main window view model and changing screens based off that information
arion
arion14mo ago
uh, i got no idea sorry
Mekasu0124
Mekasu0124OP14mo ago
it's all good 🙂 thank you for your help so far ❤️ I was able to get it resolved. I wasn't supposed to set it to Content = ... but instead just call the function itself as the view has the ViewModel content already instantiated within it.
Want results from more Discord servers?
Add your server