C
C#10mo ago
Mekasu0124

✅ Unsure of how to fix these avalonia errors

Object reference not set to an instance of an object -> LoginView.axaml
Unable to resolve type SplashViewModel from namespace using:SplashScreen.ViewModels -> Splash.axaml
Object reference not set to an instance of an object -> UpdateAvailableView.axaml
Object reference not set to an instance of an object -> LoginView.axaml
Unable to resolve type SplashViewModel from namespace using:SplashScreen.ViewModels -> Splash.axaml
Object reference not set to an instance of an object -> UpdateAvailableView.axaml
LoginView.axaml: https://pastebin.com/Kb0resSx LoginViewMode.cs: https://pastebin.com/BcuyxF5S Splash.axaml: https://pastebin.com/iazXwshx SplashViewModel.cs: https://pastebin.com/U1FEwwSU UpdateAvailableView.axaml: https://pastebin.com/C9WhCRC5 UpdateAvailableViewModel.cs: https://pastebin.com/p2mT7zt2 I spent a vast majority of yesterday trying to avoid asking for assistance with these problems and tried fixing them on my own. I get these errors in my Error List in visual studio. When I try running my program, it launches, but it's just a black screen and I'm not sure how to fix these problem. Thanks
112 Replies
Klarth
Klarth10mo ago
If it launches, there are probably ghost errors. It happens sometimes because XAML has its own tooling that's separate from C#'s. You can double check by checking the Output tab in VS to see if the build really failed. If it was successful, just ignore the errors. You need to restart VS to get rid of them, usually. Not sure about the black screen issue. It's hard to debug multiple file systems without knowing exactly which view is black. Easier if it were on GitHub so somebody could clone + run locally.
Mekasu0124
Mekasu0124OP10mo ago
GitHub
GitHub - mekasu0124/Diary: A general use diary with the option to s...
A general use diary with the option to store your data locally or in online storage - mekasu0124/Diary
Klarth
Klarth10mo ago
You baked in way too many bugs within testing. I really have no idea how you got this far. 1. You used async in a constructor which silently fails. Do not do this. Instead:
public MainWindowViewModel(Database db, GithubService gvs, Helpers hp)
{
_db = db;
_gvs = gvs;
_hp = hp;
}
public MainWindowViewModel(Database db, GithubService gvs, Helpers hp)
{
_db = db;
_gvs = gvs;
_hp = hp;
}
And call your async method from code-behind:
public partial class MainWindowView : Window
{
public MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!;

public MainWindowView()
{
InitializeComponent();
}

protected override async void OnLoaded(RoutedEventArgs e) // Yes this needs to be async void
{
await ViewModel.CheckForUpdate();
}
}
public partial class MainWindowView : Window
{
public MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!;

public MainWindowView()
{
InitializeComponent();
}

protected override async void OnLoaded(RoutedEventArgs e) // Yes this needs to be async void
{
await ViewModel.CheckForUpdate();
}
}
2. You have no ViewLocator, so your Views won't be found from your ViewModels anyways. You can either add the ViewLocator or do this in App.axaml:
<Application
x:Class="Diary.App"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="using:Diary.Views"
xmlns:vm="using:Diary.ViewModels"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

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

<Application.DataTemplates>
<DataTemplate x:DataType="vm:LoginViewModel">
<v:LoginView />
</DataTemplate>
<DataTemplate x:DataType="vm:UpdateAvailableViewModel">
<v:UpdateAvailableView />
</DataTemplate>
</Application.DataTemplates>
</Application>
<Application
x:Class="Diary.App"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="using:Diary.Views"
xmlns:vm="using:Diary.ViewModels"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

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

