I just returned from Chicago from Microsoft Ignite - the largest tech conference about Microsoft technology. Unsurprisingly, it was mainly about AI, and it was a terrific experience.

I got the chance to be part of the Expert Meetup zone. For each of the ten main areas of interest, there were several booths where the attendees could ask any questions. I was at the booth focused on Serverless in Azure, so we discussed Azure Functions, Azure Container Apps, and .NET Aspire, my favorite dev tool of recent months. I met many great people from the product groups who stand behind these technologies.

Since Ignite is not primarily a developer conference, most sessions were a bit further from my technical interest. However, being the CEO of RIGANTI, I want to understand how enterprises around the world implement AI, which use-cases are relevant to them, and what challenges they face. These insights greatly help us steer our company strategy and give better advice to our clients about integrating AI into their businesses.

I also attended an interesting session about the modernization of legacy systems, a topic similar to my recently published book. When speaking about this topic, I always specialized purely in .NET Framework applications, for this is what I meet most of the time. However, this session went much further in the past - most of it was about mainframes and ancient languages like COBOL or Smalltalk. It showed how to use AI to analyze or reverse-engineer the original intent of ancient code routines, how to extract business rules, build missing or incomplete documentation, and how to generate test cases to ensure the new code will provide the same behavior and functionality. This was a very different approach in contrast to my strictly deterministic and non-AI-based approach presented in the book, and I got plenty of new ideas.

Another very interesting session was about using Azure Kubernetes Service at scale. The team has been adding an impressive number of features, focusing on making Kubernetes easy to use and enhancing security - for example, by container vulnerability scanning and patching. I was amazed to see so many features and improvements they delivered recently. Apparently, AKS is a very popular service, and the product group has a chance to do so many interesting things.

And as always, I couldn't miss Mark Russinovich's session on Azure infrastructure. It conflicted with my Expert Meetup sessions, so I had to watch it from recording during my flight back, but it is thrilling to see the effort Microsoft puts in their data centers to make them energy efficient.

Tomas Herceg at Microsoft Ignite in front of GitHub logo

Thursday was the first day of our Update Conference Prague - the largest event for .NET developers in the Czech Republic with more than 500 attendees. On the same day, I also had an online session at .NET Confa global online .NET conference organized by Microsoft.

Originally we were thinking of setting up a studio at Update from where I could stream my session, but because I was speaking at another conference the previous week, there was not enough time for testing the setup. I am lucky that the venue where Update takes place is only about 100 meters from the RIGANTI office.

Same as last year, my .NET Conf session was about modernizing legacy applications, but this time not from the technical point of view. I focused on arguments the tech leads or developers can use to explain the risks and benefits to managers and other non-technical stakeholders.

I talked about security risks that are often seen in old libraries and frameworks, then about performance improvements of the new .NET that can translate to significantly lower infrastructure costs. I also discussed hiring and developer happiness - an important criterion in the long-term perspective. Most developers want to use the new versions to keep their knowledge up-to-date, and there is an increasingly large group of developers who started with the new .NET Core and never touched the old stuff.

Then I focused on planning and estimation, and recommended to prepare a “business plan” - evaluate not only risks coming from DOING the migration, but also from NOT DOING it. You can lead a few experiments to get the idea of complexity and make rough estimates based on the results.

The last important thing I mentioned was communication. In many cases, being predictable is way more valuable than deliver on time, for many legacy applications are only internal tools and the deadlines are often only virtual. Some developers complain that spending a few hours making a presentation about the current progress is a waste of time, and use the time to write code in a hope that the deadline will be saved. In reality, these few hours will probably not save the day, and the stakeholders will face an unexpected surprise. Clear and intensive communication can prevent this.

You can find more information on this topic in my new book Modernizing .NET Web Applications. It contains a detailed guide for migrating from ASP.NET Web Forms, ASP.NET MVC, WCF, SignalR, Entity Framework 6, and other .NET Framework technologies to their equivalents in .NET 8 and beyond, and much more.

