C
C#4mo ago
Mekasu0124

✅ Conditionally rendering the text and style of a label avalonia

<UserControl>
<UserControl.Styles>
<Style Selector="Label"></Style>
<Style Selector="Label.Success"></Style>
<Style Selector="Label.Error"></Style>
</UserControl.Styles>

<Border>
<Grid>
<Grid>
<Label />
</Grid>
</Grid>
</Border>
</UserControl>
<UserControl>
<UserControl.Styles>
<Style Selector="Label"></Style>
<Style Selector="Label.Success"></Style>
<Style Selector="Label.Error"></Style>
</UserControl.Styles>

<Border>
<Grid>
<Grid>
<Label />
</Grid>
</Grid>
</Border>
</UserControl>
public class LoginViewModel : ViewModelBase
{
private bool _isSuccess;

public LoginViewModel() {}

public bool IsSuccess
{
get => _isSuccess;
set => this.RaiseAndSetIfChanged(ref _isSuccess, value);
}
}
public class LoginViewModel : ViewModelBase
{
private bool _isSuccess;

public LoginViewModel() {}

public bool IsSuccess
{
get => _isSuccess;
set => this.RaiseAndSetIfChanged(ref _isSuccess, value);
}
}
I have a label that has text that is worked with in the view model. The text that it dislpays is based off the actions of the user. I want to set this label up to where the view model displays the correct message and correct styles based off a boolean value.
24 Replies
br4kejet
br4kejet4mo ago
Create a value converter than converts bool to Visibility One for true=visible,false=collapsed, and another for false=visible,true=collapsed
Mekasu0124
Mekasu01244mo ago
public class LoginViewModel : ViewModelBase
{
private string ErrorText = "Invalid Username/Password. Try Again!";
private bool _isVisible = false;

public LoginViewModel()
{
Login = ReactiveCommand.Create(CheckInputs);
}

public User CheckInputs()
{
if (!_hp.ValidateUsername(Username))
{
IsVisible = true;
// clear username text in text box
}
}
}
public class LoginViewModel : ViewModelBase
{
private string ErrorText = "Invalid Username/Password. Try Again!";
private bool _isVisible = false;

public LoginViewModel()
{
Login = ReactiveCommand.Create(CheckInputs);
}

public User CheckInputs()
{
if (!_hp.ValidateUsername(Username))
{
IsVisible = true;
// clear username text in text box
}
}
}
ok here's where I'm at so far. I did the label like <Label Classes="Error" Grid.Row="0" Content="{Binding ErrorText}" IsVisible="{Binding IsVisible}" /> so now if the user enters an invalid username, I want the label to show, the program to sleep for 2 seconds, then clear the texbox where the user typed in the invalid username.
br4kejet
br4kejet4mo ago
Ah didn't know avalonia had an IsVisible property
Mekasu0124
Mekasu01244mo ago
it does 🙂 how would I get access to the text box to clear the text?
br4kejet
br4kejet4mo ago
You could bind the text box's text in your view model
Mekasu0124
Mekasu01244mo ago
I have that. It's bound to the variables Username and Password. I just don't know how to make the program sleep without freezing the program
br4kejet
br4kejet4mo ago
What do you need it to sleep for?
Mekasu0124
Mekasu01244mo ago
it's like a make shift error thing. So really what's supposed to happen is that if the user enters invalid credentials, the system errors. It changes the view state of the error label so that the string Invalid Username/Password. Try Again! shows and the TextBox styles change from a normal state to a yellow background. After 2-3 seconds, the screen kind of does a "refresh" and it changes the styles of the TextBox's back to their normal state, clears the user's input, and allows the user to try again
br4kejet
br4kejet4mo ago
You could a Task + the dispatcher to do that Set the background to yellow, start a task (or thread) and await Task.Delay (or Thread.Sleep) for 3000 millis, then call the dispatcher's Invoke method, where you'd restore the background
Mekasu0124
Mekasu01244mo ago
so there is a TextBox for the username, a MaskedTextBox for the password and a Label for the error text. When the user enter's their credentials, it runs a check. If any part of the check fails, then it changes the TextBox and MaskedTextBox to a yellow background and changes the IsVisible state so that it can be displayed. After 3 seconds, the program needs to clear the text and change the styles back to normal on the TextBox and the MaskedTextBox and then change the IsVisible state of the label again so that it's no longer displayed
br4kejet
br4kejet4mo ago
You can access the dispatcher via i think Application.Current.Dispatcher, at least for WPF you can, not sure about avalonia
Mekasu0124
Mekasu01244mo ago
ok but my brain is running into a problem
br4kejet
br4kejet4mo ago
Sleeping is the command line way of things, in UI applications you use timers/background threads and then dispatch a completion callback onto the main thread
Mekasu0124
Mekasu01244mo ago
ok so if I use await Task.Delay() then I'll need a cancellation token.
br4kejet
br4kejet4mo ago
textBox.Background = Brushes.Yellow;
Task.Delay(3000).ContinueWith((task) => {
Dispatcher.Invoke(() => {
textBox.ClearValue(TextBox.BackgroundProperty);
});
}, TaskScheduler.FromCurrentSynchronizationContext());
textBox.Background = Brushes.Yellow;
Task.Delay(3000).ContinueWith((task) => {
Dispatcher.Invoke(() => {
textBox.ClearValue(TextBox.BackgroundProperty);
});
}, TaskScheduler.FromCurrentSynchronizationContext());
Eh not really unless you want to cancel the yellow thing feature?
Mekasu0124
Mekasu01244mo ago
namespace SplashScreen.ViewModels
{
internal class SplashViewModel : ViewModelBase
{
#region PrivateColorsForScreen
private string _background = "";
private string _foreground = "";
private string _border = "";
public string Background
{
get => _background;
set => this.RaiseAndSetIfChanged(ref _background, value);
}
public string Foreground
{
get => _foreground;
set => this.RaiseAndSetIfChanged(ref _foreground, value);
}
public string Border
{
get => _border;
set => this.RaiseAndSetIfChanged(ref _border, value);
}
#endregion

private string _startUpMessage = "Application Is Loading . . .";
public string StartUpMessage
{
get => _startUpMessage;
set => this.RaiseAndSetIfChanged(ref _startUpMessage, value);
}

private CancellationTokenSource _cts = new CancellationTokenSource();
public CancellationToken cancellationToken => _cts.Token;

public void Cancel()
{
_cts.Cancel();
}
}
}
namespace SplashScreen.ViewModels
{
internal class SplashViewModel : ViewModelBase
{
#region PrivateColorsForScreen
private string _background = "";
private string _foreground = "";
private string _border = "";
public string Background
{
get => _background;
set => this.RaiseAndSetIfChanged(ref _background, value);
}
public string Foreground
{
get => _foreground;
set => this.RaiseAndSetIfChanged(ref _foreground, value);
}
public string Border
{
get => _border;
set => this.RaiseAndSetIfChanged(ref _border, value);
}
#endregion

private string _startUpMessage = "Application Is Loading . . .";
public string StartUpMessage
{
get => _startUpMessage;
set => this.RaiseAndSetIfChanged(ref _startUpMessage, value);
}

private CancellationTokenSource _cts = new CancellationTokenSource();
public CancellationToken cancellationToken => _cts.Token;

public void Cancel()
{
_cts.Cancel();
}
}
}
this is how my splash screen is done well I thought maybe I could do a system that would 1. validate the username. If the username is not a valid input => error class 2. validate the password. If the password is not a valid input => error class 3. validate that the username exists in the database. if not => error class 4. validate that the password input matches the currently saved password in the database. If not => error class 5. validate that both username and password are not empty strings. If so => error class Error Class: <- started by the Task.Delay - Has access to the TextBox, MaskedTextBox, Label Classes="Error" objects - Changes both TextBox and MaskedTextBox background colors to yellow. - runs for 3 seconds - changes both TextBox and MaskedTextBox background colors back to normal. - clears the user's inputs from both the TextBox and MaskedTextBox
br4kejet
br4kejet4mo ago
Yeah you can do that last section with the dispatcher code I sent, but you do those extra things as well as set the yellow background But I don't know how you're gonna do it entirely in view models though That's kinda where my MVVM knowledge comes to an end... If avalonia has a Behaviour system built in, you might be able to create one and do stuff, and somehow access the view model maybe?
Mekasu0124
Mekasu01244mo ago
wait I have the background of all the items set through compiled bindings. In the view model I set those values from a json file that is loaded on run time.
public LoginViewModel : ViewModelBase
{
private string _background = "";
private User _user = new();
private StyleModel _userStyles;

public LoginViewModel(User user)
{
_userStyles = User.UserSEttings.StylesModel;
Background = _userStyles.BackgroundColor;
}

public User User
{
get => _user;
set => this.RaiseAndSetIfChanged(ref _user, value);
}
}
public LoginViewModel : ViewModelBase
{
private string _background = "";
private User _user = new();
private StyleModel _userStyles;

public LoginViewModel(User user)
{
_userStyles = User.UserSEttings.StylesModel;
Background = _userStyles.BackgroundColor;
}

public User User
{
get => _user;
set => this.RaiseAndSetIfChanged(ref _user, value);
}
}
so I can control it through that
public async Task<User> CheckInputs()
{
if (!_hp.ValidateUsername(Username))
{
IsVisible = true;
Background="#E2EB23";
await Task.Delay(3000).ContinueWith((task) =>
{
Dispatcher.Invoke(() =>
{
Background = _userStyles.BackgroundColor;
Username = "";
Password = "";
IsVisible = false;
});
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
public async Task<User> CheckInputs()
{
if (!_hp.ValidateUsername(Username))
{
IsVisible = true;
Background="#E2EB23";
await Task.Delay(3000).ContinueWith((task) =>
{
Dispatcher.Invoke(() =>
{
Background = _userStyles.BackgroundColor;
Username = "";
Password = "";
IsVisible = false;
});
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
and I'm getting the error An object reference is required for the non-static field, method, or property 'Dispatcher.Invoke(Action)'
br4kejet
br4kejet4mo ago
Is it not Application.Current.Dispatcher? Or Dispatcher.Instance maybe I haven't used avalonia much
Mekasu0124
Mekasu01244mo ago
It's Dispatcher.UIThread.Invoke(() => {}) just foudn that out LoginViewModel: https://pastebin.com/btLF7T8K LoginView.axaml: https://pastebin.com/nNCpck3P Here's what I have and I currently am getting the error Unable to find public constructor for type Diary:Diary.ViewModels.LoginViewModel() LoginView.axaml Line 12 and I have zero idea why
br4kejet
br4kejet4mo ago
It takes a User parameter, so it can't create it reflectively Well there's no default constructor so it can't create it
Mekasu0124
Mekasu01244mo ago
ok so then how should I write it? the only reason I ask how should I write it is because I have other projects where I've done simliar things in a near exact manor and they worked fine
br4kejet
br4kejet4mo ago
I'm not sure, I don't really use MVVM that much anymore
Mekasu0124
Mekasu01244mo ago
ok no problem 🙂 thanks for your help. I'm going to create a new thread for my 3 remaining errors.