Extending ViewModels
A ViewModel is one of the ways you can pass information from a DynamicWeb 10 solution to the frontend - read about templating in DynamicWeb here. ViewModels are fast in part because they only contain the properties you need in a context, but if you need access to non-standard information on a ViewModel you can extend our existing ViewModels with new properties.
When you need to extend a ViewModel, you first need to identify which ViewModel you want to extend.
- Here is a list of all the ViewModels which inherit from ViewModelBase
- Here is a list of the ViewModels inheriting from FillableViewModelBase
When you know which ViewModel you want to extend, you can open your favorite IDE and start implementing.
To start extending an existing ViewModel, you need to create a new class, that inherits from the ViewModel, you want to extend. In the new class, you can override an existing method or property by using the new
keyword or add completely new properties and methods, which can return the data you need.
You can read about C# Extension Methods in general here.
It's not possible to change the original values on the ViewModel, because they will be overwritten as part of the ViewModel instantiation, so if you need to return some different data in a property, you need to create your own property in your class, and have that return the correct data.
using Dynamicweb.Frontend;
namespace ViewModelExtensibility
{
public class PageInfoViewModelExtended : PageInfoViewModel
{
private object _instance;
public PageInfoViewModelExtended()
{
//this is initialisation. The model does not have anyting set at this point.
//Initialize objects, i.e. services, that can be used throughtout the instance - i.e. to get addtional data.
_instance = new object();
//This will not work as the header will be overriden later in the initialisation process of this object.
base.Name = "Set name in constructor does not work";
}
/// <summary>
/// Take over default property of the base viewmodel. Suitable to change default behavior
/// </summary>
public new string Name
{
get
{
return $"{base.Name} changed1";
}
}
/// <summary>
/// Addtional data property available in template. Suitable for additional data
/// </summary>
public string MetaTitleOrName
{
get
{
//Retrive the actual page from the database to access information, which is not available on the PageViewModel
var page = Dynamicweb.Content.Services.Pages.GetPage(base.ID);
if (!string.IsNullOrEmpty(page.MetaTitle))
{
return page.MetaTitle;
}
else
{
return page.GetDisplayName();
}
}
}
}
}
Item-based ViewModels
In some cases you have ViewModels, which is based on an Item. If you only want to extend a ViewModel working on a specific ItemType, then you can do that by adding the AddInName, with the SystemName of the ItemType, to the class.
using System;
using System.Collections.Generic;
using System.Text;
using Dynamicweb.Frontend;
using Dynamicweb.Rendering;
using Dynamicweb.Extensibility.AddIns;
namespace Dynamicweb.Examples.Rendering
{
/// <summary>
/// Custom paragraph viewmodel for specific item type. Use AddInName attribute with the item type name. See <see cref="AddInName"/>
/// </summary>
[AddInName("MultiPurposeParagraphInfo")]
public class SwiftPosterViewModel : ParagraphViewModel
{
private object _instance;
public SwiftPosterViewModel()
{
//this is initialisation. The model does not have anyting set at this point.
//Initialize objects, i.e. services, that can be used throughtout the instance - i.e. to get addtional data.
_instance = new object();
//This will not work as the header will be overriden later in the initialisation process of this object. Don't try to initialize any properties of the base class.
base.Header = "Seting the header inside the constructor does not work";
}
/// <summary>
/// Take over default property of the base viewmodel. Suitable to change default behavior
/// </summary>
public new string Header
{
get
{
return $"{base.Header} changed1";
}
}
/// <summary>
/// Addtional data property available in template. Suitable for additional data
/// </summary>
public string NameOrTile
{
get
{
if (Item.GetRawValue("Title") is object)
{
return Item.GetRawValue("Title").ToString();
}
else
{
return $"From nameOrTitle prop: {Header}";
}
}
}
/// <summary>
/// Method to call from template using data from the model instance. Suitable for rendering logic
/// </summary>
/// <returns></returns>
public string GetPosterPadding()
{
string posterPadding = Item.GetRawValueString("ContentPadding", string.Empty);
string posterPaddingClass = "px-3";
switch (posterPadding)
{
case "none":
posterPaddingClass = " p-3 px-xl-3 py-xl-4";
break;
case "small":
posterPaddingClass = " p-3 p-xl-4";
break;
case "large":
posterPaddingClass = " p-4 p-xl-5";
break;
}
return posterPaddingClass;
}
}
[AddInName("MultiPurposeParagraphInfo")]
public class SwiftPosterInfoViewModel : ParagraphInfoViewModel
{
/// <summary>
/// Take over default property of the base viewmodel. Suitable to change default behavior
/// </summary>
public new string Name
{
get
{
return $"{base.Name} changed2";
}
}
/// <summary>
/// Addtional data property available in template. Suitable for additional data
/// </summary>
public string NameOrTile
{
get
{
if (Item.GetRawValue("Title") is object)
{
return Item.GetRawValue("Title").ToString();
}
else
{
return $"From nameOrTitle prop: {Name}";
}
}
}
/// <summary>
/// Method to call from template using data from the model instance. Suitable for rendering logic
/// </summary>
/// <returns></returns>
public string GetPosterPadding()
{
string posterPadding = Item.GetRawValueString("ContentPadding", string.Empty);
string posterPaddingClass = "px-3";
switch (posterPadding)
{
case "none":
posterPaddingClass = " p-3 px-xl-3 py-xl-4";
break;
case "small":
posterPaddingClass = " p-3 p-xl-4";
break;
case "large":
posterPaddingClass = " p-4 p-xl-5";
break;
}
return posterPaddingClass;
}
}
}