Last post we integrated the steps I had laid out to utilize the plugin architecture with WPF. Up next is applying the same steps to Blazor. But the communication between the client and the back-end is dependent on IMessenger from the CommunityToolkit.Mvvm. How do we use that in Blazor?

As always all code is available in the Github project.

I had started down a path of trying to figure out how to add MVVM to Blazor. I mean, at its core it’s a design pattern. Given that CommunityToolkit.Mvvm successfully implements a framework for easily integrating the MVVM pattern, it shouldn’t be too hard. Except… it is, it really is kind of a pain. Also I was trying to re-invent the wheel. There was some great work done by Kelly Adams on his blazor-mvvm project. This was followed up by Graeme_Grant and his Blazing.Mvvm project. There is also a whole separate project by Christian Klemm titled MvvmBlazor that is a bit of a more light-weight approach to MVVM integration into Blazor.

They both look like great projects but they provide approaches that are beyond the scope of what I was attempting to explain here, how to add IMessenger to Blazor. So I started to try and strip each of them down to a simplified MVVM architecture that would fit into easily digestible bytes like what I post here. I would still be working on that approach if I didn’t come to the realization that I didn’t need to be so thorough. The only thing we need is to be able to use IMessenger in Blazor.

IMessenger is a feature that is included with CommunityToolkit.Mvvm but it’s more an add-on, not directly connected to MVVM. It does facilitate the MVVM pattern and it certainly fits with that library as a tool to make MVVM integration and communication easier to understand and implement but it’s completely stand-alone. So I’m going to take the KISS approach (Keep it simple silly 🙂 and just use the IMessenger.

To start with I added the basic, run-of-the-mill Blazor project and added a new Chatter page.

To my Chatter.razor I added the @page attribute to provide a url:

@page "/chat"

To my NavMenu.razor I removed all the boilerplate stuff except home and added my new page:

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="chat">
                <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Chat
            </NavLink>
        </div>

Okay, basics are in place. I can navigate to the page, even if there isn’t anything there yet.

The next step is to modify my build steps on the plugin projects. When I did the original changes to build for the WPF app that worked okay, but it was very limiting. Now I need to support multiple projects so I need to copy to multiple projects when building. This step isn’t strictly necessary but I wanted to so I didn’t have to keep moving around files if I ever changed the plugins.

The problem with using the properties page for a project when editing pre and post builds is that they only represent a single execute command. But we want to do this step for multiple end-point projects. And each plugin project needs to do it.

The steps for each project look like:

  1. PreBuild
    1. Make plugin dir for WPF
    2. Make plugin dir for Blazor
  2. PostBuild
    1. Remove existing directory from plugins for project
    2. Make directory in plugins for project
    3. Copy all the files needed to project directory

To accomplish this I edited the project file for each of the plugin handlers and changed the PreBuild and PostBuild tags to:

  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="rmdir /q /s "$(SolutionDir)Chatter\bin\$(Configuration)\net8.0-windows\Plugins\$(ProjectName)" 2> nul
mkdir "$(SolutionDir)Chatter\bin\$(Configuration)\net8.0-windows\Plugins\$(ProjectName)"
xcopy /s "$(SolutionDir)$(ProjectName)\bin\$(Configuration)\$(TargetFramework)\*.*" "$(SolutionDir)Chatter\bin\$(Configuration)\net8.0-windows\Plugins\$(ProjectName)\"" />
    <Exec Command="rmdir /q /s "$(SolutionDir)ChatterBlazor\bin\$(Configuration)\net8.0\Plugins\$(ProjectName)" 2> nul
mkdir "$(SolutionDir)ChatterBlazor\bin\$(Configuration)\net8.0\Plugins\$(ProjectName)"
xcopy /s "$(SolutionDir)$(ProjectName)\bin\$(Configuration)\$(TargetFramework)\*.*" "$(SolutionDir)ChatterBlazor\bin\$(Configuration)\net8.0\Plugins\$(ProjectName)\"" />
  </Target>
  
  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="mkdir "$(SolutionDir)Chatter\bin\$(Configuration)\net8.0-windows\Plugins"" IgnoreExitCode="true" />
    <Exec Command="mkdir "$(SolutionDir)ChatterBlazor\bin\$(Configuration)\net8.0\Plugins"" IgnoreExitCode="true" />
  </Target>

I would recommend looking at the project file on Github as html formatting is stripping out the xml characters. The commands themselves are basic command-line stuff. The important bit is the “IgnoreExitCode” attribute on the Exec tag.

Normally when running pre and post build steps Visual Studio will stop on any error. I needed this to not happen as I needed each project to be able to create the plugin directory in the respective target project since I can’t guarantee which will run first. If there already is a plugin directory command line will throw an error saying the directory already exists and the build will stop. But I don’t care if it already exists.

Up next is to add the CommunityToolkit.Mvvm nuget to the Blazor project and register the IMessenger. To my Program.cs I added:

//Add in the messenger so our back-end can access it
builder.Services.AddSingleton<WeakReferenceMessenger>();
builder.Services.AddSingleton<IMessenger, WeakReferenceMessenger>(provider => provider.GetRequiredService<WeakReferenceMessenger>());

Great, now how do I use them? In my code block of Chatter.razor I want to use the [Inject] attribute so that Blazor knows to inject from the services my IMessenger.

    [Inject] IJSRuntime JSRuntime { get; set; }
    [Inject] protected IMessenger Messenger { get; set; }

I’m also injecting the Javascript runtime here so I can force the div with chat messages to always scroll to the bottom when adding a new chat.

Now I have access to an IMessenger in my Blazor page that I can use for the plugins. Next week I’ll discuss what I do with it, loading the plugins and a more complete dive into everything that is in Chatter.razor.

Thanks,
Brian

One thought on “Using IMessenger with Blazor

Leave a Reply

Your email address will not be published. Required fields are marked *

FormatException

928 East Plymouth Drive Asbury Park, NJ 07712