I got several requests to publish the slide deck. You can find it below:

I’ve seen various demos on hosting Blazor applications inside MAUI apps, and I’ve been wondering about the use cases – on the first look it didn’t seem so practical to me. However, a bit later I noticed that having such thing few years ago, it would save us quite a lot of work.

At RIGANTI, we’ve been building several desktop applications for our customers. One of the application contained quite complex reservation system. The application was built using WPF as it needed to interact with various hardware devices (RFID readers, receipt printers, credit card terminals, and so on), and so the reservation calendar (a complex component) was built using WPF.

Later we found that we need a similar calendar on the web – the project has another public facing site which would take advantage of the reservation system, and thus we had to rebuild similar UI on the web. A hybrid app would help us to save a lot of time.


How to integrate web app into MAUI app

I’ve looked at the BlazorWebView control implementation and found out that it’s not complicated – basically it is a wrapper over the WebView2 component. It would be nice to offer a similar feature to DotVVM users. DotVVM apps use the MVVM pattern, same as in MAUI, so it may be even more interesting – both desktop and web UI can use the same Model-View-ViewModel approach.

The implementation in Blazor directly invokes the components to render the HTML. It is not invoking the entire HTTP request pipeline in ASP.NET Core, which can be more performant, however the application may not take advantage of some parts of ASP.NET Core, e. g. the authorization.

Also, there are some custom mechanisms to serve static files to the web app. I haven’t explored the details on Android an iOS yet, so it may be necessary to do it that way, but the static pages middleware in ASP.NET Core could do the same job if it gets the correct implementation of IFileProvider.

Since the DotVVM runtime has a lot of dependencies on ASP.NET Core services, I’ve found that the easiest way would be to invoke directly the RequestDelegate to handle the entire HTTP request using ASP.NET Core pipeline.


Same as in BlazorWebView, the app is running on a special origin 0.0.0.0 – it’s not a valid IP address, but WebView thinks that it is fine, and doesn’t do any DNS resolution. We could use localhost, but if the WebView would need to call something on the local machine, there could be conflicts or some problems with sharing cookies. This seems more feasible.

The crucial part for integration is to intercept the HTTP request sent by the WebView and provide the response without performing any networking operation.

_webview.CoreWebView2.AddWebResourceRequestedFilter($"{AppOrigin}*", CoreWebView2WebResourceContext.All);

_webview.CoreWebView2.WebResourceRequested += async (s, eventArgs) =>
{
    …
};

The request is handled like this:

public async Task<DotvvmResponse> ProcessRequest(Uri requestUri, string method, IEnumerable<KeyValuePair<string, string>> headers, Stream contentStream)
{
    using var scope = serviceScopeFactory.CreateScope();
    var httpContext = new DefaultHttpContext()
    {
        Request =
        {
            Scheme = requestUri.Scheme,
            Host = new HostString(requestUri.Host, requestUri.Port),
            Path = requestUri.PathAndQuery,
            Method = method
        },
        Response =
        {
            Body = new MemoryStream()
        },
        ServiceScopeFactory = serviceScopeFactory,
        RequestServices = scope.ServiceProvider
    };
    foreach (var header in headers)
    {
        httpContext.Request.Headers.Add(header.Key, header.Value);
    }            
    if (contentStream != null)
    {
        httpContext.Request.Body = contentStream;
    }
    await requestDelegate(httpContext);

    return new DotvvmResponse(
        httpContext.Response.StatusCode,
        httpContext.Response.Headers.SelectMany(h => h.Value.Select(v => new KeyValuePair<string, string>(h.Key, v))),
        (MemoryStream)httpContext.Response.Body);
}

You can obtain the RequestDelegate using the standard ASP.NET Core dependency injection – it is registered there.


Communication with the host

The hosted web app will probably need to communicate with the MAUI app. Since both MAUI and DotVVM runs in the same process, it is theoretically possible to directly call methods in MAUI/DotVVM viewmodels, but DotVVM was designed as stateless and thus its C# viewmodel exists only for a short period of time – only during execution of a particular HTTP request. Most of the time, the viewmodel is represented by the JavaScript object in the page itself.

