February 2016 - Posts

DevExpress @ Techorama 2018 Edition

This week, the 5th edition of Techorama conference is being held in Antwerp, Belgium and DevExpress will be present as Gold partner.

image

John, Julian and I will be there manning the booth. In case you missed our “What’s New in v18.1” webinars, we’ll be showing you all new awesome features that come with it.

I will be doing a Partner Session about CodeRush so make sure to check that out if you want to get started with CodeRush!

If you’re there, make sure to stop by and get a cool T-Shirt and a raffle ticket to win a platform subscription of choice.

DevExpress will be present at dotnet Cologne 2018

At May 4th 2018, DevExpress is proud to be Gold-Partner at the dotnet Cologne 2018 conference in Cologne Germany.

John and I will be there to show you everything you want to know about our newest v18.1 release, and we'll be handing out some cool T-Shirts and other give aways.

I will also be on stage doing a very cool session about Cognitive Services and A.I.

If you're at dotnet Cologne, make sure to come by and say 'Hi!'

DevExtreme - New Material Design Theme (v18.1)

Google's Material Design is one of the most popular design languages used in Web and Mobile apps today.
This UI is characterized by a grid-based layout and the use of only a few vivid colors to raise the user's attention to certain elements on the screen.

Since it is originally developed for Google's Android UI, it has a strong focus on touch based devices but it can absolutely be used on desktops as well. In fact, there is official documentation available on how Material Design should work and look across different devices.

Since we value your feedback - and we got a lot on this subject - we have decided to supply you with a Light Material Design Theme for the DevExtreme components. This theme even comes with 5 predefined color-schemes which match the Material guidelines color palette!


Guidelines vs Real World

Though the documentation on Material Design is very detailed, we faced a couple of scenarios in which the documentation did not accommodate so we had to optimize and change some of those guidelines to have them match real-world use-cases.

DataGrid

One of the issues we encountered is concerning the DevExtreme DataGrid.
The Material Design documentation clearly states that the padding between columns should be at least 56 pixels.

If the grid only contains numbers it looks good, but when the grid contains text columns as well, those texts will be truncated pretty heavily which makes it hard to read.

For this reason we decided to ignore this guideline and reduced the padding in our theme.

PivotGrid

According to the documentation, any table should have large indents between columns and should not have separators.

The purpose of the PivotGrid however is to display a lot of information in a tree-style way. If we would have followed the guidelines concerning spaces and indentation, a lot of screen real estate would be wasted.

For the sake of readability we did include separators between rows and columns.

Then, there are some components that come with DevExtreme which are not described in the guidelines at all.

In these cases we've taken a look at the different apps and services created by Google which provide similar functionality.
An example is the design of the DevExtreme Scheduler which is inspired by Google Calendar.

It's all in the details

To make sure that our controls have the best Material Design experience possible, there are a couple of other things we did that are worth mentioning.

The DevExtreme components have a huge set of configuration options to configure how they look. If you take a look at our scheduler, you can specify whether you want tabs or a drop-down box to select the current view by setting the appropriate configuration property.

If you select our Light Generic Theme, and don't set any of those properties, the Scheduler will display tabs to switch views while if you select the Material Design Theme, the Scheduler will display the drop-down instead of the tabs.

The same happens with labels; With the Material Design Theme we put the labels without a colon above the editor while with the Generic Themes, we put them in front of the control with a colon by changing the options of the Form Widget.

In other words, we apply a default configuration on the controls depending on the selected theme. If you want, you can change this behavior in your application but it will not be according to the Material Design guidelines anymore.  

Demos and Theme Builder

Besides the different color schemes we provide with the Material Design Theme, we will make the Material Design Theme available in our online Theme Builder so you can select the accent color from the Material Palette or choose any color you want to have your personalized Material Theme.

With the official v18.1 release, you'll also be able to switch the Demo Gallery over to the Material Design Theme so you can see how awesome this looks!

More to come

Once v18.1 is released we will keep improving the Material Design theme. There will be a Dark Material Design theme, and we will include things like an option to enable or disable flat-style buttons, a floating action button and animated labels in the Form Widget when its control receives focus. 

Angular, Vue, React, ASP.NET MVC / Core and more

