I am trying to set up my application to show a different view depending on what the name of the device is. Unfortunately, I can't seem to get the View to load, though the ViewModel is getting called. I'm not entirely sure what I'm missing.
Currently, when the application loads, the only thing shows up is the MainWindow, but the View is not being loaded inside of it.
How can I get a View to load?
Here is my MainWindow.axaml:
<Window
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:TAStagingApp.ViewModels.Implementations;assembly=TAStagingApp.ViewModels"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="TAStagingApp.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Background="{StaticResource TABackgroundColor}"
Icon="/Assets/tastaging.png"
WindowStartupLocation="CenterScreen"
Title="TA Staging">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid>
<ContentPresenter Name="StagingContent"/>
</Grid>
</Window>
And the MainWindow.axaml.cs
namespace TAStagingApp.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public void SetContent(Control control)
{
var mainContent = this.FindControl<ContentPresenter>("StagingContent");
mainContent!.Content = control;
}
}
App.axaml.cs
using AvaloniaApplication = Avalonia.Application;
namespace TAStagingApp;
public partial class App : AvaloniaApplication
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
var viewModelLoader = new ViewModelLoader(new MainWindow());
ServiceLocator.Initialize(viewModelLoader);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
DataContext = GetRequiredService<IMainWindowViewModel>();
desktop.MainWindow = new MainWindow
{
DataContext = DataContext,
};
viewModelLoader.LoadContentViewBasedOnComputerName();
}
base.OnFrameworkInitializationCompleted();
}
private static T GetRequiredService<T>() => Locator.Current.GetRequiredService<T>();
}
ViewModelLoader.cs
namespace TAStagingApp.DependencyInjection.Loaders;
public sealed class ViewModelLoader(MainWindow mainWindow) : IViewModelLoader
{
private readonly MainWindow _mainWindow = mainWindow;
public void LoadContentViewBasedOnComputerName()
{
var serviceProvider = DeviceTypeHelper.GetDeviceTypeServiceProvider();
var deviceType = DeviceTypeHelper.GetDeviceType(serviceProvider);
Control contentView = deviceType.Value.ToString() switch
{
var device when device.Equals(DeviceType.Server.ToString()) => new ServerConfigureView(),
var device when device.Equals(DeviceType.Terminal.ToString()) => new TerminalConfigureView(),
var device when device.Equals(DeviceType.Kitchen.ToString()) => new TerminalConfigureView(),
_ => new AdminView()
};
_mainWindow.SetContent(contentView);
}
}
MainWindowViewModel.cs
namespace TAStagingApp.ViewModels.Implementations;
public class MainWindowViewModel : ViewModelBase, IMainWindowViewModel
{
public MainWindowViewModel(IActivateViewModel viewModel)
{
if (viewModel.GetType() == typeof(TerminalConfigureViewModel))
{
TerminalConfigureViewModel = viewModel;
}
else if (viewModel.GetType() == typeof(ServerConfigureViewModel))
{
ServerConfigureViewModel = viewModel;
}
else
{
AdminViewModel = viewModel;
}
Activate(viewModel);
}
public MainWindowViewModel()
{
}
public IActivateViewModel AdminViewModel { get; } = default!;
public IActivateViewModel TerminalConfigureViewModel { get; } = default!;
public IActivateViewModel ServerConfigureViewModel { get; } = default!;
private static void Activate(IActivateViewModel viewModel)
=> viewModel.Activate();
}
I've been looking at this for two days now and can't figure out what I'm missing.
The problem is your ViewModelLoader
is working with an instance of MainWindow
which is different from the actual
public override void OnFrameworkInitializationCompleted()
{
var viewModelLoader = new ViewModelLoader(new MainWindow()); // <<<< this window
ServiceLocator.Initialize(viewModelLoader);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
DataContext = GetRequiredService<IMainWindowViewModel>();
desktop.MainWindow = new MainWindow // <<< diffrent from this window
{
DataContext = DataContext,
};
viewModelLoader.LoadContentViewBasedOnComputerName();
}
base.OnFrameworkInitializationCompleted();
}
you can fix it by instantiating ViewModelLoader
with the application Main window
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
DataContext = GetRequiredService<IMainWindowViewModel>();
desktop.MainWindow = new MainWindow
{
DataContext = DataContext,
};
// <<< instantiate ViewModelLoader with the application MainWindow
var viewModelLoader = new ViewModelLoader(desktop.MainWindow);
ServiceLocator.Initialize(viewModelLoader);
viewModelLoader.LoadContentViewBasedOnComputerName();
}
base.OnFrameworkInitializationCompleted();
}
Actually, you should redesign your architecture because you get architecture dead look!
If I were you, I will use data templates to make my life easy!
<Window.DataTemplates>
<DataTemplate DataType="{x:Type vm:ServerConfigureViewModel}">
<view:ServerConfigureView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:TerminalConfigureViewModel}">
<view:TerminalConfigureView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:AdminViewModel}">
<view:AdminView/>
</DataTemplate>
</Window.DataTemplates>
And put in your MainViewModle
Property Called CurrentViewModel
or ActivatedViewModel
or Whatever of type ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModdel
{
get => _currentViewModel;
set => SetProperty(ref _currentViewModel, value);
}
}
All of ServerConfigureViewModel
, TerminalConfigureViewModel
and AdminViewModel
should inherit from ViewModelBase
and can be signed to CurrentViewModdel
and bind to it in main Window
<Grid>
<ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>
now if you change CurrentViewModdel
in Main Window View will change without all this code of ViewModleLocator
and this staff