Therefore, the DotvvmWebView implementation contains several useful methods:

  • GetViewModelSnapshot – this method obtains a snapshot of the viewmodel and returns it as dynamic. I’ve been thinking about a strongly-typed way of obtaining the viewmodel, but there are many problems – using the same viewmodel class is not a good idea since it has many dependencies (on HttpContext and other services) – there would have been some interface. Dynamic might be good enough.
  • PatchViewModel – the MAUI app can send patches to the page viewmodel. Again, this is fully dynamic API, but since we are working with JavaScript, it’s probably not such a big deal.
  • CallNamedCommand – this function allows to call commands declared using <dot:NamedCommand> control in the page. Currently, you need to specify the viewId, which is a DotVVM internal ID of the associated JS module – I want to introduce some API that will get the viewId using a CSS selector – you would be able to specify CSS selector for a control that contains the named command.

DotVVM can send events to the MAUI app by calling _webview.SendNotification(methodName, arg1, arg2…) in a static command – this will invoke the PageNotificationReceived event on DotvvmWebView. Using this mechanism, you can respond to the events in DotVVM pages.


I’ve implemented the prototype of DotVVM + MAUI integration on GitHub. Currently, it supports only Windows UWP host, but I plan to add support for more platforms. There are some further challenges in making this component production-ready – DotVVM compiles its pages on the first access, which will be slow on mobile devices – we’ll probably need some way of pre-compilation.

Recently, we got a question on our Gitter chat about building markup controls that use binding expressions.

Basically, the requirement was to have a breadcrumb navigation control that would be used this way:

<cc:Breadcrumb DataSource="{value: ListNavParent}" 
               ItemActiveBinding="{value: ItemActive}" 
               PublicUrlBinding="{value: PublicUrl}" 
               TitleBinding="{value: Title}" />

The first parameter is DataSource - a hierarchy of pages in the navigation (Section 1 > Subsection A > Page C). The remaining parameters are binding expressions that would be evaluated on every item in the data source and provide the URL and title of the item and the information whether the item is active.

DotVVM has several controls which work this way – ComboBox for example. There is also the DataSource property as well as ItemTextBinding, ItemValueBinding and so on.

However, it is not trivial to build a control like that. In this article, I’d like to suggest some ways how to do it.


Rule 1: Use DataContext wherever possible

Writing such control can be much simpler if you don’t try to deal with properties like DataSource and SomethingBinding. Since the control just renders the breadcrumb navigation (a bunch of links) and does nothing else, and the collection of links will probably be stored in the viewmodel anyway (you need to pass it in the DataSource property somehow), you can get rid of the DataSource property and pass the collection of links to the control via the DataContext property. Inside the control, all bindings will be evaluated just on the control’s DataContext.

And because the DataContext will be a collection of some objects of a known type, making the Breadcrumb control would be easy. If you want to use the control in a more generic way (for different kinds of objects), feel free to use and interface.

First, let’s create the object which would represent the individual link in the navigation:

public class BreadcrumbLink
{
    public string Title { get; set; }

    public string Url { get; set; }

    public bool IsActive { get; set; }
}


It would be nice if we could display also a home icon in the control – the control will need a URL for the home icon as well as the collection of links.

If you have multiple things you need to pass to the control, there are two options:

  • Pass one of the things (probably the main one) as the DataContext and create control properties for the other values.
  • <cc:Breadcrumb DataContext="{value: Links}" HomeUrl="/" />


  • Create a tiny viewmodel object that holds all information the control needs. It depends how “coupled” the values are to each other – if you need to aggregate various data from different places in the viewmodel, the first option would work better. If all the values are tied together and worked with from one place, this approach is better.

Let’s create a tiny viewmodel object for the entire control:

public class BreadcrumbViewModel
{

    public string HomeUrl { get; set; }

    public List<BreadcrumbLink> Links { get; set; }

}


