C
C#5mo ago
M.

✅ Issue with Triggering View Change in Avalonia using C#

Hello 👋, I'm facing an issue in C# with Avalonia, and I could use some help. My current code allows me to change the view (usercontrol) in my MainWindow.axaml, but I'm having difficulties triggering this change from a button inside another view (usercontrol), (
MainWindow.Instance.NavigateTo<ExampleView>();
MainWindow.Instance.NavigateTo<ExampleView>();
). Could someone help me troubleshoot this problem? Part of my code in MainWindow.axaml.cs related to the view change :
c#
public static MainWindow Instance { get; private set; }

public MainWindow()
{
InitializeComponent();
Instance = this;
}
public void NavigateTo<T>() where T : UserControl, new()
{
contentControl.Content = new T();
}
c#
public static MainWindow Instance { get; private set; }

public MainWindow()
{
InitializeComponent();
Instance = this;
}
public void NavigateTo<T>() where T : UserControl, new()
{
contentControl.Content = new T();
}
Relevant code for this from my MainWindow.xaml :
<ContentControl Name="contentControl"/>
<ContentControl Name="contentControl"/>
I'm not entirely sure where the issue is coming from because it was working before. I'm not sure if it's related, but this happened after an update to Avalonia and the libraries in my project. I can't say for certain if that's the cause because I haven't touched the code or the software after doing that update If my issue isn't clear, I can try to explain it better and even create a 'diagram' if needed
14 Replies
Sir Rufo
Sir Rufo5mo ago
GitHub
GitHub - SirRufo/so-77657479-PageNavigation: Example for stackoverflow
Example for stackoverflow. Contribute to SirRufo/so-77657479-PageNavigation development by creating an account on GitHub.
M.
M.5mo ago
Thank you, I'll look into that right away. Sorry, but that doesn't help me much.
Sir Rufo
Sir Rufo5mo ago
Why not?
M.
M.5mo ago
Because my issue is simply that I can't change the displayed user control from another user control that is already displayed. If I click on buttons that change the user control directly from the main window, it works perfectly.
Sir Rufo
Sir Rufo5mo ago
Yes, and my project shows you how to solve that issue
M.
M.5mo ago
Oh, sorry. Do you have the part of the code that corresponds to what I want to do, or it's okay, I'll look for it.
Sir Rufo
Sir Rufo5mo ago
In the folder Navigation, but it is not a ready to use solution for you but it should put you on the right track how to solve it
M.
M.5mo ago
Thank you for everything, I will try to implement that. Hi, So I took an example from your code, and I just have two errors that I can't resolve in my AvaloniaNavigationService.cs - Cannot resolve symbol 'MainWindow' | Line : var window = (Window)Avalonia.Application.Current.MainWindow; - Cannot resolve symbol 'IQueryAttributable' | Line: if (instance is IQueryAttributable attributable) - 📂 Navigation - AvaloniaNavigationService.cs - INavigationService.cs - QueryPropertyAttribute.cs - Routing.cs My codes : AvaloniaNavigationService.cs
namespace AvaloniaApp.Navigation
{
internal class AvaloniaNavigationService : INavigationService
{
private readonly IServiceProvider _services;
private UserControl? _view;
private bool _clearNavigationStack;

public AvaloniaNavigationService(IServiceProvider services)
{
_services = services;
}

public Task NavigateToAsync(string route, IDictionary<string, object>? parameters = null)
=> OnNavigateAsync(route, parameters);

private async Task OnNavigateAsync(string route, IDictionary<string, object>? parameters = null)
{
await Task.Yield();

var window = (Window)Avalonia.Application.Current.Windows[0]; // Use the appropriate index for your main window
bool clearNavigationStack = route.StartsWith("//") ? _clearNavigationStack = true : false;

var view = Routing.GetOrCreateContent(route.Substring(2), _services);
if (parameters != null) ApplyQueryParameters(view, parameters);

window.Content = _view = (UserControl)view;
_clearNavigationStack = clearNavigationStack;
}

private void ApplyQueryParameters(object instance, IDictionary<string, object> parameters)
{
var t = instance.GetType();

foreach (var attr in t.GetCustomAttributes<QueryPropertyAttribute>())
{
if (parameters.TryGetValue(attr.QueryId, out var value))
{
var prop = t.GetProperty(attr.Name);
if (prop != null && prop.CanWrite && prop.PropertyType.IsAssignableFrom(value.GetType()))
prop.SetValue(instance, value);
}
}

if (instance is IQueryAttributable attributable)
attributable.ApplyQueryAttributes(parameters);
}
}
}
namespace AvaloniaApp.Navigation
{
internal class AvaloniaNavigationService : INavigationService
{
private readonly IServiceProvider _services;
private UserControl? _view;
private bool _clearNavigationStack;

public AvaloniaNavigationService(IServiceProvider services)
{
_services = services;
}

public Task NavigateToAsync(string route, IDictionary<string, object>? parameters = null)
=> OnNavigateAsync(route, parameters);

private async Task OnNavigateAsync(string route, IDictionary<string, object>? parameters = null)
{
await Task.Yield();

var window = (Window)Avalonia.Application.Current.Windows[0]; // Use the appropriate index for your main window
bool clearNavigationStack = route.StartsWith("//") ? _clearNavigationStack = true : false;

var view = Routing.GetOrCreateContent(route.Substring(2), _services);
if (parameters != null) ApplyQueryParameters(view, parameters);

window.Content = _view = (UserControl)view;
_clearNavigationStack = clearNavigationStack;
}

private void ApplyQueryParameters(object instance, IDictionary<string, object> parameters)
{
var t = instance.GetType();

foreach (var attr in t.GetCustomAttributes<QueryPropertyAttribute>())
{
if (parameters.TryGetValue(attr.QueryId, out var value))
{
var prop = t.GetProperty(attr.Name);
if (prop != null && prop.CanWrite && prop.PropertyType.IsAssignableFrom(value.GetType()))
prop.SetValue(instance, value);
}
}

if (instance is IQueryAttributable attributable)
attributable.ApplyQueryAttributes(parameters);
}
}
}
INavigationService.cs
c#
namespace AvaloniaApp.Navigation
{
public interface INavigationService
{
Task NavigateToAsync(string route, IDictionary<string, object>? parameters = null);
}
}
c#
namespace AvaloniaApp.Navigation
{
public interface INavigationService
{
Task NavigateToAsync(string route, IDictionary<string, object>? parameters = null);
}
}
QueryPropertyAttribute.cs
c#
namespace AvaloniaApp.Navigation{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class QueryPropertyAttribute : Attribute
{
public QueryPropertyAttribute(string name, string queryId) => (Name, QueryId) = (name, queryId);
public string Name { get; }
public string QueryId { get; }
}
}
c#
namespace AvaloniaApp.Navigation{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class QueryPropertyAttribute : Attribute
{
public QueryPropertyAttribute(string name, string queryId) => (Name, QueryId) = (name, queryId);
public string Name { get; }
public string QueryId { get; }
}
}
Routing.cs
c#
namespace AvaloniaApp.Navigation
{
public static class Routing
{
private static readonly Dictionary<string, Type> _registeredRoutes = new Dictionary<string, Type>();

public static UserControl GetOrCreateContent(string route, IServiceProvider serviceProvider) =>
_registeredRoutes.TryGetValue(route, out var type) ?
serviceProvider.GetService(type) as UserControl ?? throw new InvalidOperationException("") :
throw new InvalidOperationException("");

public static void RegisterRoute(string route, Type type)
{
if (string.IsNullOrEmpty(route) || !type.IsAssignableTo(typeof(UserControl)))
throw new ArgumentException();

_registeredRoutes.Add(route, type);
}
}
}
c#
namespace AvaloniaApp.Navigation
{
public static class Routing
{
private static readonly Dictionary<string, Type> _registeredRoutes = new Dictionary<string, Type>();

public static UserControl GetOrCreateContent(string route, IServiceProvider serviceProvider) =>
_registeredRoutes.TryGetValue(route, out var type) ?
serviceProvider.GetService(type) as UserControl ?? throw new InvalidOperationException("") :
throw new InvalidOperationException("");

public static void RegisterRoute(string route, Type type)
{
if (string.IsNullOrEmpty(route) || !type.IsAssignableTo(typeof(UserControl)))
throw new ArgumentException();

_registeredRoutes.Add(route, type);
}
}
}
MainWindow.axaml.cs
c#
namespace AvaloniaApp.Views
{
public partial class MainWindow : Window
{
static MainWindow Instance { get; private set; }
private readonly INavigationService _navigationService;
public MainWindow()
{
InitializeComponent();
Instance = this;
_navigationService = new AvaloniaNavigationService(Locator.Current.GetService<IServiceProvider>());

NavigateTo<HomeView>();}
private void InitializeComponents()
{
AvaloniaXamlLoader.Load(this);
}
private void NavigateTo<T>() where T : UserControl, new()
{
_navigationService.NavigateToAsync(typeof(T).Name);
}
}
c#
namespace AvaloniaApp.Views
{
public partial class MainWindow : Window
{
static MainWindow Instance { get; private set; }
private readonly INavigationService _navigationService;
public MainWindow()
{
InitializeComponent();
Instance = this;
_navigationService = new AvaloniaNavigationService(Locator.Current.GetService<IServiceProvider>());

NavigateTo<HomeView>();}
private void InitializeComponents()
{
AvaloniaXamlLoader.Load(this);
}
private void NavigateTo<T>() where T : UserControl, new()
{
_navigationService.NavigateToAsync(typeof(T).Name);
}
}
Sir Rufo
Sir Rufo5mo ago
GitHub
so-77657479-PageNavigation/WpfApp/Navigation/IQueryAttributable.cs ...
Example for stackoverflow. Contribute to SirRufo/so-77657479-PageNavigation development by creating an account on GitHub.
Sir Rufo
Sir Rufo5mo ago
A quick search for avalonia get mainwindow shows https://github.com/AvaloniaUI/Avalonia/issues/3418#issuecomment-732779247
GitHub
Where is Application.Current.MainWindow ? · Issue #3418 · AvaloniaU...
I'm upgrading from 8.0 to 9.0 and MainWindow property from Application.Current no longer exist. Why has it been removed and what's the replacement? For me it was an easy way to show dialogs...
M.
M.5mo ago
Thank you for your help, I succeeded and it works perfectly
Sir Rufo
Sir Rufo5mo ago
$close
MODiX
MODiX5mo ago
Use the /close command to mark a forum thread as answered
Sir Rufo
Sir Rufo5mo ago
Ihope you noticed that you can not only navigate but also can send values to the navigation target declared by attributes or implementing the IQueryAttributable 😉