<Application.DataTemplates>
<DataTemplate x:DataType="vm:LoginViewModel">
<v:LoginView />
</DataTemplate>
<DataTemplate x:DataType="vm:UpdateAvailableViewModel">
<v:UpdateAvailableView />
</DataTemplate>
</Application.DataTemplates>
</Application>
At this point, I'm getting exceptions because I don't have the necessary files, so I can't diagnose any further. This observable merging seems like a bad idea, but I don't use RxUI:
public void UpdateScreen(VersionData version)
{
var vm = new UpdateAvailableViewModel(version, CurrentUser);

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

Content = vm;
}
public void UpdateScreen(VersionData version)
{
var vm = new UpdateAvailableViewModel(version, CurrentUser);

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

Content = vm;
}
Your csproj also has this which prevents nesting of MainWindowView in the Solution Explorer:
<ItemGroup>
<Compile Update="Views\MainWindowView.axaml.cs">
<DependentUpon>MainWindowView.axaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Update="Views\MainWindowView.axaml.cs">
<DependentUpon>MainWindowView.axaml</DependentUpon>
</Compile>
</ItemGroup>
Mekasu0124
Mekasu0124OP10mo ago
Oh wow. So i just got a lot of it backwards and didn’t even realize it. Aside from the view locator, I’ve been doing everything else the same across multiple projects. Hm. Ok. I’ll have to do some rewriting when I get home from work. Another question for you. I’m wanting to build my apps for offline use as well. Like a hybrid type thing. I want to create a function or a class (whichever is better) that I can call from app.axaml.cs. It basically check if the user is connected to internet. If so, it’ll use an online dabatase. Otherwise, it’ll use SQLite with a local database instead and then when the user connects to internet again in the future, it’ll push all their locally saved progress to the online database
Klarth
Klarth10mo ago
There's no way to reliably check internet connection without making a request. You could create an endpoint to ping for a fast response on startup, otherwise you need to fallback when the connection times out (manually adding this to the task). Alternatively, you could work the other way around: host locally by default and sync to the online API periodically. You won't need intensive fallback handling to offline mode on every request this way. If you're using a REST client like https://github.com/reactiveui/refit, it will let you configure a timeout in one spot for all requests.
Mekasu0124
Mekasu0124OP10mo ago
Great advice. Ty. I’m using a FastAPI for this project if that helps With FastAPI don’t I have to use something called UUID
Klarth
Klarth10mo ago
Oh, Python stuff. No idea if it has a client generator. Otherwise, you need to create the client with types and endpoints yourself on the C# side so you can actually communicate with the API. Refit helps with that if you need to build it yourself.
Mekasu0124
Mekasu0124OP10mo ago
This is how I use the FastAPI on my websites. I’m assuming it would be similar? Probably not. I’ll have to look up how to use FastAPI with a C# Avalonia desktop app
No description
No description
Klarth
Klarth10mo ago
That looks poorly maintainable. Having a function that mixes API access, serialization, data transforms, and defining the storage schema which apparently just is supposed to leak to the caller? .NET is strongly typed, so you create Models to store the data. Either classes or records, but records are easier to define. The property names in the Model match the JSON schema, so System.Text.Json will let you map it in a single call. Data transforms are less straightforward.
Mekasu0124
Mekasu0124OP10mo ago
Which part? The Python or the JavaScript?
Klarth
Klarth10mo ago
"Yes"
Mekasu0124
Mekasu0124OP10mo ago
Right. I’ll be using a json file for the config file. It’ll have a setup Boolean, and it’ll hold the users settings that they adjust through the settings menu
Klarth
Klarth10mo ago
Obvious bias because I'm used to writing in C#. :catlul:
Mekasu0124
Mekasu0124OP10mo ago
So the Java script is the React API. That’s how it’s used and that’s how everyone who’s helped me with my code used it too. I don’t know enough about JS to do anything more than put stuff on the screen. As far as the Python goes, all that function is doing is reading in the information from the json file. It’s going to create a new Project model and then put them all in a list and return it back to the Java script code
Klarth
Klarth10mo ago
The JS side is certainly the worse of the two.
Mekasu0124
Mekasu0124OP10mo ago
True lol but I always use json files that are locally hosted to store the setup Boolean and other items needed from the user that don’t need to go anywhere past local host It’s ReactJS. lol to my knowledge, and I could always be wrong, they do it like that in the docs too
Klarth
Klarth10mo ago
I know. The examples (and JS as a whole) will just DIY the schema definition in code (functions) instead of creating concrete type definitions.
Mekasu0124
Mekasu0124OP10mo ago
Understandable. The only thing I hate about C# that I love about Python is the type definitions. If I have x = 9, it should be smart enough to know that’s an integer lol but that’s just my opinion
Klarth
Klarth10mo ago
var x = 9; good enough And you keep strong typing, don't need type hints, etc.
Mekasu0124
Mekasu0124OP10mo ago
Idk I just don’t feel “natural” using var lmao
Klarth
Klarth10mo ago
I always use an explicit type for numerics, but that's possibly from long-ingrained habits.
Mekasu0124
Mekasu0124OP10mo ago
everyone has their own way of doing things, and that's what makes us unique 😄
Klarth
Klarth10mo ago
Classes and structs? Almost never outside of needing to define something and initialize later.
Mekasu0124
Mekasu0124OP10mo ago
ok. just got to my computer. going back up and starting re-writes that's what I like about Python, although different, to me they're the same
class MyClass:
def __init__(self):
self.username = ""
self.password = ""
class MyClass:
def __init__(self):
self.username = ""
self.password = ""
public class MyClass()
{
private string _username;
private string _password;
}
public class MyClass()
{
private string _username;
private string _password;
}
corrected*
Klarth
Klarth10mo ago
C# version's members aren't accessible anywhere.
public record MyClass(string UserName, string Password);
public record MyClass(string UserName, string Password);
Is the better way to define classes that are data-only (no behavior).
Mekasu0124
Mekasu0124OP10mo ago
1. You used async in your constructor which silently fails. Do not do this, instead: <code>
Right. It silently fails because I did not await the call. I didn't await the call because I didn't want to make the constructor async, however, I will give your method a try.
2. You have no ViewLocator, so your Views won't be found from your ViewModels anyways. You can either add the ViewLocator or do this in App.axaml: <code>
My GradeBook application doesn't have a ViewLocator file either, and it runs perfectly fine. https://github.com/mekasu0124/GradeBook
This observable merging seems like a bad idea, but I don't use RxUI: <code> Your csproj also has this which prevents nesting of MainWindowView in the solution explorer: <code>
I'll look into this, but it helps me to keep the MainWindowView in the View folder and same for MainWindowViewModel too.
Alternatively, you could work the other way around: host locally by default and sync to the online API periodically. You won't need intensive fallback handling to offline mode on every request this way. If you're using a REST client like <link>, it will let you configure a timeout in one spot for all requests
I'm interested in learning more about this and possibly seeing some code examples too please?
Klarth
Klarth10mo ago
2. I don't see any way that project could work to resolve views. There don't seem to be any DataTemplates nor is there a ViewLocator. I can't really give Refit examples. I've tried it once on an API that I had a serious blocker on, so I ended up not being able to use said API. It was much nicer than using HttpClient or RestSharp though.
Mekasu0124
Mekasu0124OP10mo ago
ok I'll look up a tutorial for that
Klarth
Klarth10mo ago
There are some basic docs and plenty of examples if you GitHub search for relevant methods / classes like RestService.For. (The blocker was related to OAuth and requiring actual written permissions for a manually created account, so nothing limited by Refit itself. The API had changed since I last tried)
Mekasu0124
Mekasu0124OP10mo ago
both projects have a ViewModelBase.cs file in the VIewModels folder, but they don't have a ViewLocator file and I went to restore some repo's from old projects to look at them, but GitHub cleaned out the deleted list so that's a bust. I'm just going to create a new blank project, and see if it comes with one
Mekasu0124
Mekasu0124OP10mo ago
yea see it doesn't come with anymore since they changed how the project is created. Used too, you would select one of the options for an Avalonia project (desktop, web, mobile) in the menu when creating the project, but they changed it and I guess it doesn't come with it anymore
No description
Klarth
Klarth10mo ago
I've been seeing the .Desktop project created lately across other people. Which project template is that, specifically?
Mekasu0124
Mekasu0124OP10mo ago
the only one that avalonia has available to my knowledge and at the end I select compiled bindings and that's the setup process now since I last updated VS and this changed happened along with it so I'm assuming its set to the latest version of avalonia 🤷
No description
No description
No description
No description
No description
No description
Klarth
Klarth10mo ago
Oh, template studio. I don't use wizards.
Klarth
Klarth10mo ago
I use the MVVM app template on the bottom...the structure is different.
No description
Klarth
Klarth10mo ago
Also can optionally include the ViewLocator.
Klarth
Klarth10mo ago
GitHub
GitHub - AvaloniaUI/avalonia-dotnet-templates: Avalonia Templates f...
Avalonia Templates for dotnet new. Contribute to AvaloniaUI/avalonia-dotnet-templates development by creating an account on GitHub.
Mekasu0124
Mekasu0124OP10mo ago
thta's not even an option for me. I'm going to have to fix that because I very much dislike this .Desktop thing and having to fix my play button to target that instead of the project I'm working in. Compiling down to an exe from the .Desktop etc
Klarth
Klarth10mo ago
Having a .Desktop project is a bit more future-proof if you create new targets later.
Mekasu0124
Mekasu0124OP10mo ago
understandable, but I build my mobile apps in ReactNativeJS. I build my websites in ReactJS and if it's a desktop app that I'm just going to use by myself on my own computer, I build it in Python using PyQT.
Klarth
Klarth10mo ago
I have a single UI project (domain, services, etc in separate projects) which is going to take a fair amount of work to retarget if I add WASM or specific publishing options for Mac/Linux.
Mekasu0124
Mekasu0124OP10mo ago
the only reason I even learned C# and how to use Visual Studio was because it's easier to package down into a .exe than it is in Python which I still can't figure out
Klarth
Klarth10mo ago
By a fair amount, I probably mean like 2-3 hours at most. :udidit:
Mekasu0124
Mekasu0124OP10mo ago
yea I don't necessarily built applications for any specific os. I haven't even released a project yet that I've built haha. But when I build my desktop projects, I would probably use the publish wizard in Visual Studio and compile the project down into an executable for all 3 os's ohhhh it's because I had just bought this computer maybe 3 weeks ago, and that was the one thing I forgot to do you said that you don't use ReactiveUI or RxUI (unsure if those are the same) so if that's the case, then do you use CommunityToolkit instead?
Klarth
Klarth10mo ago
Yes, I use Mvvm Toolkit and bring in reactivity via System.Reactive / ReactiveProperty / DynamicData if needed.
Mekasu0124
Mekasu0124OP10mo ago
I'm curious because I don't exactly like ReactiveUI because of all the boiler-plate code. I would rather use CommunityToolkit but I don't know how to convert my MWVM to use CTk instead of RxUI
Klarth
Klarth10mo ago
You can use Mvvm Toolkit and ReactiveUI side-by-side until you phase out ReactiveUI. If you're migrating instead of starting a new project, start by changing ViewModelBase to derive from ObservableObject. Then change the various ReactiveUI properties to be:
[ObservableProperty] private string _firstName;
[ObservableProperty] private string _firstName;
And change the containing class to be a partial class.
Mekasu0124
Mekasu0124OP10mo ago
I am actually re-writing the Diary application. I just created a new project with CommunityToolkit. I would rather phase out of something at one time instead of as I go. I'll get too confused and try to do the boiler plate code lol yea the [ObservableProperty] and partial class part I know about. I tried to learn CTk once before
Klarth
Klarth10mo ago
Then work on changing ReactiveCommands to:
[RelayCommand]
public void DoTheThing() // Could be async Task, async Task<T>, etc
{
}
[RelayCommand]
public void DoTheThing() // Could be async Task, async Task<T>, etc
{
}
And bind your commands like: Command="{Binding DoTheThingCommand}"
Mekasu0124
Mekasu0124OP10mo ago
right. [ObservableProperty], [RelayCommand], Commmand=... those I know. My issue is like ok. My MainWindowViewModel for the Diary project I'm re-writing. CTk doesn't use Observable.Merge() so I know that In the ViewModel's for each window needs to be relay commands, but how would I return needed information from that relay command back to the main window view model so that it can be passed to the next screen that needs it?
Klarth
Klarth10mo ago
Messenger is typically what I use. You can check out https://github.com/stevemonaco/AvaloniaNavigationDemo You can still use observables with Mvvm Toolkit, but you need to bring it in separately as I mentioned earlier. I find Messenger to be 100x more readable for navigation scenarios though.
Mekasu0124
Mekasu0124OP10mo ago
ok. I'm looking at the link now this is a bit of a bit to chew for me. I'll see what I can do on my own and when I get snagged, I'll be back
using CommunityToolkit.Mvvm.ComponentModel;
using Diary.Models;
using Diary.Services;
using System.Threading.Tasks;