Now we can define the control like this:

@viewModel RepeaterBindingSample.Controls.BreadcrumbViewModel, RepeaterBindingSample

<ol class="breadcrumb">
    <li><a href="{value: HomeUrl}"><span class="glyphicon glyphicon-home" /></a></li>
    <dot:Repeater DataSource="{value: Links}" RenderWrapperTag="false">
        <ItemTemplate>
            <li class-active="{value: IsActive}">
                <a href="{value: Url}">{{value: Title}}</a>
            </li>
        </ItemTemplate>
    </dot:Repeater>
</ol>


In this case, the control doesn’t need to have a code-behind file as it won’t need any control properties. All we need to do is to prepare the BreadcrumbViewModel object in the page viewmodel (nesting of viewmodels is a very powerful thing btw) and pass it to the control. The @viewModel directive of the control enforces that the control can be used only when its DataContext is BreadcrumbViewModel. If you use the control on any other place, you’ll get an error – bindings in DotVVM are strongly-typed.

<cc:Breadcrumb DataContext="{value: Breadcrumb}" />

Since this control will probably be on all pages of the app, it can be used in the master page. In addition to that, the master page can require all pages to provide items for the breadcrumb navigation using an abstract method:

public abstract class MasterPageViewModel : DotvvmViewModelBase
{

    [Bind(Direction.ServerToClientFirstRequest)]
    public BreadcrumbViewModel Breadcrumb { get; } = new BreadcrumbViewModel()
    {
        HomeUrl = "/"
    };

    public override Task Init()
    {
        if (!Context.IsPostBack)
        {
            Breadcrumb.Links = new List<BreadcrumbLink>(ProvideBreadcrumbLinks());
        }

        return base.Init();
    }

    protected abstract IEnumerable<BreadcrumbLink> ProvideBreadcrumbLinks();
}


The conclusion is – pass data to controls using DataContext and avoid creating code behind for the controls if you can.


But what if I really want to work with custom binding expressions?

Building of custom binding expressions is tricky, but very powerful. Before we start, let’s review how binding works in DotVVM.

The DotVVM controls define their properties using this awful boilerplate syntax:

public string HomeUrl
{
    get { return (string)GetValue(HomeUrlProperty); }
    set { SetValue(HomeUrlProperty, value); }
}
public static readonly DotvvmProperty HomeUrlProperty
    = DotvvmProperty.Register<string, Breadcrumb2>(c => c.HomeUrl, string.Empty);


Why is that? Because the property can contain either its value, or a data-binding expression (which is basically an object implementing the IBinding interface). DotVVM controls have a dictionary of property values-or-bindings, and offer the GetValue and SetValue methods that can work with the property values (including evaluation of the binding expression). The only important thing here is the static readonly field HomeUrlProperty which is a DotVVM property descriptor. It can specify the default value, it supports inheritance from parent controls (if you set DataContext property on some control, its value will propagate to the child controls - unless the child control sets DataContext to something else), and there are more features to that.

There are several types of bindings – the most frequent is the value and command bindings. But how to construct the binding object?

The easiest way is to have it constructed by the DotVVM – it means specify it somewhere in a DotHTML file and have it compiled by the DotVVM view engine. It happens when you declare the code-behind for your markup control:

@viewModel System.Object
@baseType RepeaterBindingSample.Controls.Breadcrumb2, RepeaterBindingSample


public class Breadcrumb2 : DotvvmMarkupControl
{

    public string HomeUrl
    {
        get { return (string)GetValue(HomeUrlProperty); }
        set { SetValue(HomeUrlProperty, value); }
    }
    public static readonly DotvvmProperty HomeUrlProperty
        = DotvvmProperty.Register<string, Breadcrumb2>(c => c.HomeUrl, string.Empty);

}

If you use the control on some page and look what’s in the HomeUrl property (e. g. using GetValueRaw(HomeUrlProperty)), you’ll get the constructed binding object.

