1

I'm using .NET 9, WinUI 3, CommTk.mvvm, Microsoft DI.

I have 3 projects in the solution:

  • WinUI 3 (the interface)
  • WinUI 3 class library (user controls to be used in multiple WinUI 3 projects)
  • C# class library (view models, other C# classes)

It is unclear to me how I can use DI in the Winui3 class library user controls.

Many of the controls need a DataAccessUtil injected to retrieve values from the database for combobox items.

3
  • I forgot to mention, my UserControls must have a parameterless constructor, so no constructor injection. That is my problem. Commented Jul 21 at 14:48
  • One of the approaches is to inject needed dependencies in the method as params. The other way would be to have some ambient service provider. Commented Jul 22 at 6:30
  • @MichałTurczyn I have considered that. Not real crazy about the idea. Thank you for your input. Commented Jul 22 at 13:03

2 Answers 2

-1

WinUI controls (user controls, pages etc) are expected to have a parameterless constructor.

The standard way to use DI in the UI side (user controls, pages, windows) etc. is to initialize the dependencies via static access to the service provider (see this example).

This boils down to WinUI's Frame.Navigate(Type sourcePageType, object parameter) which doesn't natively support the standard MS DI.
This feature has been requested here, please vote!).

However there are a few solutions, one of them mentioned in this comment - WinUI.DependencyInjection.

Another option is to create an additional constructor that takes all dependencies and delegate the parameterless constructor to it (I'm not sure but a private constructor is maybe supported by WinUI):

public class MyUserControl
{
    public MyUserControl() // check if private works
        this(App.GetService<IMyServiceA>(), App.GetService<IMyServiceB>())
    {
    }    

    public MyUserControl(IMyServiceA serviceA, IMyServiceB serviceB)
    {
    }
}
public partial App : Application
{
    public IHost Host { get; }

    public static T GetService<T>() where T : notnull =>
        ((App)Current).Host.Services.GetRequiredService<T>();
}

This is at least helpful in organizing the initialization stuff in one spot instead of scattering them around through the file. In addition enables you in easily mocking and substituting those dependencies in your tests.

Sign up to request clarification or add additional context in comments.

4 Comments

thank you for the links. I am reading over them. My controls class lib does not have a ref to the main app and it's static di. Might just put the controls back in the main winui project.
You can create a shared project which exposed all those interfaces, while they're implemented and registered to the service provider. I've updated my answer with additional content.
Yes, I have that very static method in my App.xaml.cs file. But the Shared Project with UserControls does not have re project reference to the Main project. The main project has a reference to the UserControls project, so usercontrols cant call App.getservice
for some reason I'm unable to reply to your other comment, but you can create an additional static class called App with a Get service method in the user control library and lazily load the service provider in there.
-1

IMHO, your user controls shouldn't depend directly on DataAccessUtil. Instead, expose relevant properties as DependencyProperty, and inject services, which can access DataAccessUtil, into view models.

The sample code below uses the CommunityToolkit.Mvvm NuGet package for MVVM but should help clarify what I mean.

AwesomeUserControl

<UserControl
    x:Class="WinUIDemoApp.AwesomeUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:WinUIDemoApp"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid>
        <ComboBox x:Name="AwesomeComboBox" ItemsSource="{x:Bind ComboBoxOptions, Mode=OneWay}" />
    </Grid>
</UserControl>
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace WinUIDemoApp;

public sealed partial class AwesomeUserControl : UserControl
{
    public static readonly DependencyProperty ComboBoxOptionsProperty =
        DependencyProperty.Register(
            nameof(ComboBoxOptions),
            typeof(object),
            typeof(AwesomeUserControl),
            new PropertyMetadata(default));

    public AwesomeUserControl()
    {
        InitializeComponent();
    }

    public object ComboBoxOptions
    {
        get => (object)GetValue(ComboBoxOptionsProperty);
        set => SetValue(ComboBoxOptionsProperty, value);
    }
}

ComboBoxOptionsService

public interface IComboBoxOptionsService
{
    string[] GetComboBoxOptions();
}
public class ComboBoxOptionsService : IComboBoxOptionsService
{
    public string[] GetComboBoxOptions()
    {
        // Replace this demo code with options from DataAccessUtil.
        int item1 = Random.Shared.Next(0, 10);
        return
        [
            $"Option {item1}",
            $"Option {item1 + 1}",
            $"Option {item1 + 2}",
        ];
    }
}

AwesomeViewModel

public partial class ShellViewModel : ObservableObject
{
    private readonly IComboBoxOptionsService _comboBoxOptionsService;

    public ShellViewModel(IComboBoxOptionsService comboBoxOptionsService)
    {
        this._comboBoxOptionsService = comboBoxOptionsService;
        ComboBoxOptions = _comboBoxOptionsService.GetComboBoxOptions();
    }

    [ObservableProperty]
    public partial string[] ComboBoxOptions { get; set; }
}

AwesomePage

<local:AwesomeUserControl ComboBoxOptions="{x:Bind ViewModel.ComboBoxOptions, Mode=OneWay}" />
public sealed partial class Shell : Page
{
    public Shell()
    {
        InitializeComponent();
    }

    public AwesomeViewModel ViewModel { get; } = App.GetService<AwesomeViewModel>();
}

1 Comment

Damn Andrew. Very interesting approach. Lemme see about testing your solution out. Back in a bit. Thank you for your great idea and going to the effort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.