This brand new theme will be available for all the frameworks we support with DevExtreme including:
Angular, Vue, React, jQuery, Knockout, ASP.NET MVC and .NET Core.

Try it now?

If you want to give this feature a test-drive, the public beta is already available. Use the npm pre-release package:

npm install --save devextreme@18.1-unstable

If you're not using npm, check out Mehul’s earlier blog post about pre-releases.

What do you think?

Let me know what you think of this by replying on this article or if you have questions or suggestions, then please take part in the discussion about this feature on GitHub.

Join the Webinar

If you want to see all new features introduced in our v18.1 release including the Material Design Theme, sign up for our upcoming “New in v18.1 - DevExtreme HTML / JS Controls” webinar where you’ll have a chance to ask questions about all the new features as well.

Join the webinar: https://attendee.gotowebinar.com/register/9186007723238099714

DevExtreme TreeList - Paging and State Persisting (v18.1)

We've included some really cool features in the DevExtreme TreeList widget in our upcoming v18.1 release.

Paging

If you're dealing with a lot of data in the TreeList, you might not want to scroll all the way down (or up).
For this reason, we decided to implement a pager in the TreeList similar to the one found in the DataGrid widget. Not only does it allow you to page through the nodes, but it also allows you to select the page size and it shows you the number of nodes and pages.

You can configure the use of the pager by specifying the paging and pager configuration options as shown below:

{
    //...
    scrolling: {
        mode: "standard"
    },
    paging: {
        enabled: true,
        pageSize: 10
    },
    pager: {
        showPageSizeSelector: true,
        allowedPageSizes: [5, 10, 20],
        showInfo: true
    }
}

The TreeList widget has the same API as the DataGrid widget.

If you have questions or suggestions, feel free to take part in the discussion about this feature at GitHub.

Note: Due to the difficulty of remote hierarchical data paging, this feature is only available on the client-side.

State Persisting

Suppose you have made some run-time customizations in the TreeList widget like filter-settings, column visibility or even expanded/collapsed/selected nodes.

You can now re-apply these settings in a later stage of the application with our v18.1 version of the TreeList widget.
Below is a code fragment on how to persist this information in the browser's localStorage:

{
    //...
    stateStoring: {
        enabled: true,
        type: 'localStorage',
        storageKey: 'treeListStorage'
    }
}

Angular, Vue, React, ASP.NET MVC / Core and more

All of these new features will be available on all the frameworks we support like Angular, Vue, React, jQuery, Knockout, ASP.NET MVC and .NET Core.

Try it now?

If you want to give these features a test-drive, simply add it to your project by entering:

npm install --save devextreme@18.1-unstable

If you're not using npm, check out this post.

Like it?

Let me know by replying to this post.

Microsoft Identity and XPO Continues: Support for .NET Core available

Some time ago I wrote an article on an XPO-based storage provider for Microsoft Identity. This provider is available as NuGet package, but at the time of writing, we didn’t have XPO available for .NET Core.

One of the things we released in v17.2 is XPO for .NET Core on which I did a webinar and a blog post. An obvious next step for the Identity Storage provider would be to make it  available on .NET Core as well.

Some additional goals to achieve

With the desire to support .NET Core as well as .NET Framework, there were some additional wishes I had with this project:

One code base for .NET Framework and .NET Core

Before I started with the .NET Core support, I had the impression that .NET Core version would be similar to the .NET Framework version. I was for 75% correct with that assumption.

After investigating the Identity source code on GitHub, it seemed that there is some extra functionality in the .NET Core version (Claims on Roles, and UserTokens). Also the signature on several interface methods was changed. (Cancellation token parameter)

As expected, the references in the .NET Framework version are different from the references for the .NET Core version.

One NuGet package for .NET Framework and .NET Core

Since the NuGet package format supports multi .NET version support, it would be nice to have everything packed up in one package.

Use .NET Core Dependency Injection to configure XPO and Storage provider

One of the sweet new things in ASP.NET Core is the Dependency Injection which comes with it. It seems obvious to use the D.I. to initialize the XPO datalayer as well as the storage provider in a similar way as the M.S. project template is doing for Entity Framework.

What needs to be done?

To have one project supporting both .NET Framework and .NET Core, the project file needs to be converted to the new csproj file format. We also need to change one of the default options to  support different .NET versions.