There are several things inside the binding object – the bare minimum are these:

  • DataContextStack – this structure represents the hierarchy of types in the DataContext path – if the binding is in the root scope of the page, the stack has only PageViewModel; if it’s nested in a repeater, there will be PageViewModel and BreadcrumbLink types. Please note that these are only types, not the actual values. The binding must know of that type is _this, _parent and  _root.
  • LINQ expression that represents the binding itself. It is strongly-typed, it can be compiled to IL so it’s fast to evaluate, and it can be quite easily translated to JS because it’s an expression tree. The expression is basically a lambda with one parameter of object[] – this is a stack of DataContexts (not types but the actual values – they are passed to the binding so it can evaluate). The element 0 is _this, the element 1 is _parent, the last one is _root

There can be other things, like translated Knockout expression of the binding, original binding string and so on, but they are either optional or will be generated automatically when needed. If you want to construct your own binding, you need at least the DataContextStack and the expression.

To create a binding, you can use something like this:

In order to build a binding, you need an instance of BindingCompilationService, the expression and the DataContextStack.

You can obtain the BindingCompilationService from dependency injection – just request it in the control constructor.

The DataContextStack can be obtained by calling control.GetDataContextType().

An finally, you need the expression – you can define it yourself like this:

Expression<Func<object[], string>> expr = contexts => ((BreadcrumbViewModel)contexts[0]).HomeUrl;

Note that the expression is always Func<object[], ResultType>. The contexts variable is the array of data context – the first element (0) is _this. The code snippet above is equivalent to {value: _this.HomeUrl} or just {value: HomeUrl}. The cast to specific type is needed just because contexts is array of objects.

To build the expression and pass it for instance to the Literal control, you need something like this:

// build the binding for literal yourself
Expression<Func<object[], string>> expr = contexts => ((BreadcrumbViewModel)contexts[0]).HomeUrl;
var dataContextStack = this.GetDataContextType();
var binding = ValueBindingExpression.CreateBinding(bindingCompilationService, expr, dataContextStack);

// create the literal control and add it as a child
var literal = new Literal();
literal.SetBinding(Literal.TextProperty, binding);
Children.Add(literal);

So now you know how to work with bindings – if you’d like to create a control with a property that accepts a binding, or if you need to compose your own binding, that’s how this is done.


Now how would I create a control as described at the beginning?


If you really want to create a control which is used in the following way, there is some extra things that need to be done.

<cc:Breadcrumb DataSource="{value: ListNavParent}" ItemActiveBinding="{value: ItemActive}" PublicUrlBinding="{value: PublicUrl}" TitleBinding="{value: Title}" />

The DataSource property contains a binding that is evaulated in the same DataContextStack in which the control lives. If you use this control in the root scope of the page, its DataContext will be just PageViewModel.

But the remaining properties will actually be evaluated on the individual items in the DataSource collection. They need to have the DataContextStack equal to the (PageViewModel, BreadcrumbLink) pair. Also, the IntelliSense in Visual Studio will need to know that so it could offer you the property names.

That’s why these properties have to be decorated with DataContextChange attributes. These attributes can stack on top of each other and basically they tell how to change the DataContextStack in order to get the result.

The TitleBinding property will be declared like this:

[ControlPropertyTypeDataContextChange(order: 0, propertyName: nameof(DataSource))]
[CollectionElementDataContextChange(order: 1)]
public IValueBinding<string> TitleBinding
{
    get { return (IValueBinding<string>)GetValue(TitleBindingProperty); }
    set { SetValue(TitleBindingProperty, value); }
}
public static readonly DotvvmProperty TitleBindingProperty
    = DotvvmProperty.Register<IValueBinding<string>, Breadcrumb2>(nameof(TitleBinding));


The first attribute says “let’s take the type of the value assigned to the DataSource property”. The second says “let’s take that type; it should be a collection or an array, so let’s take the element type of this and use it as a second level in the DataContextStack”.

So, if you assign a collection of type List<BreadrumbLink> in the DataSource property, the TitleBinding property stack will be (PageViewModel, BreadcrumbLink).

