Table of Contents

Creating custom screens

How to create custom screens for the administration UI

When you want to create your own screens, there are a few things you need to consider. First we need to figure out where the data to the screen should come from. In DynamicWeb data on a Screen comes from a Query, so we need to see if any of the built-in Queries can delivery the data that's needed or if you need to implement your own.

Next we need to identify if our screen need to manipulate data, because if that's needed, then you need to implement your own commands

Query

If you need to implement your own Query, there's two different types of Queries

  • One that return a single model
  • One the return a list of models

Single model

So when you want to implement a query that only return a single model, you need to inherit from the DataQueryModelBase<TModel> class to indicate that this is a Query. When inheriting, you need to define which Model this Query will be returning.

public sealed class PageByNavigationTagQuery : DataQueryModelBase<PageDataModel>
{
    public string NavigationTag { get; set; } = "";

    public override PageDataModel GetModel()
    {
        //Find the page using the domain api
        var page = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(NavigationTag)

        //Map the page into a PageDataModel
        return MappingService.Map<PageDataModel>(page);
    }
}

List of models

When you want your query to return a list of models, there are two different ways to do it.

The first option is to create a simple Query, which just returns an IEnumerable<TModel>. This one doesn't require much code, and it's easy to understand, but the downside of this option is that you have no context information on the Model about, which conditions the Query have used find the information, which can sometimes be needed when you need to show the information on the Screen.

The simple Query is implemented by inheriting from the DataQueryListBase<TModel, TDomainModel>

public sealed class PagesByParentIdQuery : DataQueryListBase<PageDataModel, Page>
{
    public int ParentId { get; set; }
    protected override IEnumerable<Page> GetListItems()
    {
        return Dynamicweb.Content.Services.Pages.GetPagesByParentID(ParentId);
    }
}

The other option is to define your own specific ListDataModel, and then your query will return that ListDataModel.

public sealed class SubPagesListDataModel : DataListViewModel<PageDataModel>
{
    public int ParentId{get;set;}
}

Now that we have our ListDataModel, then we can implement the Query, which now need to inherit from DataQueryListBase<TModel, TDomainModel, TListModel>.

public sealed class PagesByParentIdQuery : DataQueryListBase<PageDataModel, Page, SubPagesListDataModel>
{
    protected override SubPagesListDataModel MakeListModel() => new()
    {
        ParentId = ParentId,
    };

    protected override IEnumerable<Page> GetListItems()
    {
        return Dynamicweb.Content.Services.Pages.GetPagesByParentID(ParentId);
    }
}

Commands

There's two different kinds of Commands, that can be implemented

  • Command with a Model
  • Command without a Model

Command with Model

A Command with a Model is only used for saving data on an EditScreen. You set the Model type when you implement the Command. When the Command is being called, then the Model will contain the information that has been posted to it by the EditScreen. That way you will have access to all the information which can then be transformed into a domain model persisted in the database.

To implement a Command with a Model you need to inherit from CommandBase<TModel>, where TModel defines which Model this Command will be working on.

public sealed class FolderSaveCommand : CommandBase<FolderModel>
{
    public override CommandResult Handle()
    {
        //Getting the model from the command
        var model = GetModel();

        //Fetching the model from the domain, if we have a valid ID, otherwise we will create a new Page
        var folder = model.Id == 0 ? new Page(model.AreaId, model.ParentId) : Services.Pages.GetPage(model.Id);

        //Check permission level
        var level = model.Id == 0 ? PermissionLevel.Create : PermissionLevel.Edit;
        folder.ValidatePermission(level);

        //Mapping the values from the Model to the domain model
        folder.IsFolder = true;
        folder.MenuText = model.Name;
        folder.ActiveFrom = DateTime.Now;
        folder.Active = true;
        folder.Allowsearch = false;
        folder.Allowclick = false;
        folder.ShowInSitemap = false;
        folder.ShowInLegend = false;
        folder.ShowUpdateDate = false;
        folder.UrlIgnoreForChildren = true;
        folder.HideForPhones = true;
        folder.HideForTablets = true;
        folder.HideForDesktops = true;

        //Saving the domain model
        Services.Pages.SavePage(folder);

        //Return a result for indicating that the commands executed successfully
        return new CommandResult
        {
            Model = model,
            Status = CommandResult.ResultType.Ok
        };
    }
}

Command without Model

For all other Commands, you just need to inherit from CommandBase without any generic arguments. When we are working with a Command without a Model, we have no information about this entity we are working on. So this kind of Command would need to have some properties that can contain an identifier for the entity we want to manipulate.

public sealed class AreaDeleteCommand : CommandBase
{
    public int Id { get; set; }

    public override CommandResult Handle()
    {
        //Find the domain model matching the given Id
        var area = Services.Areas.GetArea(Id);

        //Check if the user has permissions to do the action
        area.ValidatePermission(PermissionLevel.Delete);

        //Delete the area
        var response = Services.Areas.DeleteArea(Id);

        //Return a result for indicating that the commands executed successfully
        var result = new CommandResult
        {
            Status = response.Succeeded ? CommandResult.ResultType.Ok : CommandResult.ResultType.Error,
            Message = response.Succeeded ? "" : response.Message
        };

        return result;
    }
}

Customized Screens

Now that we have created the Queries and Commands needed for our Screens, we can start implementing the actual screens. DynamicWeb provides three built-in ScreenTypes, which can be used for creating a custom screen:

  • ListScreen
  • EditScreen
  • OverviewScreen

When you use one of the standard ScreenTypes, you don’t have to worry about the look and feel of your screen, as we’ve taken care of that part – you just need to concentrate about your data.

All of the standard ScreenTypes are located in Dynamicweb.CoreUI.Screens, and contain methods that you need to override to put something on the screen.

In the example below we inherit from ListScreenBase – in this case for the HealthProviderCheckDataModel – and use two overrides to add Name and Columns to the screen:

public sealed class HealthProviderCheckListScreen : ListScreenBase<HealthProviderCheckDataModel>
{
    // Override default values
    protected override string GetScreenName() => "Health checks";

    protected override IEnumerable<ListViewMapping> GetViewMappings()
    {
        var rowMapping = new RowViewMapping
        {
            Columns = new System.Collections.Generic.List<ModelMapping>
            {
                CreateMapping(p => p.State),
                CreateMapping(p => p.Name),
                CreateMapping(p => p.Description),
                CreateMapping(p => p.Count)
            }
        };

        return new ListViewMapping[] { rowMapping };
    }
}
To top