namespace Diary.ViewModels
{
public partial class MainWindowViewModel : ViewModelBase
{
#region ObservableProperties
[ObservableProperty] ViewModelBase _content;
[ObservableProperty] private UserModel _user;
[ObservableProperty] private Database _db;
[ObservableProperty] private GithubService _gvs;
[ObservableProperty] private Helpers _hp;
#endregion

public MainWindowViewModel(Database db, GithubService gvs, Helpers hp)
{
_db = db;
_gvs = gvs;
_hp = hp;
}

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

if (version.IsNewer)
{
UpdateScreen(version);
}
else
{
LoginScreen(_db, _hp);
}
}

public void LoginScreen()
{

}
}
}
using CommunityToolkit.Mvvm.ComponentModel;
using Diary.Models;
using Diary.Services;
using System.Threading.Tasks;

namespace Diary.ViewModels
{
public partial class MainWindowViewModel : ViewModelBase
{
#region ObservableProperties
[ObservableProperty] ViewModelBase _content;
[ObservableProperty] private UserModel _user;
[ObservableProperty] private Database _db;
[ObservableProperty] private GithubService _gvs;
[ObservableProperty] private Helpers _hp;
#endregion

public MainWindowViewModel(Database db, GithubService gvs, Helpers hp)
{
_db = db;
_gvs = gvs;
_hp = hp;
}

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

if (version.IsNewer)
{
UpdateScreen(version);
}
else
{
LoginScreen(_db, _hp);
}
}

public void LoginScreen()
{

}
}
}
this is where I'm at on the re-write of MainWindowViewModel and using RxUI, I would start implementing Observable.Merge()'s so instead I would do what? I would reactive commands within each window respectively?
Klarth
Klarth10mo ago
GitHub
AvaloniaNavigationDemo/AvaloniaNavigationDemo/ViewModels/MainWindow...
Contribute to stevemonaco/AvaloniaNavigationDemo development by creating an account on GitHub.
Klarth
Klarth10mo ago
Your other VMs are triggering the navigation events AFAIK, so they have the navigation commands (which send the messages).
Mekasu0124
Mekasu0124OP10mo ago
even with following that method, won't I have to still have some type of filter so that the program knows what window to show based off what button is clicked and where?
Klarth
Klarth10mo ago
Depends on how complex the navigation is. Usually not. You can create a load of message types or condense them into one that you "filter" over. There's really only two components: 1. Swapping the ActiveContent's ViewModel based on the message: https://github.com/stevemonaco/AvaloniaNavigationDemo/blob/master/AvaloniaNavigationDemo/ViewModels/MainWindowViewModel.cs 2. Changing the Window dimensions / characteristics when said ActiveContent changes: https://github.com/stevemonaco/AvaloniaNavigationDemo/blob/master/AvaloniaNavigationDemo/Views/MainWindow.axaml.cs#L25
Mekasu0124
Mekasu0124OP10mo ago
1. Swapping the ActiveContent's ViewModel based on the message: <llink>
Instead of that, just as a split second thought. I could be wrong. idk. but um. couldn't I just make a relay command that when activated, it calls the MainWindowViewModel.Content variable and changes it to the vm that's needed for that button click? Like
public LoginScreenViewMode()
{
[RelayCommand]
public void GoHome()
{
var vm = new HomeScreenViewModel();
MainWindowViewModel.Content = vm;
}
}
public LoginScreenViewMode()
{
[RelayCommand]
public void GoHome()
{
var vm = new HomeScreenViewModel();
MainWindowViewModel.Content = vm;
}
}
2.
Can't I just set the Width and Height in the axaml of the page itself and that work fine?
Klarth
Klarth10mo ago
1. Yes, but the entire point of messaging is that you can send the message from anywhere. You can't do MainWindowViewModel.Content = TheThing; from anywhere. You need the right instance of MainWindowViewModel in the exact spot you're using it. Wiring all of this is tricky, even with an IoC container doing the heavy lifting because you can somewhat easily run into circular dependencies. 2. Sure. But in this case, the login "window" wants a much smaller, fixed appearance than the app itself. This is, of course, optional.
Mekasu0124
Mekasu0124OP10mo ago
I'll fix the window sizes while I'm doing the re-write. Ok so them if I'm doing messaging, then I would need to register every window with it's own unique message which seems to always be a record. ok I'll give it a shot and send you a message when I get it done to see what you think
Klarth
Klarth10mo ago
record simply because very easy to define. They don't need any logic either. And yes, you can register each major navigation step. There are some patterns, like Master-Detail, which more-or-less happen automatically with binding, so you don't need messages there. Sometimes it's convenient to send out a message that the active item has changed for disconnected components to listen to. For menus or status bars that need to update their information.
Mekasu0124
Mekasu0124OP10mo ago
I'm going to be building a Menu Bar component to use inside of the MainWindowViewModel because It's not going to change per screen, and it's always going to be visible
Klarth
Klarth10mo ago
It may be awkward getting information into it. Especially if your hierarchy is like:
MainWindow
- MenuBar
- MainContent
- Editor
MainWindow
- MenuBar
- MainContent
- Editor
Getting data from Editor to MenuBar will be difficult. In some cases, you can use DI and inject MenuBar into Editor. Some cases, messaging is better. Some cases, you write another VM that contains shared data and inject into both to bind to.
Mekasu0124
Mekasu0124OP10mo ago
// app.axaml.cs
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);