Notice that the type of the property is IValueBinding<string> – this hints everyone that they shouldn’t fin the “actual value” in the property.

Also, if you use the binding generated in this property, make sure you use it in the correct DataContextStack.

How to use binding object in DotHTML?

Unfortunately, right now you can’t. I think it is possible to write an attached property that would do that (I will try it), but DotVVM doesn’t ship with any option to do that. 

However, it is possible to generate the controls from the C# code. Theoretically, you can combine markup controls and code-only approach of tampering with bindings, but sometimes it gets even more complicated, so I prefer using code-only control in these cases.

Let’s start by defining the Breadcrumb2 control and its properties:

public class Breadcrumb2 : ItemsControl 
{

    public string HomeUrl
    {
        get { return (string)GetValue(HomeUrlProperty); }
        set { SetValue(HomeUrlProperty, value); }
    }
    public static readonly DotvvmProperty HomeUrlProperty
        = DotvvmProperty.Register<string, Breadcrumb2>(c => c.HomeUrl, string.Empty);
    
    [ControlPropertyTypeDataContextChange(order: 0, propertyName: nameof(DataSource))]
    [CollectionElementDataContextChange(order: 1)]
    public IValueBinding ItemActiveBinding
    {
        get { return (IValueBinding)GetValue(ItemActiveBindingProperty); }
        set { SetValue(ItemActiveBindingProperty, value); }
    }
    public static readonly DotvvmProperty ItemActiveBindingProperty
        = DotvvmProperty.Register<IValueBinding, Breadcrumb2>(nameof(ItemActiveBinding));

    [ControlPropertyTypeDataContextChange(order: 0, propertyName: nameof(DataSource))]
    [CollectionElementDataContextChange(order: 1)]
    public IValueBinding PublicUrlBinding
    {
        get { return (IValueBinding)GetValue(PublicUrlBindingProperty); }
        set { SetValue(PublicUrlBindingProperty, value); }
    }
    public static readonly DotvvmProperty PublicUrlBindingProperty
        = DotvvmProperty.Register<IValueBinding, Breadcrumb2>(nameof(PublicUrlBinding));

    [ControlPropertyTypeDataContextChange(order: 0, propertyName: nameof(DataSource))]
    [CollectionElementDataContextChange(order: 1)]
    public IValueBinding TitleBinding
    {
        get { return (IValueBinding)GetValue(TitleBindingProperty); }
        set { SetValue(TitleBindingProperty, value); }
    }
    public static readonly DotvvmProperty TitleBindingProperty
        = DotvvmProperty.Register<IValueBinding, Breadcrumb2>(nameof(TitleBinding));


}


The class inherits from ItemsControl, a handy base class that is common for all controls which have the DataSource property. Building your own DataSource property is not that easy if you want it to support all the nice things like the _index variable – you can look in the ItemsControl source code how it’s done.

Otherwise, the code snippet above is not complicated – it is just long. It declares the HomeUrl property as well as the ItemActiveBinding, PublicUrlBinding and TitleBinding with the DataContextChange attributes.

Now, let’s build the contents of the control. Since it doesn’t depend on the actual data passed to the control, we can do this in the Init phase – in general, the earlier the better.

protected override void OnInit(IDotvvmRequestContext context)
{
    var ol = new HtmlGenericControl("ol");
    ol.Attributes["class"] = "breadcrumb";
    ol.Children.Add(BuildHomeItem());
    ol.Children.Add(BuildRepeater());
    Children.Add(ol);

    base.OnInit(context);
}


The BuildHomeItem method just builds the first piece of markup:

<li><a href="{value: HomeUrl}"><span class="glyphicon glyphicon-home" /></a></li>
private HtmlGenericControl BuildHomeItem()
{
    var homeLi = new HtmlGenericControl("li");
    {
        var homeLink = new HtmlGenericControl("a");
        {
            homeLink.Attributes["href"] = GetValueRaw(HomeUrlProperty);     // hard-coded value or binding

            var homeSpan = new HtmlGenericControl("span");
            {
                homeSpan.Attributes["class"] = "glyphicon glyphicon-home";
            }
            homeLink.Children.Add(homeSpan);
        }
        homeLi.Children.Add(homeLink);
    }
    return homeLi;
}