Change the contents of the csproj file to the new format

The old csproj file format looks like this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" 
       Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{4A9EC437-F8F0-4C53-B288-DE26E58F061F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>DX.Data.Xpo.Identity</RootNamespace>
    <AssemblyName>DX.Data.Xpo.Identity</AssemblyName>
    <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="DevExpress.Data.v17.1, Version=17.1.5.0, Culture=neutral, 
         PublicKeyToken=b88d1754d700e49a, processorArchitecture=MSIL">
      <HintPath>..\packages\DevExpress.Data.17.1.5.0\lib\DevExpress.Data.v17.1.dll</HintPath>
    </Reference>
    <!-- the rest of the file is omitted -->

  

Together with a .nuspec file and a post build event, it is possible to build the project and the NuGet package in one go.

The new csproj format, looks much cleaner and it encapsulates the .nuspec file function right into the project. I think that’s a nice improvement. To start using this new file format, the most easy way is to unload the solution and edit the source code. The DX.Utils project will look like below:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <Version>1.0.0.6</Version>
    <FileVersion>1.0.0.6</FileVersion>
    <Authors>Don Wibier (DevExpress)</Authors>
    <Description>Several C# utility classes and extension methods</Description>
    <Copyright>Copyright (c) 2017 Don Wibier</Copyright>
  </PropertyGroup>
  
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
    <PackageLicenseUrl>https://github.com/donwibier/DXWeb/blob/master/LICENSE</PackageLicenseUrl>
    <PackageProjectUrl>https://github.com/donwibier/DXWeb/tree/master/DX.Utils</PackageProjectUrl>
    <PackageIconUrl>https://www.devexpress.com/favicon.ico</PackageIconUrl>
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
    <PackageTags>DXWeb Wibier DevExpress</PackageTags>
    <PackageReleaseNotes>
      1.0.0.6: Changed .NET Framework to v4.6.1
      1.0.0.5: Initial dual mode package for .NET Framework and .NET Standard 2.0
Some features don't work yet on .NET Standard 2.0</PackageReleaseNotes>
    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
  </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='net461'">
    <Reference Include="System" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Core" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />    
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
    <PackageReference Include="System.Configuration.ConfigurationManager">
      <Version>4.4.1</Version>
    </PackageReference>
  </ItemGroup>
</Project>


  

I mentioned earlier that we need to make a small change in the default project format. It is the TargetFrameworks node; it contains both netstandard2.0 and net461. These values can be used for conditional ItemGroups to e.g. include framework dependent assembly and NuGet package references as well as source files. Because I included the Package information nodes, once I build the project, it will build the NuGet package automatically which will hold everything for .NET Framework and .NET Core.

Once we reload the project and open one of the cs files, you will notice a small addition to de code editor of VisualStudio. It is the combobox to select the framework you’re working on:

image

If we build the project now, you will have a NuGet package which includes the multi-version content:

image

Now we need to do some conditional coding because of the changed signature of the Interface methods which I have implemented on the XPUserStore as well as the XPRoleStore. On the XPUserStore there are some additional interfaces implemented for 2 Factor Authentication.

The easiest way to accomplish that is by using the #if (NETSTANDARD2_0) compiler directive to include or exclude specific code. Below is a fragement of the XPRoleStore which includes this conditional coding construct:

#if (NETSTANDARD2_0)
    public class XPRoleStore<TKey, TRole, TXPORole, TXPORoleClaim> : XpoStore<TXPORole, TKey>,
        IQueryableRoleStore<TRole>,
        IRoleClaimStore<TRole>
        where TKey : IEquatable<TKey>
        where TRole : XPIdentityRole<TKey, TXPORole, TXPORoleClaim>, IRole<TKey>
        where TXPORole : XPBaseObject, IDxRole<TKey>, IRole<TKey>
        where TXPORoleClaim: XPBaseObject, IDxRoleClaim<TKey>
#else
    public class XPRoleStore<TKey, TRole, TXPORole> : XpoStore<TXPORole, TKey>,
    	IQueryableRoleStore<TRole, TKey>
    	where TKey : IEquatable<TKey>
    	where TRole : XPIdentityRole<TKey, TXPORole>, IRole<TKey>
    	where TXPORole : XPBaseObject, IDxRole<TKey>, IRole<TKey>
#endif
{
    public XPRoleStore() : base()
    {}

    public XPRoleStore(string connectionName) :
      base(connectionName)
    {}
    public XPRoleStore(string connectionString, string connectionName) :
      base(connectionString, connectionName)
    {}
    public XPRoleStore(XpoDatabase database) :
      base(database)
    {}
#if (NETSTANDARD2_0)
    public async virtual Task<IdentityResult> CreateAsync(TRole role, 
						CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        await CreateAsync(role);
        return IdentityResult.Success;
    }
#endif
    public virtual Task CreateAsync(TRole role)
    {
      ThrowIfDisposed();
      if (role == null)
      {
        throw new ArgumentNullException("role");
      }
      return Task.FromResult(XPOExecute((db, wrk) =>
      {
        var xpoRole = XPOCreateRole(wrk);
        xpoRole.Assign(role, 0);
        return null;
      }));
    }
    // rest of the code is omitted ...
}
  

What you see is that the .NET Core method calls the actual original .NET Framework method to maintain the one codebase strategy.

Datamodel Changes

As I mentioned earlier, Identity for .NET Core has some additional functionality. The most noticeable ones are the support for Claims on Roles and the implementation of UserTokens. Another smaller detail is the use of normalized fields in certain circumstances.

What this means in that for essential lookup fields like username and email addresses, there is an extra field NormalizedEmail and NormalizedName. I implemented similar functionality in the first release of this provider by implementing the UserNameUpper and EmailUpper fields.

The idea behind this is quite straightforward. Whenever we need to lookup records by name or email, we use the Upper (or Normalized for .NET Core) for selecting. Remember that some database back ends are sorting and filtering case-sensitive.We don’t want this on the accounts and roles. A technical side effect is that the index files of the specific tables only contain uppercase characters, so less combinations to compare which improves the search speed.

The Normalized addition is even slightly better as the Upper approach I used. It should also remove the accents from characters which is common in certain languages to overcome sorting issues. .NET Core comes with a configurable Normalizer class which can be changed to suit your needs.

If we take a look at the adapted datamodel, it will look like below:

image

One of the really cool things with XPO is that the datamodel will be changed automatically in the database once we have added the extra classes and fields. No need for migrations.

Configuring Identity to use the XPO Storage provider

If we create an ASP.NET Core project through the project template, you can see in the ~/Startup.cs file how the storage provider for E.F. is configured.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();
    // Add application services.
    services.AddTransient<IEmailSender, EmailSender>();
    services.AddMvc();
}
  