var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>();

ServiceProvider provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);

Database db = new();
GithubService gvs = new();
Helpers hp = new();
string version = hp.CheckForVersionFile();
UserModel user = hp.CheckForStyleFile();


desktop.MainWindow = new MainWindow
{
DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>()
};
}

base.OnFrameworkInitializationCompleted();
}
// app.axaml.cs
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);

var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>();

ServiceProvider provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);

Database db = new();
GithubService gvs = new();
Helpers hp = new();
string version = hp.CheckForVersionFile();
UserModel user = hp.CheckForStyleFile();


desktop.MainWindow = new MainWindow
{
DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>()
};
}

base.OnFrameworkInitializationCompleted();
}
In my app.axaml.cs file I have 5 variables that need to be passed to the MainWindowViewModel so that the rest of the application has access to it when it's needed. I decided to create them here as it's easier to create once and re-use 100 times than it is to create 100 times. It's also easier on debugging for me. So using the Messenger.Register<>(); how do I pass the 5 variables to MainWindowViewModel? would you happen to have an example of this?
Klarth
Klarth10mo ago
You should register Database, GithubService, and Helpers with the DI services. Then add those 3 to MainWindowViewModel ctor parameters. Resolve the version and user within it from the Helpers instance passed in. You shouldn't mix-and-match when you're doing DI with more primitive parameter types like string or int that will be ambiguous. Which case?
Mekasu0124
Mekasu0124OP10mo ago
you completely lost me on this. I do apologize. I know that DI means Dependency Injector, but ok wait. let me see what I can figure out on my own. I'll be back on this messaging. I think I'm going to go that route for this application.
Klarth
Klarth10mo ago
TBH, I use the third approach with a lot of injection. Messaging isn't an all-or-nothing approach. Just use it where it makes sense. https://github.com/stevemonaco/ImageMagitek/blob/main/TileShop.UI/Features/Shell/MenuViewModel.cs I used messaging with menus before and I found I needed to register a lot of messages. This was before Mvvm Toolkit and record came along, so it's more convenient now.
Mekasu0124
Mekasu0124OP10mo ago
lol tbh, RxUI is a bit easier to understand, but I'm going to give this a try
Mekasu0124
Mekasu0124OP10mo ago
MainView.axaml: https://pastebin.com/k5PZhjEr MainView.axaml.cs: https://pastebin.com/MBpgCEBM MainViewModel.cs: https://pastebin.com/7XRNMiyn ok so I've copied the code from the navigation example repo literally line for line and in doing so, I'm resulting in 3 errors. and I'm lost on how to solve them
No description
Klarth
Klarth10mo ago
Typos: Binging
Mekasu0124
Mekasu0124OP10mo ago
i hate spelling >_< ok so since I'm redoing this, would it be better to check for my version.txt and config.json files in the app.axaml.cs file or should I move that to the helpers file and then call it in the MainWindowViewModel or MainViewModel?
Klarth
Klarth10mo ago
App.axaml.cs is not a great place for error-handling. There are mechanisms you can use, but if you expect to need any error handling, I'd move the call into the VM or View. I'm not sure what they actually do, but I'd put the processing into a service class.
Mekasu0124
Mekasu0124OP10mo ago
ok thanks 🙂 With this, do you mean to do like
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);

