Dev Blog - Johan Danforth

I'm Johan Danforth and this is my dev blog - a mix of .NET, ASP.NET, Rest, Azure and some other random coding stuff.

  • WebView2 Getting Started

    The Microsoft WebView2 control allows you to embed Web Tech in native applications. It’s miles better than the old web browser controls. The WebView2 control is based on the Chromium platform.

    The official docs for the control are found here: https://docs.microsoft.com/en-us/microsoft-edge/webview2/

    Pre-reqs

    Important note (as of 2021-01-20) - ensure you installed the following list of pre-requisites before proceeding:

    I suggest you visit https://www.microsoftedgeinsider.com/download and get the “Edge Canary Channel” that way.

    Install WebView2 SDK in Visual Studio

    Once you’ve created your WebForms or WPF project, install the Microsoft.Web.WebView2 package from Nuget:

    PM> Install-Package Microsoft.Web.WebView2

    Initializing WebView2 Control

    Much about the control is asynchronous, and to make sure the control is loaded and ready, add an InitializeAsync() method to your form constructor, and place the events of interest there, like so:

            public webView2TestForm()
            {
                InitializeComponent();
                InitializeAsync();
            }
    
            async void InitializeAsync()
            {
                webViewControl.NavigationCompleted += WebViewControlOnNavigationCompleted;
                webViewControl.WebMessageReceived += WebViewControlOnWebMessageReceived;
    
                await webViewControl.EnsureCoreWebView2Async(null);
            }
    
    

    Send Message to Web Page

    Easiest way to communicate between the web page and the native application is to send messages. From the native app:

    webViewControl.CoreWebView2.PostWebMessageAsString("proxy.object.added");
    

    To handle messages, the web page need some scripting:

            window.chrome.webview.addEventListener('message',
                event => {
                    console.log('Got message from host!');
                    console.log(event.data);
                    handleMessages(event.data);
                });
    
    

    Send Message to Host

    To send a message from the web page

    window.chrome.webview.postMessage('page.ready');
    

    To handle messages from web page in the host:

            private void WebViewControlOnWebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
            {
                var message = e.TryGetWebMessageAsString();
                switch (message)
                {
                    case "page.ready":
                        Trace.TraceInformation("Got page.ready message!");
                        break;
                    default:
                        Trace.TraceWarning("Unknown message received: " + message);
                        break;
                }
            }
    
    

    Proxy Objects

    One of the coolest features availeble is to send a “proxy object” from the native application to the web page. There are some limitations but powerful enough. The best info I’ve found is this: https://docs.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.addhostobjecttoscript 

    The class/object must be exposed as a COM object (!):

        [ClassInterface(ClassInterfaceType.AutoDual)]
        [ComVisible(true)]
        public class ProxyHostObject
        {
            // sample property
            public string Name { get; set; } = "Johan";
    
            // sample method
            public string GetName()
            {
                return Name;
            }
    
            // sample indexed property
            [System.Runtime.CompilerServices.IndexerName("Items")]
            public string this[int index]
            {
                get => _dictionary[index];
                set => _dictionary[index] = value;
            }
            private Dictionary<int, string> _dictionary = new Dictionary<int, string>();
        }
    
    

    The use of the ClassInterface attribute is discussed in the Edge github repo, because the AutoDual value is not recommended in the docs, and even deprecated in dotnet:

    Using AutoDual is strongly discouraged because of the versioning limitations described in System.Runtime.InteropServices.ClassInterfaceAttribute.

    The issue is discussed here: https://github.com/MicrosoftEdge/WebView2Feedback/issues/517

    To create and use the proxy object in the web page:

        async function handleMessages(message) {
            switch (event.data) {
            case 'proxy.object.added':
                {
                    const obj = window.chrome.webview.hostObjects.proxyobject;
                    console.log(obj);
    
                    var name1 = await obj.Name;
                    console.log('name prop: ' + name1);
                    var name2 = await obj.GetName();
                    console.log('name func: ' + name2);
    
                    obj.GetName().then(name => {
                        console.log("GetName promise name: " + name);
                    });
    
                    // Indexed properties
                    let index = 123;
                    obj[index] = "test";
                    let result = await obj[index];
                    console.log(result);
                }
                break;
    
            default:
                console.log("unknown message: " + event.data);
            }
        }
    
    
  • How to setup Continuous Integration and Deploy Pipeline for ASP.NET MVC Framework application to Azure Web App

    In this article I will try to explain in as few steps as possible a way to do CI and deploy from an Azure DevOps repository to an Azure hosted Web App. There are many ways to do this, but this worked for me (2021-01-18).

    I’m creating the CI from the Azure portal, but you can also do it more manually, like described by Scott Hanselman in this blog post, which can be useful to read through: https://www.hanselman.com/blog/azure-devops-continuous-builddeploytest-with-aspnet-core-22-preview-in-one-hour

    Tools and Frameworks used: Visual Studio 2019 Version 16.8.4, .Net Framework 4.7.2

    Pre-reqs: I assume you already have an Azure Web App registered that runs ASP.NET 4.8 or similar. Mine is called ‘ci-test-pipeline’. I also assume you already have a repository and a project in Azure DevOps, again, mine is called ‘ci-test-pipeline’. You can also have your repo in github, the steps should be similar. In this repo, I have a vanilla ASP.NET MVC application created and checked in. In my case ‘ci.test.pipeline.web’ in a ‘ci.test.pipeline’ solution:

    ci pipeline solution

    Initially the pipeline page in DevOps will be empty, unless you’ve already setup a pipeline. To create a CI pipeline you can go about it in a few different ways, but this article describes how to do it from the Web App page in Azure portal.

    Goto the Deployment Center page in the Web App menu, and select to create a Continous Deployment (CI / CD) deployment for ‘Azure Repos’.

    Then just follow the steps, it’s hard to do anything wrong as the wizard helps you with dropdowns along the way. Just make sure you select the correct repo and which ‘Web Application Framework’ you are using. In my case for an ASP.NET MVC .Net Framework solution, select ‘ASP.NET’.


    deplyment center


    deployment center 2

    deployment center 3

    deployment center 4

    Once the pipeline is created, it should look something like this on the deployment center page, with a message of a successful setup. This is still in the Azure portal, under your Web App:

    deployment center 5

    If your project was already checked in, like in my case, this also triggers a first build + deployment in Azure DevOps:

    devops ci pipeline created 1

    And you should eventually get an email from Azure DevOps with a success or failure message. In my case I get a ‘partial success’ because of an error in the pipeline:

    build warning 1

    To view the deails of this message, just look through the detailed steps in the last run in Azure DevOps. You can easily get to these details by clicking the ‘View results’ link/button in the email, and drill into the job details:

    build warning 2

    For some reason the ‘Publish symbols path’ is missing a required task variable – ‘Build.SourceVersion’, but the build succeded and published/uploaded the artifact to a ‘drop container’ and the application is deployed to the Azure Web App. I’m not concerned about this warning right now, just happy it runs through.

    To see the deployed files in the Azure portal, you can go look at the console development tool for the Web App in Azure portal, which is neat:

    azure portal console after first pipeline run

    And you should also be able to view your website at <your web app name>.azurewebsites.net, to see that it works as expected:

    website 1

    To view and edit the pipelines steps created for you by the Azure portal wizard, visit the pipelines menu in Azure DevOps, and select to ‘Edit’ the pipeline:

    devops ci pipeline edit 1

    In edit mode, you see all the steps, and you may opt to disable the ‘Test Assemblies’ and ‘Publish symbols path’, until they are needed:

    devops ci pipeline edit 2

    Remember to save the changes, and try them out again. It’s useful to follow the pipeline job by clicking on the ‘Agent’ that runs the job. the screen updates for every step:

    devops ci pipeline agent working 1

    There are loads of different pipeline steps you can add and customize, which is very helpful if you want to do pre- or post-build steps with your code – like adding build information to your website - which build is deployed and what source code version is actually running.

    That’s enough screenshots for this article. Hope it helps.

    In a follow-up to this blog post, I’ll add build information to the website, and finally add authentication.

  • New features in Dapper.Contrib

    I’ve given the official Dapper.Contrip package some love lately. So now all methods include support for lists! So instead of doing lots of insert calls in a loop, just call connection.Insert(myList); and Dapper/Contrib will interate and insert your entities effectively and fast!

    Dapper.Contrib now contains these methods:

    T Get<T>(id);
    IEnumerable<T> GetAll<T>();
    int Insert<T>(T obj);
    int Insert<T>(IEnumerable<T> list);
    bool Update<T>(T obj);
    bool Update<T>(IEnumerable<T> list);
    bool Delete<T>(T obj);
    bool Delete<T>(IEnumerable<T> list);
    bool DeleteAll<T>();

    For .NET 4.5 users there are also async methods available. For a complete and (almost) up-to-date help page on how Dapper.Contrib works, please visit https://github.com/StackExchange/dapper-dot-net/tree/master/Dapper.Contrib

    Dapper.Contrib can be installed from Nuget, read more at https://www.nuget.org/packages/Dapper.Contrib/

  • Effectively insert list of objects with Dapper

    If you are using Dapper as your sql/object mapper for .Net, you often find yourself inserting lists of objects. There are several ways of doing it, but maybe you're not doing it the fastest way possible (apart from hand-coding)?

    What most people do is iterating over the list add execute the insert for each item like this:

    foreach (var item in myList
    {
        connection.Execute("INSERT INTO MYTABLE VALUES (@A, @B)", item);
    }

    but this is not the fastest way becuse the Dapper Execute() extension takes a list as parameter and iterates for you:

    connection.Execute("INSERT INTO MYTABLE VALUES (@A, @B)", myList);

    I tested by inserting 10.000 simple objects into an SqlCe database and clocked the resuts. The first option took 1.88 seconds, and the second took 1.19 seconds! So, letting Dapper iterate and insert is not twice as fast, but almost!

    To install Dapper, run the following command in the Package Manager Console

    PM> Install-Package Dapper

  • jQuery validate and the comma decimal separator

    Oh this is such a simple and good solution that I must put it on my blog at least for my own future reference. Big THANKS to Lenard Gunda for writing this blog post

    http://blog.rebuildall.net/2011/03/02/jQuery_validate_and_the_comma_decimal_separator

    If you live outside the US and have problems with getting client side validation accepting comma decimal separator in input fields - just overwrite jQuery validation range and number methods with these javascript lines in the end of all your javascript loading.

    $.validator.methods.range = function (value, element, param) {
        var globalizedValue = value.replace(",", ".");
        return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
    }
     
    $.validator.methods.number = function (value, element) {
        return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:[\s\.,]\d{3})+)(?:[\.,]\d+)?$/.test(value);
    }

  • Bootstrap Image Carousel Fade Transition

    The built in image carousel animation for Bootstrap is a slide, which is nice, but I found this fade transition while Googling, and it seems to work well. I found it here on http://codepen.io/Rowno/pen/Afykb and it’s penned by Roland Warmerdam.

    Html:

    <div class="carousel slide carousel-fade" data-ride="carousel">
        <div class="carousel-inner">
            <div class="item active">
                <img class="img-rounded" src="..." alt="...">
            </div>
            <div class="item">
                <img class="img-rounded" src="..." alt="...">
            </div>
            <div class="item">
                <img class="img-rounded" src="..." alt="...">
            </div>
        </div>
    </div>
    

    Css (less):

    .carousel-fade {
        .carousel-inner {
            .item {
                opacity: 0;
                -moz-transition-property: opacity;
                -o-transition-property: opacity;
                -webkit-transition-property: opacity;
                transition-property: opacity;
            }
    
            .active {
                opacity: 1;
            }
    
            .active.left,
            .active.right {
                left: 0;
                opacity: 0;
                z-index: 1;
            }
    
            .next.left,
            .prev.right {
                opacity: 1;
            }
        }
    
        .carousel-control {
            z-index: 2;
        }
    }
  • Formatting File Upload Input with Bootstrap

    If you’re using file upload forms in your Bootstrap themed website and would like to format the button and the field nicely, take a peek at this article by Cory LaViska. He nails it.

     

    A small example which produces an input-group like this (“Fil” means file in Swedish):

    image

    <div class="editor-label">
        <label>Fil</label>
        <div class="input-group">
            <span class="input-group-btn">
                <span class=" btn btn-default btn-file">
                    välj fil... <input type="file" name="data" id="data">
                </span>
            </span>
            <input type="text" id="valdfil" class="form-control" readonly />
        </div>
    </div>
    
    <script type="text/javascript">
    
        $(document).ready(function () {
            $(document).on('change', '.btn-file :file', function () {
                var input = $(this),
                    numFiles = input.get(0).files ? input.get(0).files.length : 1,
                    label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
                input.trigger('fileselect', [numFiles, label]);
            });
    
            $('.btn-file :file').on('fileselect', function (event, numFiles, label) {
                console.log(numFiles);
                console.log(label);
                $("#valdfil").val(label);
            });
        });
    
    </script>

    You must also add these CSS lines:

    .btn-file {
        position: relative;
        overflow: hidden;
    }
    .btn-file input[type=file] {
        position: absolute;
        top: 0;
        right: 0;
        min-width: 100%;
        min-height: 100%;
        font-size: 100px;
        text-align: right;
        filter: alpha(opacity=0);
        opacity: 0;
        outline: none;
        background: white;
        cursor: inherit;
        display: block;
    }
  • Using FontAwesome in Universal Apps

    image_thumb2Want to use FontAwesome in Universal Apps (both Windows Phone & Windows 8.1). The procedure is similar to how you do it for WPF:

    1) Install-Package FontAwesome

    2) Mark the file /fonts/fontawesome-webfont.ttf with “Build Action” set to “Content” (not “Resource”)

    3) Try out the font like this in a sample Windows Phone main page:

    <Viewbox>
        <TextBlock Text="&#xf09b;"                 FontFamily="fonts/fontawesome-webfont.ttf#FontAwesome" />
    </Viewbox>
    

    And you should see:

    image

    Note that the steps and syntax is a bit different from how you do it in WPF.

  • Using FontAwesome in WPF

    imageWant to use FontAwesome (http://fontawesome.io/) in your WPF application? With a few simple steps you can do it.

    1) Use Nuget and Install-Package FontAwesome

    2) Mark the file /fonts/fontawesome-webfont.ttf and set it’s “Build Action” to “Resource”

    3) Test the font in a simple TextBlock like this:

    <Window x:Class="FontAwesomeWPFBlogTest.MainWindow"         
    xmlns
    ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"       
    xmlns
    :x="http://schemas.microsoft.com/winfx/2006/xaml"       
    Title
    ="MainWindow"       
    Height
    ="350"       
    Width
    ="525">     <Grid>         <Viewbox>             <TextBlock Text="&#xf09b;"
                           FontFamily="pack://application:,,,/fonts/#FontAwesome" />         </Viewbox>     </Grid> </Window>

    Run it and you should see this:

    image

    The “hardest” thing is to make sure you enter the right icon-hexcode for the Text property. You can look at the different icons available in the icon-gallery page on http://fontawesome.io/icons/ then check the name of the icon you like. After that, go to the /Contents/font-awesome.css file in your project and look it up there, say for example the paint-brush icon:

    .fa-paint-brush:before {   content: "\f1fc";
    }
    

    The content-value says “\f1fc” which is the hex value of the paint-brush symbol in the font and that is the value you have to enter in the Text-property in your XAML:

    Text=”&#f1fc;”

  • Lazy Loading TreeView Sample with ASP.NET MVC and jqTree

    I’ve been looking for a lightweight TreeView library to use with ASP.NET MVC, and I think I’ve found my “weapon of choice” – jqTree.

    The code is available on https://github.com/mbraak/jqTree and the author mbraak (Marco Braak) is very active in the “issues” section answering questions from users.

    The package is not available on Nuget at the moment though, but it’s easy enough to download and set up manually.

    Download and unzip the code from Github, copy the files jqtree.css and jqtree-circle.png to your /Content folder, and copy the tree.jquery.js file to your /Scripts folder. Then open up your /App_Start/BundlesConfig.cs file and add the files to your bundles, something like this:

     

      bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                          "~/Scripts/bootstrap.js",
                          "~/Scripts/tree.jquery.js",    
                          "~/Scripts/respond.js"));
    
      bundles.Add(new StyleBundle("~/Content/css").Include(
                          "~/Content/bootstrap.css",
                          "~/Content/jqtree.css",
                          "~/Content/font-awesome.min.css", 
                          "~/Content/site.css"));

    Add this viewmodel to hold the treeview node data:

    public class Node
    {
        public string label { get; set; }
        public string id { get; set; }
        public bool load_on_demand { get; set; }
    
        public Node(string label, string id, bool loadOnDemand = true)
        {
            this.label = label;
            this.id = id;
            this.load_on_demand = loadOnDemand;
        }
    }
    

     

    Then create a controller method in HomeController.cs which will be called on-demand/lazily by the TreeView control in your HTML-view. My action is called /Home/Nodes and takes and optional “node” parameter which holds the id of the treeview-node being “expanded”. This is some simple test-code using the local computer folder structure on the D-drive just to try it out. If it’s a folder I’m passing “true” to the load_on_demand property of the jqTree javascript model:

    public ActionResult Nodes(string node)
    {
        Debug.WriteLine("id=" + node);
        var list = new List<Node>();
        if (node == null)
        {
            var items = Directory.GetFileSystemEntries("D:/");
            foreach (var item in items)
            {
                list.Add(new Node(Path.GetFileName(item), item, Directory.Exists(item)));
            }
        }
        else
        {
            var items = Directory.GetFileSystemEntries(node);
            foreach (var item in items)
            {
                list.Add(new Node(Path.GetFileName(item), item, Directory.Exists(item)));
            }
        }
        return Json(list, JsonRequestBehavior.AllowGet);
    }
    

    In your Razor HTML-view, the base for the TreeView control is a simple <div> tag, with an data-url attribute pointing at the method it should call to get data:

     <div id="tree1" data-url="/home/nodes"></div>

    and finally some javascript/jquery to initialize the TreeView and add some handling, this code loads the treeview, sets the icons I would like to have (from font-awesome) and adds a click-handler which just writes out the selected node name to the console.

    @section scripts {
        <script language="javascript" type="text/javascript">
    
            $(function () {
                 $('#tree1').tree({
                    closedIcon: $('<i class="fa fa-plus"></i>'),
                    openedIcon: $('<i class="fa fa-minus"></i>'),
                    dragAndDrop: false,
                    selectable: false
                });
    
                // bind 'tree.click' event
                $('#tree1').bind(
                    'tree.click',
                    function (event) {
                        // The clicked node is 'event.node'
                        var node = event.node;
                        console.log('clicked ' + node.name );
                    }
                );
            });
    
        </script>
    }

    This is all you need to do, and it’s possible (and quite easy too) to modify the look of the tree in the view, have your own icons and such. Ask questions in the “issues” section of the project and I’m sure you’ll get your answers - https://github.com/mbraak/jqTree/issues

    Thanks to mbraak for cool stuff, me likes Ler