The AddDbContext<T>(..) method is an extension method which comes with Entity Framework, and the .AddEntityFrameworkStores<ApplicationDbContext>() is another extension method which comes with the E.F. storage provider.

I have created similar extension methods to first configure the XPO Datalayer, and also an extension method which enables you to inject a UnitOfWork into your controller or other parts of you application which support DI.

public static class XpoCoreExtensions
{
    public static IServiceCollection AddXpoDatabase(this IServiceCollection serviceCollection, string connectionName, string connectionString)
    {
        return serviceCollection.AddSingleton<XpoDatabase>(new XpoDatabase(connectionString, connectionName));
    }
    public static IServiceCollection AddXpoUnitOfWork(this IServiceCollection serviceCollection, string connectionName)
    {
        return serviceCollection.AddScoped<UnitOfWork>((sp) => sp.GetService<XpoDatabase>().GetUnitOfWork());
    }
}
  

The extension method for configuring the storage provider is a bit more complicated. In the ConfigureServices method, there is a call to services.AddIdentity<ApplicationUser, IdentityRole>(). In our case, these 2 types specify the DTO classes for our Storage provider.

When configuring the XPO Storage provider, we need to pass in the persistent types for the ApplicationUser and Role. To follow the same approach, I’d like to do that through generic type parameters. The initialization will look like this:

services
        .AddIdentity<ApplicationUser, ApplicationRole>()
        .AddXpoIdentityStores<XpoApplicationUser, XpoApplicationRole>()                
        .AddDefaultTokenProviders();
  