var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>()
.AddSingleton<MainViewModel>()
.AddSingleton<LoginViewModel>()
+ .AddSingleton<Database>()
+ .AddSingleton<GithubService>()
+ .AddSingleton<Helpers>();

var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);

desktop.MainWindow = new MainWindow
{
DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>(),
};
}

base.OnFrameworkInitializationCompleted();
}
}
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
BindingPlugins.DataValidators.RemoveAt(0);

var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>()
.AddSingleton<MainViewModel>()
.AddSingleton<LoginViewModel>()
+ .AddSingleton<Database>()
+ .AddSingleton<GithubService>()
+ .AddSingleton<Helpers>();

var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);

desktop.MainWindow = new MainWindow
{
DataContext = Ioc.Default.GetRequiredService<MainWindowViewModel>(),
};
}

base.OnFrameworkInitializationCompleted();
}
}
Klarth
Klarth10mo ago
Sure. Those might not need to be singleton lifetime scoped though. Depends.
Mekasu0124
Mekasu0124OP10mo ago
ok what would be the better option? like I wanna understand. This is as far as I've gotten with trying to understand your message there. I do apologize
Klarth
Klarth10mo ago
AddTransient exists for services, etc that don't need to be singleton.
Mekasu0124
Mekasu0124OP10mo ago
So something like this?
var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>();
.AddSingleton<MainViewModel>()
.AddSingleton<LoginViewModel>()
.AddSingleton<UpdateAvailableViewModel>()
.AddTransient(GithubService) // or .AddTransient(gvs) and initialize gvs before var services
.AddTransient(Database) // or .AddTransient(db) and " "
.AddTransient(Helpers) // or .AddTransient(hp) and " "
var services = new ServiceCollection();
services.AddSingleton<MainWindowViewModel>();
.AddSingleton<MainViewModel>()
.AddSingleton<LoginViewModel>()
.AddSingleton<UpdateAvailableViewModel>()
.AddTransient(GithubService) // or .AddTransient(gvs) and initialize gvs before var services
.AddTransient(Database) // or .AddTransient(db) and " "
.AddTransient(Helpers) // or .AddTransient(hp) and " "
or
GithubService gvs = new();
...
.AddTransient(gvs);
GithubService gvs = new();
...
.AddTransient(gvs);
Klarth
Klarth10mo ago
.AddTransient<GithubService>() though you don't have to chain everything together.
Mekasu0124
Mekasu0124OP10mo ago
is it better to chain or worse?
Klarth
Klarth10mo ago
Doesn't really matter. I tend to separate for readability.
Mekasu0124
Mekasu0124OP10mo ago
ok so by adding them as transients, does that allow me the ability to access those instances in a ViewModel maybe by passing it through the parameters? Like
.AddTransient<GithubService>();

