C
C#8mo ago
Mekasu0124

✅ conditional input between two controls avalonia

<ComboBox
Name="SelectUser"
Classes="SelectUserCombo"
Grid.Row="1"
Grid.Column="0"
SelectedItem="{Binding SelectedUsername}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox
Name="SelectUser"
Classes="SelectUserCombo"
Grid.Row="1"
Grid.Column="0"
SelectedItem="{Binding SelectedUsername}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
public partial class SelectUserViewModel : ViewModelBase
{
public List<string> _usernames;
public string? _selectedUsername;
public string? _newUser;
public IObservableList<User?>? _currentUsers;

public SelectUserViewModel()
{
AvaloniaXamlLoader.Load(this);

CurrentUsers = UserQuery.GetUsernames();
ComboBox comboBox = this.
comboBox.ItemsSource = CurrentUsers;

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

IObservable<bool> okEnabled = selectedUsernameOk;

Ok = ReactiveCommand.Create(
ReturnSelectedUser,
okEnabled);
Cancel = ReactiveCommand.Create(() => {
Environment.Exit(0);
});
}
public partial class SelectUserViewModel : ViewModelBase
{
public List<string> _usernames;
public string? _selectedUsername;
public string? _newUser;
public IObservableList<User?>? _currentUsers;

public SelectUserViewModel()
{
AvaloniaXamlLoader.Load(this);

CurrentUsers = UserQuery.GetUsernames();
ComboBox comboBox = this.
comboBox.ItemsSource = CurrentUsers;

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

IObservable<bool> okEnabled = selectedUsernameOk;

Ok = ReactiveCommand.Create(
ReturnSelectedUser,
okEnabled);
Cancel = ReactiveCommand.Create(() => {
Environment.Exit(0);
});
}
I'm trying to load a list of usernames into a combo box (if any exists) when the page loads in the pages ViewModel. I know I can do it in the code behind like this
public partial class SelectUserView : UserControl
{
private List<User?>? Usernames;

public SelectUserView()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
Usernames = UserQuery.GetUsernames();
ComboBox comboBox = this.Find<ComboBox>("SelectUser");
comboBox.ItemsSource = Usernames;
}
}
public partial class SelectUserView : UserControl
{
private List<User?>? Usernames;

public SelectUserView()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
Usernames = UserQuery.GetUsernames();
ComboBox comboBox = this.Find<ComboBox>("SelectUser");
comboBox.ItemsSource = Usernames;
}
}
however, that's not best practice. The username that the user selects from the combo box is bound to SelectedUsername. The usernames shown within the combobox are loaded into an ObservableList CurrentUsers. How would I load that list into the combobox since the ViewModel says that this. does not have a method called Find()? Thanks
5 Replies
Mekasu0124
Mekasu01248mo ago
<ComboBox
Name="SelectUser"
Classes="SelectUserCombo"
Grid.Row="1"
Grid.Column="0"
SelectedItem="{Binding SelectedUsername}"
ItemsSource="{Binding CurrentUsers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="Select One" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox
Classes="SelectedUserInput"
Grid.Row="1"
Grid.Column="1"
Text="{Binding NewUser}" />
<Button
Classes="SelectUser"
Grid.Column="1"
Content="Play"
Command="{Binding Ok}" />
<ComboBox
Name="SelectUser"
Classes="SelectUserCombo"
Grid.Row="1"
Grid.Column="0"
SelectedItem="{Binding SelectedUsername}"
ItemsSource="{Binding CurrentUsers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="Select One" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBox
Classes="SelectedUserInput"
Grid.Row="1"
Grid.Column="1"
Text="{Binding NewUser}" />
<Button
Classes="SelectUser"
Grid.Column="1"
Content="Play"
Command="{Binding Ok}" />
I have a combobox that I'm loading a list into if the list exists
public partial class SelectUserViewModel : ViewModelBase
{
public List<string> _usernames;
public List<User> _currentUsers;
public string? _selectedUsername;
public string? _newUser;

public SelectUserViewModel()
{
CurrentUsers = UserQuery.GetUsernames();

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

IObservable<bool> okEnabled = selectedUsernameOk;

Ok = ReactiveCommand.Create(
ReturnSelectedUser,
okEnabled);
Cancel = ReactiveCommand.Create(() => {
Environment.Exit(0);
});
}

public User ReturnSelectedUser()
{
if (!string.IsNullOrEmpty(NewUser))
{
return new User
{
UserName = NewUser
};
}
else
{
return new User
{
UserName = SelectedUsername
};
}
}

public List<string> Usernames
{
get => _usernames;
set => this.RaiseAndSetIfChanged(ref _usernames, value);
}

public List<User> CurrentUsers
{
get => _currentUsers;
set => this.RaiseAndSetIfChanged(ref _currentUsers, value);
}

public string? SelectedUsername
{
get => _selectedUsername;
set => this.RaiseAndSetIfChanged(ref _selectedUsername, value);
}

public string? NewUser
{
get => _newUser;
set => this.RaiseAndSetIfChanged(ref _newUser, value);
}

public ReactiveCommand<Unit, User> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
public partial class SelectUserViewModel : ViewModelBase
{
public List<string> _usernames;
public List<User> _currentUsers;
public string? _selectedUsername;
public string? _newUser;

public SelectUserViewModel()
{
CurrentUsers = UserQuery.GetUsernames();

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

IObservable<bool> okEnabled = selectedUsernameOk;

Ok = ReactiveCommand.Create(
ReturnSelectedUser,
okEnabled);
Cancel = ReactiveCommand.Create(() => {
Environment.Exit(0);
});
}

public User ReturnSelectedUser()
{
if (!string.IsNullOrEmpty(NewUser))
{
return new User
{
UserName = NewUser
};
}
else
{
return new User
{
UserName = SelectedUsername
};
}
}

public List<string> Usernames
{
get => _usernames;
set => this.RaiseAndSetIfChanged(ref _usernames, value);
}

public List<User> CurrentUsers
{
get => _currentUsers;
set => this.RaiseAndSetIfChanged(ref _currentUsers, value);
}

public string? SelectedUsername
{
get => _selectedUsername;
set => this.RaiseAndSetIfChanged(ref _selectedUsername, value);
}

public string? NewUser
{
get => _newUser;
set => this.RaiseAndSetIfChanged(ref _newUser, value);
}

public ReactiveCommand<Unit, User> Ok { get; }
public ReactiveCommand<Unit, Unit> Cancel { get; }
}
this is the view model that operates the screen. I have the selected user name for the combo box bound to SelectedUser and I have the TextBox input bound to NewUser. I am trying to get the screen to 1. light up the Play Game button when either a username is selected from the drop down, or when a username is typed into the TextBox. 2. Return either the selected username or the new username depending on which the player chose to do. The point is if their username isn't found within the list for the combo box, then they would enter their new username into the text box and still be able to click play game I'm having problems getting this logic right
JakenVeina
JakenVeina8mo ago
I'm not sure on the specific implementation for Avalonia, if it'd be any different from WPF but your VM would be....
public partial class SelectUserViewModel
: ViewModelBase
{
public string? Username { get; set; }

public List<string> ExistingUsernames { get; }

public ReactiveCommand<Unit, User> Ok { get; }

public ReactiveCommand<Unit, Unit> Cancel { get; }
}
public partial class SelectUserViewModel
: ViewModelBase
{
public string? Username { get; set; }

public List<string> ExistingUsernames { get; }

public ReactiveCommand<Unit, User> Ok { get; }

public ReactiveCommand<Unit, Unit> Cancel { get; }
}
assuming that a username is unique across all users if you have an underlying UserId value that's the real PK, or if you want to display more info about a user, within the dropdown, than just their username, you could do...
public List<User> ExistingUsers { get; }
public List<User> ExistingUsers { get; }
but that's about it you should only need one set of items for the dropdown, and one property representing the textbox the textbox handles text, don't waste time trying to treat it like it's something else if you do this, then you'll provide a converter to the ComboBox to tell it how to translate a User into text to put in the textbox, when the user picks something when Ok is executed, do a quick lookup to see if the username that's in the textbox is existing or not, and go to town waaaaait..... woooooooooow, okay so, ComboBox is significantly different in Avalonia than WPF it's not editable why on EARTH would they call it a ComboBox then so, let's go with.....
public partial class SelectUserViewModel
: ViewModelBase
{
public string? NewUsername { get; set; }

public User? SelectedUser { get; set; }

public List<User> ExistingUsers { get; }

public ReactiveCommand<Unit, User> Ok { get; }

public ReactiveCommand<Unit, Unit> Cancel { get; }
}
public partial class SelectUserViewModel
: ViewModelBase
{
public string? NewUsername { get; set; }

public User? SelectedUser { get; set; }

public List<User> ExistingUsers { get; }

public ReactiveCommand<Unit, User> Ok { get; }

public ReactiveCommand<Unit, Unit> Cancel { get; }
}
although, really, I would recommend splitting up the Ok button into separate Create and Login buttons the alternative that keeps just one OK button is more convoluted, both mechanically, and UX-wise, I think you'd need to, at minimum, disable the NewUsername textbox whenever a user is selected from the dropdown then you'd need to make sure that the dropdown has a "None" or "Create New" option, so that the user can go BACK to creating a new username, if they've already selected something from the dropdown I guess that's not a terrible idea
Mekasu0124
Mekasu01248mo ago
I can split it up. I can make it to where the combobox and it's label have a login button and where the textbox has it's own create button
JakenVeina
JakenVeina8mo ago
that's what I'd do, I think
Mekasu0124
Mekasu01248mo ago
axaml file => https://pastebin.com/kGp1TDKE view model file => https://pastebin.com/DKchATXP Ok. I've split them up between two buttons, and have attempted the code for it in the view model. Both buttons return a User back to the main window view model (https://pastebin.com/g6zuNUiW) So what's supposed to happen is if the user selects a username from the combo box, and clicks login, then it pushes that user back to the MWVM and then goes to the home screen. If the user creates a new username, then it pushes that new user back to the MWVM and then writes it to the database and then goes to the home screen. I'm not entirely sure on how to get the MWVM's CreateUser() function to determine whether or not the user created a new username, or if it's an existing user name so that it knows what to do with it testing it how it sits, it does nothing. I even tried to do
public User ReturnSelectedUser()
{
if (SelectedUser == "Select One")
{
return new User
{
UserName = NewUser
};
}
else
{
return new User
{
UserName = SelectedUser
};
}
}
public User ReturnSelectedUser()
{
if (SelectedUser == "Select One")
{
return new User
{
UserName = NewUser
};
}
else
{
return new User
{
UserName = SelectedUser
};
}
}
and it does nothing. No errors. Doesn't write anything to the database. I placed breakpoints on the return statements, and when I enter a new username into the textbox, it triggers the ReturnSelectedUser function, but then skips the if statement and does the else part but still does nothing with the database and doesn't even trigger back to the MWVM I was able to get it figured out. Thank you for your help