The extension method contains some code to determine the generic types and pass those into the XPUserStore and XPRoleStore constructors. It furthermore contains some code to determine the types which where specified in the AddIdentity method. You can check how this is done by viewing the soure code on GitHub.

How to use this in your own .NET Core App

Thanks to the Dependency Injection in .NET Core, it’s pretty straightforward to configure the XPO Storage provider. Once you have created a new project or opened an existing one, you can remove all references to Entity Framework including the using statements. Also all code related to Entity Framework can be removed like the DBContext class and the migrations files. This is basically everything in the ~/Data folder.

Next make sure you have configured your environment to use your personal DevExpress NuGet feed as described here.

We can now add a reference to the DX.Data.Xpo.Identity NuGet package to the project. This will get all dependencies like DevExpress.Xpo as well.

In the ~/Models/ApplicationUser.cs file, replace the ApplicationUser class with the following code:

public class ApplicationUser : XPIdentityUser<XpoApplicationUser>
{
    public ApplicationUser(XpoApplicationUser source) : base(source)
    {}

    public ApplicationUser(XpoApplicationUser source, int loadingFlags) : base(source, loadingFlags)
    {}

    public ApplicationUser()
    {}

    public override void Assign(object source, int loadingFlags)
    {
        base.Assign(source, loadingFlags);
        //XpoApplicationUser src = source as XpoApplicationUser;
        //if (src != null)
        //{
        //	// additional properties here
        //	this.PropertyA = src.PropertyA;
        //	// etc.				
        //}
    }
}
  

And add the following code to the file as well:

public class ApplicationRole : XPIdentityRole<XpoApplicationRole>
{
    public ApplicationRole(XpoApplicationRole source, int loadingFlags) : base(source, loadingFlags)
    {}

    public ApplicationRole(XpoApplicationRole source) : base(source)
    {}

    public ApplicationRole()
    {}
    public override void Assign(object source, int loadingFlags)
    {
        base.Assign(source, loadingFlags);
        //XpoApplicationRole src = source as XpoApplicationRole;
        //if (src != null)
        //{
        //	// additional properties here
        //	this.PropertyA = src.PropertyA;
        //	// etc.				
        //}
    }
}

// This class will be persisted in the database by XPO
// It should have the same properties as the ApplicationUser
[MapInheritance(MapInheritanceType.ParentTable)]
public class XpoApplicationUser : XpoDxUser
{
    public XpoApplicationUser(Session session) : base(session)
    {
    }
    public override void Assign(object source, int loadingFlags)
    {
        base.Assign(source, loadingFlags);
        //ApplicationUser src = source as ApplicationUser;
        //if (src != null)
        //{
        //	// additional properties here
        //	this.PropertyA = src.PropertyA;
        //	// etc.				
        //}
    }
}

[MapInheritance(MapInheritanceType.ParentTable)]
public class XpoApplicationRole : XpoDxRole
{
    public XpoApplicationRole(Session session) : base(session)
    {
    }
    public override void Assign(object source, int loadingFlags)
    {
        base.Assign(source, loadingFlags);
        //ApplicationUser src = source as ApplicationUser;
        //if (src != null)
        //{
        //	// additional properties here
        //	this.PropertyA = src.PropertyA;
        //	// etc.				
        //}
    }
}
  

The last thing we need to do is to change the service configuration in ~/Startup.cs to include the following:

public void ConfigureServices(IServiceCollection services)
{
    //Initialize XPODataLayer / Database
    services
        .AddXpoDatabase("DefaultConnection", Configuration.GetConnectionString("DefaultConnection"));
    //Initialize identity to use XPO
    services
        .AddIdentity<ApplicationUser, ApplicationRole>(options => {
            options.Lockout.AllowedForNewUsers = true;
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
            options.Lockout.MaxFailedAccessAttempts = 3;
        })
        .AddXpoIdentityStores<XpoApplicationUser, XpoApplicationRole>()                
        .AddDefaultTokenProviders();

    // Add other application services.
    services.AddTransient<IEmailSender, EmailSender>();
    services.AddMvc();
}
  

With these changes in place, we're ready to boot up our app running on XPO!

Did I mention that the source of this project is available on GitHub? Feel free to clone this repo and make a pull request in case you encounter an issue.

More Posts