public partial class MainWindowViewModel : ViewModelBase
{
private GithubService _gvs;

public MainWindowViewModel(GithubService gvs)
{
_gvs = gvs;
}
}
.AddTransient<GithubService>();

public partial class MainWindowViewModel : ViewModelBase
{
private GithubService _gvs;

public MainWindowViewModel(GithubService gvs)
{
_gvs = gvs;
}
}
Klarth
Klarth10mo ago
No, the service registration in general does that. Singleton/Transient/Scoped defines which instance you get.
Mekasu0124
Mekasu0124OP10mo ago
ok so then how do I access it in the view model? or pass it to the vm
Klarth
Klarth10mo ago
Like how you're doing above.
Mekasu0124
Mekasu0124OP10mo ago
ok and it just makes the bridge and connects the two together cool. tyvm
Klarth
Klarth10mo ago
As long as you don't have a circular dependency, sure. "The two" is a bit misleading. If you inject ViewModelA registered as transient into ViewModelB and ViewModelC, you've created two separate instances of ViewModelA. Which is important if you're expecting the same instance to be shared.
Mekasu0124
Mekasu0124OP10mo ago
oh ok. that makes sense
[RelayCommand] public async Task Login()
{
string _savedPassword = _db.GetPassword();

if (Password != _savedPassword)
{
IsVisible = true;
}

await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
[RelayCommand] public async Task Login()
{
string _savedPassword = _db.GetPassword();

if (Password != _savedPassword)
{
IsVisible = true;
}

await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
when the user clicks the Login button, I want it to check if the user's input matches what's saved in the database. If it does, I'll write a Message.Send(), but if it doesn't then I want it to change the visibility status of an error label that'll be on the screen, delay 3 seconds and then reset the screen. I'm having a bit of problem getting that logic
[RelayCommand] public async Task Login()
{
string _savedPassword = _db.GetPassword(UserName);

if (Password != _savedPassword)
{
IsVisible = true;
await Task.Delay(3000);
UserName = "";
Password = "";
}
else
{
await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
}
[RelayCommand] public async Task Login()
{
string _savedPassword = _db.GetPassword(UserName);

if (Password != _savedPassword)
{
IsVisible = true;
await Task.Delay(3000);
UserName = "";
Password = "";
}
else
{
await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
}
something like this maybe?
Klarth
Klarth10mo ago
It's tricky because there's concurrency enabled. If the user starts typing in another try during the 3s delay, their fields will get reset if they're too slow. You can try using the command cancellation https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/generators/relaycommand#cancel-commands-for-asynchronous-operations inside of OnPasswordChanged and OnUserNameChanged. Or you can use [RelayCommand(AllowConcurrentExecutions = false)], but the delay will be a bit long. This will disable the button. I would immediately clear whatever you want to clear instead of messing up the user mid-typing. eg. Just clearing the password immediately.
Mekasu0124
Mekasu0124OP10mo ago
and I do that by just the simple Password = ""; right
[RelayCommand(AllowConcurrentExecutions = false)] public async Task Login()
{
string _savedPassword = _db.GetPassword(UserName);

if (Password != _savedPassword)
{
UserName = "";
Password = "";
IsVisible = true;
await Task.Delay(3000);
IsVisible = false;
}
else
{
await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
}
[RelayCommand(AllowConcurrentExecutions = false)] public async Task Login()
{
string _savedPassword = _db.GetPassword(UserName);

if (Password != _savedPassword)
{
UserName = "";
Password = "";
IsVisible = true;
await Task.Delay(3000);
IsVisible = false;
}
else
{
await Task.Delay(50);
Messenger.Send(new UserLoggedInMessage(UserName, Password));
}
}
how about this?
Klarth
Klarth10mo ago
That works. You'll need to see if it's the UX you want though. The delay is quite long because it blocks.
Mekasu0124
Mekasu0124OP10mo ago
ok. I'll check that out when I test, ty
internal UserModel GetUser(string username)
{
using SQLiteConnection conn = new(_dbFile);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = @"SELECT * FROM users WHERE Username=$un";
cmd.Parameters.AddWithValue("$un", username);

UserModel ReturnUser = new();

try
{
reader = cmd.ExecuteReader();

while (reader.Read())
{
ReturnUser = new()
{
Id = Convert.ToInt32(reader["Id"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
Email = reader["email"].ToString(),
Username = reader["Username"].ToString(),
Password = reader["Password"].ToString(),
UserSettings = new UserSettingsModel()
{
Styles = new StylesModel()
{
FontName = reader["FontName"].ToString()
},
Settings = new SettingsModel()
{

}
}
};
}

return ReturnUser;
}
catch (SQLiteException ex)
{
return ReturnUser;
}
}
internal UserModel GetUser(string username)
{
using SQLiteConnection conn = new(_dbFile);
using SQLiteCommand cmd = conn.CreateCommand();
SQLiteDataReader reader;

conn.Open();

cmd.CommandText = @"SELECT * FROM users WHERE Username=$un";
cmd.Parameters.AddWithValue("$un", username);

UserModel ReturnUser = new();

try
{
reader = cmd.ExecuteReader();

while (reader.Read())
{
ReturnUser = new()
{
Id = Convert.ToInt32(reader["Id"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
Email = reader["email"].ToString(),
Username = reader["Username"].ToString(),
Password = reader["Password"].ToString(),
UserSettings = new UserSettingsModel()
{
Styles = new StylesModel()
{
FontName = reader["FontName"].ToString()
},
Settings = new SettingsModel()
{

}
}
};
}

return ReturnUser;
}
catch (SQLiteException ex)
{
return ReturnUser;
}
}
Inside of the returning UserModel, there is UserModel.UserSettings. The two nested models inside of UserSettings are supposed to be information pulled from the styles.json file. I did have that in a helper function but I was wondering if it would be appropriate to do it here instead so that it all gets queried and returned only once
Klarth
Klarth10mo ago
If you aren't intending on having styles.json be hand-edited, I would probably store the JSON in the DB. I would also use EF Core instead of ADO.NET. Or at least Dapper if you want to write SQL.
Mekasu0124
Mekasu0124OP10mo ago
ok then I'll just store it all in the database under the user's table or I may create another table for the user's settings
Klarth
Klarth10mo ago
Some DBs have JSON columns. Don't know if SQLite has them as I don't use the feature.
Mekasu0124
Mekasu0124OP10mo ago
ok np 🙂 whew this is a bunch of stuff to learn lol Like I'm trying not to go back to RxUI lol
Klarth
Klarth10mo ago
Nearly 100% of this stuff you would've needed to do for a RxUI app anyways. Only a small part is Mvvm Toolkit-specific.
Mekasu0124
Mekasu0124OP10mo ago
I got you. I have another question
internal void CreateDatabase()
{
using SQLiteConnection conn = new(_dbFile);
using SQLiteCommand cmd = conn.CreateCommand();
}
internal void CreateDatabase()
{
using SQLiteConnection conn = new(_dbFile);
using SQLiteCommand cmd = conn.CreateCommand();
}
Is there a way to possibly get away with doing something like
public class Database
{
private static readonly string _dbFile = "Data Source=main.db";
using private SQLiteConnection conn;
using private SQLiteCommand cmd;

internal void CreateDatabase()
{
conn = new(_dbFile);
cmd = conn.CreateCommand();
}
}
public class Database
{
private static readonly string _dbFile = "Data Source=main.db";
using private SQLiteConnection conn;
using private SQLiteCommand cmd;

internal void CreateDatabase()
{
conn = new(_dbFile);
cmd = conn.CreateCommand();
}
}
??? and when I use the connection, I can close it at the end of the function, and when I insert/update/delete I can use someting like a conn.Comit() and then a conn.Close() at the end of my functions
Klarth
Klarth10mo ago
I'm not sure that actually buys you much. But no, the second is not possible. If you cache conn/cmd in a private field, then you should implement IDisposable and take care of them there. Then the caller is responsible for (using var db = Database.CreateDatabase()) or whatever (which should probably be a static factory method). I find the approach pretty awkward, but I'm more used to working with EF Core. Outside of EF Core, I'm used to Dapper and something like this: https://github.com/JustAHack/TimCoRetailManager/blob/main/TRMDataManager.Library/Internal/DataAccess/SQLDataAccess.cs But this is for stored procedures rather than in-code SQL.
Mekasu0124
Mekasu0124OP10mo ago
oh ok. I'll just forget that for now lol
Klarth
Klarth10mo ago
Using raw ADO.NET isn't for convenience. It's what EF Core and Dapper build on top of. You can abstract some of it, but you're bound to make some bad decisions.
Mekasu0124
Mekasu0124OP10mo ago
right right. so another question then. With my websites that I doodle around with I use a FastAPI for the server-side. Is it possible to use FastAPI with Avalonia? If so, how do I do it? I can't find any tutorials or StackOverflows on google
Klarth
Klarth10mo ago
A web API is a web API.
Mekasu0124
Mekasu0124OP10mo ago
right, but in the client-side of my application, I use JS.
import axios from 'axios';

const api = axios.create({
baseURL: "http://127.0.0.1:8000"
});

export default api;
import axios from 'axios';

const api = axios.create({
baseURL: "http://127.0.0.1:8000"
});

export default api;
I create and export an API that knows where to call, and then I import api into the pages that use it and go from there
Klarth
Klarth10mo ago
You use whatever libraries C# / .NET has that can make http requests. HttpClient, Refit, or some others. No, you use axios.
Mekasu0124
Mekasu0124OP10mo ago
well that. my fault. wrong name
Klarth
Klarth10mo ago
HttpClient and Refit fill the same role with varying levels of (in)convenience.
Mekasu0124
Mekasu0124OP10mo ago
so you said HttpClient. So I would use that like I do in my installer when getting the latest release from a github repo
Klarth
Klarth10mo ago
Sure, but it's significantly less convenient than Refit.
Mekasu0124
Mekasu0124OP10mo ago
ok I'll look at Refit. Thanks ok so once more. If I create a new project in my current window
Mekasu0124
Mekasu0124OP10mo ago
No description
Mekasu0124
Mekasu0124OP10mo ago
nvm. I know the answer I'm looking for
<Application.Resources>
<FontFamily x:Key="FontFamily">/Fonts/ShadowsIntoLight-Regular#ShadowsIntoLight-Regular</FontFamily>
</Application.Resources>
<Application.Resources>
<FontFamily x:Key="FontFamily">/Fonts/ShadowsIntoLight-Regular#ShadowsIntoLight-Regular</FontFamily>
</Application.Resources>
I created a custom font in my App.axaml file. In my view page, I called to that x:Key
<UserControl.Styles>
<Style Selector="Label">
<Setter Property="FontFamily" Value="{StaticResource FontFamily}" />
</Style>
</UserControl.Styles>
<UserControl.Styles>
<Style Selector="Label">
<Setter Property="FontFamily" Value="{StaticResource FontFamily}" />
</Style>
</UserControl.Styles>
however I keep getting the error Could not create glyph Typeface and I'm trying to follow their example here on using a custome font, but I think I'm doing it wrong. In my solution explorer, I have
Diary
diary.sln
- Diary/
- App.axaml
-Fonts/
- ShadowsIntoLight-Regular.ttf
Diary
diary.sln
- Diary/
- App.axaml
-Fonts/
- ShadowsIntoLight-Regular.ttf
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-fonts
Klarth
Klarth10mo ago
You need to make sure its build action is an AvaloniaResource. Could try using the avares: URI, too. Besides that, font imports are a bit of a pain. There are a few issues specific to that in the past months, but no time to work on them.
Mekasu0124
Mekasu0124OP10mo ago
ok thank you ok I appreciate all the help you've given me, but unfortunately this is just a bit too big of a concept for me to chew at this present moment so I'm going to revert back to ReactiveUI. Thank you for the help ❤️
Want results from more Discord servers?
Add your server