As you probably know by now I love event aggregation and being able to seamless forward server side events to clients. My library SignalR.EventAggregatorProxy have been around for a while now and it enables seamless event aggregation between server and client. The .NET core client library just got updated to .NET 5. And as it turns out it works without any effort in Blazor WASM.
(more…)UI
Build server – client contracts with dotnet CLI
Now days type safety is common within the web-world with TypeScript and on the horizon WebAssembly with .NET Core (Blazor etc). I have for a long time advocated for the importance of this, especially when we are talking the contract between server and client. For example this T4 template that spits out C# CQS types as javascript.
T4 doesn’t play well with Dot Net Core, but we now have the dotnet CLI toolset we can use instead.
(more…)
MVC Custom errors, HTTP status codes and SecurityException
We use Windows Identity Foundation in my current project. WIF introduced the Claim based authorization in .NET 4.5 which is a welcomed addition since we were forced to use outdated tools like EnterpriseLibraries Rule authorization to achieve something similar before it.
Whenever a claim is not met WIF will throw a System.Security.SecurityException exception which will not play nicely with MVC Custom errors because it will render a status 500 instead of the correct status 403. This is not very user friendly since the user will think something is wrong instead of understanding that he or she needs to order additional privileges to access said part of the system.
The solution is really simple and I did not find it when googling, thus this blog post 😀 (more…)
Seperate KO View formating and ViewModel state
A normal scenario with Knockout is that you want date and number observables to be formatted to the current culture of the client, this is easily implemented in a Knockout extender, something like.
ko.extenders.numeric = function(observable, options) { var textValue = null; return ko.computed({ write: function(value) { textValue = value; var parsed = Number.parseFloat(value); if(!isNaN(parsed)) { observable(parsed); } else { observable(null); } }, read: function() { var value = observable(); if(value == null) { return textValue; } return value.toLocaleString(); //This can be replaced with for example the Globalize plugin } }); };
Use it like
this.amount = ko.observable().extend({ numeric: true });
The problem with this approach is that the ViewModel will no longer work against numbers it will work against formatted strings, so this.amount() will return a string instead of a number. This is very bad practice in a View/ViewModel seperation stand point. (more…)
Single Page Application with Sub-routing
One of my colleagues is working on a Single Page Application, he asked me for help on a solid design for routing and specifically about sub-routing. Sub-routing in a SPA is complex, as an example when you click a link in a list you want that item to be presented in a modal window. If you close the modal window and press back in the history you want it to show again, and if you press forward you want it to close. I choose to attack this problem with an Event Aggregation approach, where changes to the route resulted in a change event being fired to any listener. (more…)
Wrap client side code in a Web Forms Control
I’m currently between assignments and in the meantime I’m helping fellow colleagues with a Web Forms system. They needed a Combobox for a requirement and after some googling I found out that there is none for free which suits their needs. I have created a Knockout enabled combo called Knockout.Combobox. I decided to take this client side combobox and wrap it in a Web Forms Control.
(more…)
MVVM with Excel and Caliburn.Micro
One of my colleagues needed to modernize a tool we had for Excel. He asked me for suggestions, since he knew I’m into modern UI development. I know very little about the Excel API but after some googling I found out that you are limited to Winforms. There is however a control in Winforms called ElementHost that let you host WPF elements in Winforms. This coupled with a custom Bootstrapper for CM will enable you to develop sleek MVVM tools in Excel.
The Boostrapper will act as an entry point for our Caliburn enabled app. It needs to inherit from BootstrapperBase rather than the standard generic base class Bootstrapper of TModel.
public class Bootstrapper<TModel> : BootstrapperBase { private readonly StandardKernel kernel; public Bootstrapper(ElementHost host) : base(false) { kernel = new StandardKernel(); Start(); var model = kernel.Get<TModel>(); var view = ViewLocator.LocateForModel(model, null, null); ViewModelBinder.Bind(model, view, null); host.Child = view; } protected override void Configure() { kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope(); } protected override object GetInstance(Type service, string key) { return kernel.Get(service); } protected override IEnumerable<object> GetAllInstances(Type service) { return kernel.GetAll(service); } }
Its constructor takes the ElementHost as argument and then starts CM using the Start method on the base class. We need to hook up the ViewModel manually and its done like.
var model = kernel.Get<TModel>(); var view = ViewLocator.LocateForModel(model, null, null); ViewModelBinder.Bind(model, view, null);
First we create the ViewModel using Ninject, this way any dependecies for that model will be injected by Ninject. Then we ask CM to locate the WPF view using its convention based helper class ViewLocator. After we get the view we Bind it to the model using another CM helper class called ViewModelBinder.
Last but not least, we need to hook up the WPF element to the Winforms ElementHost.
host.Child = view;
Embedded resources
Alan Rutter asked me how to load embedded resources like themes etc.
App.xaml wont load in a none WPF project so you need to-do this manually like
var dict = new ResourceDictionary {Source = new Uri("Resources.xaml", UriKind.Relative)}; var app = new System.Windows.Application(); //This will load Application.Current Application.Current.Resources.MergedDictionaries.Add(dict);