Btw, I am used to nest the child control creation in the inner blocks – it helps me to orient in large hierarchies of the controls.


Creation of the Repeater involves implementation of the ItemTemplate class:


<dot:Repeater DataSource="{value: Links}" RenderWrapperTag="false">
    <ItemTemplate>
        <li class-active="{value: IsActive}">
            <a href="{value: Url}">{{value: Title}}</a>
        </li>
    </ItemTemplate>
</dot:Repeater>


private Repeater BuildRepeater()
{
    var repeater = new Repeater();
    repeater.RenderWrapperTag = false;
    repeater.ItemTemplate = new BreadcrumbLinkTemplate(this);
    repeater.SetBinding(Repeater.DataSourceProperty, GetValueBinding(DataSourceProperty));

    return repeater;
}


internal class BreadcrumbLinkTemplate : ITemplate
{
    private Breadcrumb2 parent;

    public BreadcrumbLinkTemplate(Breadcrumb2 parent)
    {
        this.parent = parent;
    }

    public void BuildContent(IDotvvmRequestContext context, DotvvmControl container)
    {
        var li = new HtmlGenericControl("li");
        {
            li.CssClasses.AddBinding("active", parent.GetValueBinding(Breadcrumb2.ItemActiveBindingProperty));

            var link = new HtmlGenericControl("a");
            {
                link.Attributes["href"] = parent.GetValueBinding(Breadcrumb2.PublicUrlBindingProperty);
                link.SetBinding(HtmlGenericControl.InnerTextProperty, parent.GetValueBinding(Breadcrumb2.TitleBindingProperty));
            }
            li.Children.Add(link);
        }
        container.Children.Add(li);
    }
}


And we’re done – you can find the complete code on GitHub.


As you can see, building the control the second way is much more complicated. Partly, it’s because you are trying to build a very universal control that is agnostic to the type of items passed to the DataSource collection, and accepts binding expressions as parameters. Thanks to the strongly-typed nature of DotVVM, you need to deal with bindings, data context stacks and changes, and all these things you wouldn’t have in the dynamic world. And finally, we didn’t have much time to make this simple – we focused on other areas than simplifying control development, although it is an important one too.

We had some ideas and we even have an experimental implementation, but we didn’t have the time to finalize the pull request and make it a real framework feature.

Recently, I got invited to speak at DotFest – a .NET developer conference in Novosibirsk. Due to the COVID pandemic, they had to make the event online, so the organizers set up a test call with me to make sure everything is working.


During the call, we’ve noticed that OBS (great video streaming software) on my laptop doesn’t work. We needed to grab my screen and send it to a RTSP server, but everything I’ve seen in the OBS was a black screen. The same situation occurred when I tried to add a webcam image to the scene – also a black screen.


I’ve never seen this before – I am using OBS for years and it was always working pretty well – it’s a very reliable piece of software. But I’ve never used OBS on my new laptop before – I always stream from a separate device using my awesome streaming setup.


OBS shows only black screen when using Display Capture or Video Device Capture


After googling a little bit, I’ve found this thread which described the problem and offered a working solution.

Since the thread is quite long, I’ve extracted the necessary steps including screenshots.


The problem is that my laptop has two graphics adapters – integrated Intel Iris Plus Graphics and also nVidia GE Force MX330. Windows has some automated algorithm to decide which adapter will be used by the app, and it chose the wrong one.


I had to go to Graphics Settings (in the Windows Settings app – just search for it in the Start menu) and create a preference for obs64.exe:

Creating a preference for a Desktop app

When I click Options, I need to select the integrated graphics (Power saving) card for display capture to work.

Select the power saving profile


After restarting OBS, everything works well.


According to the forum thread, for game streaming or NVENC, you should probably select the High performance profile.