In this tutorial, two widgets are created, in order to show two different approaches.
The first widget is the Guid Generator widget: Most of the logic is defined in C# code, which is executed at the server side, and the Angular component at the client side takes only responsible for displaying the data. It also has a button to demonstrate the ability of requesting to the server to get fresh data.
The second widget is the Digital Clock widget: Most of the logic is defined in Javascript code, which is executed at the client side.
Despite the implementation different, both approaches share the same paradigm:
- Widget definition is defined in C# code, to specify:
- Models
- Widget type name
- Icon
- The Angular component to render the widget
- How to load data
- Angular component to render the widget in the browser
Note that examples in this tutorial are added to Litium.Accelerator.FieldTypes project.
- Create widget definition
- Create GUID Generator component
- Create Digital Clock component
- Component styles
- Register components in Accelerator Module
- Add translation text for Widget Definition
- Advanced use case
Create widget definition
Prior to Litium 7.4, all widget types had to be created in the back-office user interface, to define the name, the icon, the web control to display. Now everything is configured in code. A widget needs a Widget Definition, which should implement IWidgetDefinition. However, the easiest way is to inherit WidgetDefinitionBase. Here is the definition of GUID generator widget:
public class GuidGeneratorWidgetDefinition
: WidgetDefinitionBase<GuidGeneratorWidgetDefinition.DataModel, IWidgetRequest, GuidGeneratorWidgetDefinition.SettingsModel>
{
public override string IconClass => "fa-gift widget-icon--blue";
public override string ComponentName => "Accelerator#GuidGeneratorWidget";
public override int SortOrder => 1500;
public async override Task<DataModel> LoadDataAsync(SettingsModel settings, IWidgetRequest request = null)
{
settings = settings ?? new SettingsModel();
var data = new DataModel();
for (int i = 0; i < settings.Count; i++)
{
data.Guids.Add(Guid.NewGuid());
}
return data;
}
public class DataModel : IWidgetData
{
public IList<Guid> Guids { get; set; } = new List<Guid>();
}
public class SettingsModel : IWidgetSettings
{
public string Name { get; set; }
public int Count { get; set; } = 5;
}
}
GUID Generator widget display a configurable number of random GUID, with a button to refresh the list asynchronously:

There are a couple of things GuidGeneratorWidgetDefinition defines:
- DataModel: represents the data which is consumed by the Angular component to display in the user interface. In this example, its data has a list of GUID. The Angular component named GuidGeneratorWidget generates a list based on this.
- SettingsModel: represents the settings model of the widget. Every widget must have a Settings model, which implements at least the Name property. In this example, user can configure the Name, as well as the number of GUID that is required, by setting the Count field.
- IconClass: the field value should be a string of CSS classes, separated by space, for instance: fa-rss widget-icon--purple. Where:
- fa-rss is the FontAwesome icon class. Please check https://fontawesome.com/icons for more information. We support FontAwesome 5.
- widget-icon--purple is the color class. There are three built-in color classes that we can use: widget-icon--purple, widget-icon--green and widget-icon--blue.
- ComponentName: the Angular component name which is used to render the widget. This is a module-prefix-component-name which is similar to what we have as EditComponentName when creating custom field type. Since we will create a component in the Litium.Accelerator.FieldTypes project, the module prefix will be Accelerator. The actual component name is GuidGeneratorWidget, which we will create later.
- SortOrder: an interger number to define the sort order of the widget type in the list where user can select it and drag it to an area to create new widget.
- LoadDataAsync: to return the DataModel with a list of GUID. This method is invoked when the widget is loaded, as well as when Refresh button in the user interface is clicked.
- When the widget is loaded: the request parameter is null.
- When Refresh button is clicked, or when the javascript function requestData(payload) is called, the request parameter is the payload which is sent by requestData(payload) function.
Now let's take a look at the Digital Clock widget definition:
public class ClockWidgetDefinition : WidgetDefinitionBase<IWidgetData, IWidgetRequest, ClockWidgetDefinition.SettingsModel>
{
public override string IconClass => "fa-clock widget-icon--green";
public override string ComponentName => "Accelerator#ClockWidget";
public override int SortOrder => 2500;
public override async Task<IWidgetData> LoadDataAsync(SettingsModel settings, IWidgetRequest request = null)
{
return null;
}
public class SettingsModel : IWidgetSettings
{
public string Name { get; set; }
}
}
ClockWidgetDefinition defines the same number of things as GuidGeneratorWidgetDefinition does, but simpler.
- The clock does not have any data, so the widget definition does not declare the data model, it simply uses IWidgetData, and returns null in LoadDataAsync.
- It does not need any setting. But since every widget needs a name, we need a SettingsModel with Name field.
By having those widget definitions, we now can see our two new widgets in the list.

Note that the text is not translated, we will fix that later. Now let's create the Angular components to display the widgets.
Create GUID generator component
Widgets needs a component to render in the user interface. As we have defined the ComponentName for GuidGeneratorWidgetDefinition as Accelerator#GuidGeneratorWidget, we need to define GuidGeneratorWidget. Under the directory Litium.Accelerator.FieldTypes\src\Accelerator\components, create a folder named guid-generator-widget with two files:
// Litium.Accelerator.FieldTypes\src\Accelerator\components\guid-generator-widget\guid-generator-widget.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { NgRedux } from '@angular-redux3/store';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { BaseWidget, DashboardWidgetActions, IAppState } from 'litium-ui';
@Component({
selector: 'guid-generator-widget',
templateUrl: './guid-generator-widget.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GuidGeneratorWidget extends BaseWidget {
constructor(ngRedux: NgRedux<IAppState>, formBuilder: FormBuilder, widgetActions: DashboardWidgetActions) {
super(ngRedux, formBuilder, widgetActions);
}
createSettingsFormGroup(): FormGroup {
return this.formBuilder.group({
name: ['', Validators.required],
count: [5, Validators.required],
});
}
}
<!-- Litium.Accelerator.FieldTypes\src\Accelerator\components\guid-generator-widget\guid-generator-widget.component.html -->
<dashboard-widget [systemId]="systemId">
<div view>
<p>Generated GUID:s</p>
<button (click)="requestData(null)">Refresh</button>
<ul>
<li *ngFor="let guid of ((value | async)?.data?.guids || [])">{{guid}}</li>
</ul>
</div>
<div class="widget-setting" settings>
<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
<div class="widget-setting__item">
<label>Name:</label>
</div>
<div class="widget-setting__item">
<input type="text" formControlName="name" required />
</div>
<div class="widget-setting__item">
<label>Number of GUID:</label>
</div>
<div class="widget-setting__item">
<input type="number" formControlName="count" required />
</div>
<div class="widget-setting__button-container">
<button class="base__success widget-setting__button" type="submit" [disabled]="settingsForm.invalid">{{ 'general.action.save' | translate }}</button>
<button class="base__default widget-setting__button" type="button" (click)="toViewMode()">{{ 'general.action.cancel' | translate }}</button>
</div>
</form>
</div>
</dashboard-widget>
As you can see in the code above, there are two build-in classes that every widget should use to save development time as well as having the consistent interface and behavior with other widgets.
BaseWidget
This is the base class for every widget. The constructor and createSettingsFormGroup function are mandatory.
- The constructor does nothing except call the base class's constructor.
- createSettingsFormGroup: creates and returns the FormGroup to be used in the Settings form. In this case, we define the Settings form as having a Name field and a Count field. Both of them are mandatory. For more information of how the form works, please check Angular - Reactive Forms.
BaseWidget defines some functions and properties:
DashboardWidget
Represents a generic "box" for every widget. It defines two transclusion slots where the actual widget will define what to insert content to. This is the same technique as Litium uses to create a custom field type's component: <field-editor>. DashboardWidgets defines two slots: view and settings. GuidGeneratorWidget component defines its view interface in <div view> and its settings form in <div settings>. The only input DashboardWidget needs is the systemId of the widget. Since the widget inherits BaseWidget, we can simply set the input systemId as <dashboard-widget [systemId]="systemId">.
By using DashboardWidget, widget's implementation has these features by default:
- Drag and drop
- Loading indicator and error message notification
- Header with widget name, settings button, expand/collapse buttons, and remove button
Create Digital Clock component
Under the directory Litium.Accelerator.FieldTypes\src\Accelerator\components, create a folder named clock-widget with three files:
// Litium.Accelerator.FieldTypes\src\Accelerator\components\clock-widget\clock-widget.component.ts
import { Component, ChangeDetectionStrategy, OnInit, OnDestroy } from '@angular/core';
import { NgRedux } from '@angular-redux3/store';
import { FormGroup, FormBuilder } from '@angular/forms';
import { BaseWidget, DashboardWidgetActions, IAppState } from 'litium-ui';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'clock-widget',
templateUrl: './clock-widget.component.html',
styleUrls: ['./clock-widget.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ClockWidget extends BaseWidget implements OnInit, OnDestroy {
currentTime = new BehaviorSubject<string>('');
private _timerId;
constructor(ngRedux: NgRedux<IAppState>, formBuilder: FormBuilder, widgetActions: DashboardWidgetActions) {
super(ngRedux, formBuilder, widgetActions);
}
createSettingsFormGroup(): FormGroup {
return this.formBuilder.group({
name: [''],
});
}
ngOnInit() {
super.ngOnInit();
const timer = () => {
const date = new Date(),
h = this._fixedPad(date.getHours()),
m = this._fixedPad(date.getMinutes()),
s = this._fixedPad(date.getSeconds());
this.currentTime.next(`${h}:${m}:${s}`);
}
this._timerId = setInterval(timer, 1000);
}
private _fixedPad(num: number): string {
let str = num + '';
while (str.length < 2) {
str = '0' + str;
}
return str;
}
ngOnDestroy() {
super.ngOnDestroy();
this._timerId && clearInterval(this._timerId);
}
}
<!-- Litium.Accelerator.FieldTypes\src\Accelerator\components\clock-widget\clock-widget.component.html -->
<dashboard-widget [systemId]="systemId">
<div view>
<div class="clock-widget__time">{{ currentTime | async }}</div>
</div>
<div class="widget-setting" settings>
<form [formGroup]="settingsForm" (ngSubmit)="saveSettings()">
<div class="widget-setting__item">
<label>Name:</label>
</div>
<div class="widget-setting__item">
<input type="text" formControlName="name" required />
</div>
<div class="widget-setting__button-container">
<button class="base__success widget-setting__button" type="submit" [disabled]="settingsForm.invalid">{{ 'general.action.save' | translate }}</button>
<button class="base__default widget-setting__button" type="button" (click)="toViewMode()">{{ 'general.action.cancel' | translate }}</button>
</div>
</form>
</div>
</dashboard-widget>
/* Litium.Accelerator.FieldTypes\src\Accelerator\components\clock-widget\clock-widget.component.css */
.clock-widget__time {
text-align: center;
font-size: 60px;
letter-spacing: 7px;
}
The widget displays a digital clock and updates every second:

Component styles
There are built-in classes for the settings form where custom widget can use as we have in the above templates. To style the component in view mode, Angular Component Styles is recommended. Digital Clock component uses Component Styles, where styles are defined in clock-widget.component.css.
Register components in AcceleratorModule
In order to use, our two widgets need to be registered in the Accelerator Module and the project should be built just like how we build the custom field type. Here is the extensions.ts file after modified to declare GuidGeneratorWidget and ClockWidget:
// Litium.Accelerator.FieldTypes\src\Accelerator\extensions.ts
import { GuidGeneratorWidget } from './components/guid-generator-widget/guid-generator-widget.component';
import { ClockWidget } from './components/clock-widget/clock-widget.component';
@NgModule({
declarations: [
// custom components should be declared in 'declarations'
FieldEditorFilterFields,
GuidGeneratorWidget,
ClockWidget,
],
imports: [
CommonModule,
UiModule,
TranslateModule,
]
})
export class Accelerator {
constructor(private _reducerRegistery: ReducerRegistry) {
// register the custom reducer so custom action like FilterFieldsActions
// will be handled
this._reducerRegistery.register({ accelerator });
}
}
In Litium 8, the components need to be exposed in webpack.js, under ModuleFederationPlugin in order for the platform to access them:
new ModuleFederationPlugin({
name: "Accelerator",
filename: "remoteEntry.js",
exposes: {
Accelerator: "./Litium.Accelerator.FieldTypes/src/Accelerator/extensions.ts",
FieldEditorFilterFieldsComponent: "./Litium.Accelerator.FieldTypes/src/Accelerator/components/field-editor-filter-fields/field-editor-filter-fields.component",
GuidGeneratorWidget: "./Litium.Accelerator.FieldTypes/src/Accelerator/components/guid-generator-widget/guid-generator-widget.component.ts",
ClockWidget: "./Litium.Accelerator.FieldTypes/src/Accelerator/components/clock-widget/clock-widget.component.ts",
},
Add translation text for widget definitions
Litium.Accelerator.Mvc\Site\Resources\Administration\Administration.resx and Litium.Accelerator.Mvc\Site\Resources\Administration\Administration.sv-se.resx should be modified to add translation text for widget definitions. By adding these two nodes, we will have the text translated correctly:
<data name="dashboard.widgets.GuidGeneratorWidgetDefinition.name" xml:space="preserve">
<value>GUID generator</value>
</data>
<data name="dashboard.widgets.ClockWidgetDefinition.name" xml:space="preserve">
<value>Digital clock</value>
</data>
The pattern is "dashboard.widgets.[WidgetDefinitionClassName].name".

Advanced use case
Add scaffold data
There is cases where we want to display some pre-defined data in the Settings form of the Widget. It can be a Channel's drop-down box where the user can select the Channel Id, or a Country, Currency's drop-down boxes. We call this as a scaffold data. It is used to build the user interface, it should not be stored to the widget's settings in the database. When loading the widget's settings form, the server returns the list of Channel. Then the user can select a Channel from the list and send back the Channel's Id to the server.
To add a scaffold data to the settings form, we add a field to the Settings model, decorate it with NotMappedAttribute, Then in the Widget definition class, override the AddScaffoldDataAsync method, to populate the data for the scaffold field:
public class SettingsModel : IWidgetSettings
{
public string Name { get; set; }
public Guid Channel { get; set; }
[NotMapped] // mark the property as to be used to build the UI, but not to store to database
[JsonProperty(TypeNameHandling = TypeNameHandling.None, ItemTypeNameHandling = TypeNameHandling.None)]
public IEnumerable<ISelectItem> Channels { get; set; }
}
// in Widget Definition class
public override async Task<SettingsModel> AddScaffoldDataAsync(SettingsModel settingsModel)
{
settingsModel = await base.AddScaffoldDataAsync(settingsModel) ?? new SettingsModel();
settingsModel.Channels = _channelService.GetAll()
.Select(c => new SelectItem() { Text = c.GetEntityName(), Value = c.SystemId })
.Prepend(new SelectItem() { Text = string.Empty, Value = Guid.Empty });
return settingsModel;
}
Fields that are marked with NotMappedAttribute will not be persisted to the database, they are ignored.
Why does LoadDataAsync return Task<IWidgetData>?
You might notice we have LoadDataAsync and AddScaffoldDataAsync methods as asynchronous methods. The reason is, in some cases, we need to load data from a third party service or a WebAPI, or from another asynchronous method. It is easier for developers to call those async methods, to handle exceptions if LodaDataAsync and AddAcaffoldDataAsync are also async.
Versions prior to Litium 7.4
- Your widget class
- Edit panel
- View panel
- Widget icon
- Widget name
- Styles
- Scripts
1. Your widget class
- If the App_Code folder doesn’t contain a Widgets folder, please create it. Also, create a code file with the name SampleWidget.cs.

- Add the class SampleWidget into this file and derive it from the DashboardWidget class. This is an abtract class and you have to implement two properties:
- ViewPanelPath
- EditPanelPath
- Override the IconClass property to set up your own icon. Please refer to the WidgetIcon section for more information.
Each Dashboard widget consists of two parts, the view panel, and the edit panel. These parts should be user controls (.ascx files). Accordingly, the ViewPanelPath property should return the path to the view panel and the EditPanelPath property should return the path to the edit panel.
- Create an inner class with the name Data (or use another name) and mark it as Serializable2. In the Data class, define the properties you want to use in the widget. The object Data class will be serialized and saved into the database. The full class should look like this:
using Litium.Foundation.Modules.Dashboard.Plugins.Widgets.Default;
using System;
namespace Web.App_code.Widgets
{
public class SampleWidget : DashboardWidget
{
private const string EDIT_PATH = "~/Litium/Dashboard/WebUserControls/Widgets/Edit/EditSampleWidgetControl.ascx";
private const string VIEW_PATH = "~/Litium/Dashboard/WebUserControls/Widgets/View/ViewSampleWidgetControl.ascx";
private const string ICON_CLASS = "fa-rss widget-icon--purple";
/// <summary>
/// Path to edit panel.
/// </summary>
/// <value></value>
public override string EditPanelPath
{
get { return EDIT_PATH; }
}
/// <summary>
/// Gets the icon class.
/// </summary>
/// <value>The icon class.</value>
public override string IconClass
{
get { return ICON_CLASS; }
}
/// <summary>
/// Path to view panel.
/// </summary>
/// <value></value>
public override string ViewPanelPath
{
get { return VIEW_PATH; }
}
/// <summary>
/// The data.
/// </summary>
[Serializable]
public class Data
{
/// <summary>
/// Arguments
/// </summary>
public string Arguments { get; set; }
}
}
}
2. Edit panel
Under Site/Dashboard/WebUserControls/Widgets/Edit there you can find saved edit panels for different widgets. If this path doesn’t exist, you need to create it. Create a new user control for your SampleWidget with the name EditSampleWidgetControl.ascx.

Add markup:
<%@ Control Language="C#" AutoEventWireup="true" Inherits="EditSampleWidgetControl" Codebehind="EditSampleWidgetControl.ascx.cs" %>
<div class="editsamplewidget">
<div class="litBoldText">Arguments</div>
<asp:TextBox ID="c_textBoxArgument" runat="server" />
</div>
The EditPanel control should implement the IWidgetEditPanel interface. This interface contains two methods, GetPanelData and SetPanelData.
namespace Litium.Foundation.Modules.Dashboard.Plugins.Widgets
{
/// <summary>
/// Widget edit panel.
/// </summary>
public interface IWidgetEditPanel
{
/// <summary>
/// Gets the panel data.
/// </summary>
/// <returns>Return an object with the panel data.</returns>
object GetPanelData();
/// <summary>
/// Sets the panel data.
/// </summary>
/// <param name = "data">The data.</param>
void SetPanelData(object data);
}
}
GetPanelData is called when the user clicks the Save or Cancel buttons. It should return a Data object that will be serialized and saved into the database. It should be the object of the Data class that we defined in step 1 of this tutorial.
SetPanelData is called upon widget initialization and gets data as a parameter. This is the data object that was returned from the GetPanelData method. You have to cast it to your object. This object will be Null unless you click the Save button or save it from the view panel. Implement this control in the following way:
using Litium.Foundation.Modules.Dashboard.Plugins.Widgets;
using System;
using System.Web.UI;
using Web.App_code.Widgets;
public partial class EditSampleWidgetControl : UserControl, IWidgetEditPanel
{
protected void Page_Load(object sender, EventArgs e)
{
}
public object GetPanelData()
{
var data = new SampleWidget.Data();
data.Arguments = c_textBoxArgument.Text;
return data;
}
public void SetPanelData(object data)
{
var obj = data as SampleWidget.Data;
if (obj != null)
{
c_textBoxArgument.Text = obj.Arguments;
}
}
}
3. View Panel
Under Site/Dashboard/WebUserControls/Widgets/View (if this path doesn’t exist please create it) there are saved View panels for different widgets. Create a new user control for your SampleWidget with the name ViewSampleWidgetControl.ascx.

And add markup.
<%@ Control Language="C#" AutoEventWireup="true" Inherits="ViewSampleWidgetControl" CodeBehind="ViewSampleWidgetControl.ascx.cs" %>
<div class="viewsamplewidget">
<div>
<label>Argument value is</label>
<asp:Label runat="server" ID="c_labelArgument"></asp:Label>
</div>
<asp:TextBox ID="c_textBoxArgument" runat="server" />
<asp:Button runat="server" OnClick="ButtonChangeArgumentOnClick" Text="Change argument" ID="c_buttonChangeText"/>
</div>
The ViewPanel control should implement the IWidgetViewPanel interface. This interface contains one Initialize method and one WidgetID property.
using System;
namespace Litium.Foundation.Modules.Dashboard.Plugins.Widgets
{
/// <summary>
/// Widget view panel.
/// </summary>
public interface IWidgetViewPanel
{
/// <summary>
/// Initializes the specified data.
/// </summary>
/// <param name = "data">The data.</param>
void Initialize(object data);
/// <summary>
/// WidgetInfoID.
/// </summary>
Guid WidgetID { get; set; }
}
}
Initialize is called at view panel initialization and receives data as a parameter. The Data parameter is the object of the type you declared in step 1 of this tutorial (Data class). This object will be Null unless you created and saved it, or clicked the Save button in the edit panel.
WidgetID contains the widget id and you can use it to save or edit data from the view panel. To get the widget to use the ModuleDashboard.Instance.DashboardWidgets.Get method.
To save the widget use the SetData method of the returned object. Implement this control in the following way:
using Litium.Foundation.GUI;
using Litium.Foundation.Modules.Dashboard;
using Litium.Foundation.Modules.Dashboard.Carriers;
using Litium.Foundation.Modules.Dashboard.Plugins.Widgets;
using System;
using System.Web.UI;
using Web.App_code.Widgets;
public partial class ViewSampleWidgetControl : UserControl, IWidgetViewPanel
{
public Guid WidgetID { get; set; }
private const string DEFAULT_VALUE = "Default value";
protected void Page_Load(object sender, EventArgs e)
{
}
public void Initialize(object data)
{
var obj = data as SampleWidget.Data;
if (obj != null)
{
c_labelArgument.Text = obj.Arguments;
}
else
{
c_labelArgument.Text = DEFAULT_VALUE;
}
}
protected void ButtonChangeArgumentOnClick(object sender, EventArgs e)
{
// Create data object
var data = new SampleWidget.Data();
data.Arguments = c_textBoxArgument.Text;
if (ModuleDashboard.Instance.DashboardWidgets.Exists(WidgetID, FoundationContext.Current.Token))
{
var dashboardWidgetInfo = ModuleDashboard.Instance.DashboardWidgets.Get(WidgetID, FoundationContext.Current.Token);
dashboardWidgetInfo.SetData(data, FoundationContext.Current.Token);
}
else
{
DashboardWidgetInfoCarrier widgetInfoCarrier = new DashboardWidgetInfoCarrier(
WidgetID, FoundationContext.Current.UserID, "widget title", typeof(SampleWidget).GetType(), data);
ModuleDashboard.Instance.DashboardWidgets.Create(widgetInfoCarrier, FoundationContext.Current.Token);
}
Initialize(data);
}
}
4. Widget icon
The Widget icon is defined in the code. For example, the SampleWidget in step 1 above is defined in the IconClass field. The field value should be a string of CSS classes, separated by space, for instance: fa-rss widget-icon--purple. Where:
- fa-rss is the FontAwesome icon class. Please check https://fontawesome.com/icons for more information. We support FontAwesome 5.
- widget-icon--purple is the color class. There are three built-in color classes that we can use: widget-icon--purple, widget-icon--green and widget-icon--blue.
5. Widget Name
- Open the project and go to the resource files at: Web > Litium > Resources. They are called ModuleDashboard.resx for EN-US language and ModuleDashboard.sv-se.resx for sv-se language.
- Add a new resource following the format [WidgetTypeName]Name. In this case, for example, the resource line will be SampleWidgetName.

6. Styles
Create the file SampleWidget.css in the Site/Dashboard/Styles folder. This file will be connected automatically.

There are four zones on the dashboard, top, left, right and bottom.

Widgets may look different depending on which zone they are in. Use the following selectors to write the widget style for the different zones:
- Top: .dropTopContainerForWidget
- Left: .dropLeftContainerForWidget
- Right: .dropRightContainerForWidget
- Bottom: .dropBottomContainerForWidget
Add styles to SampleWidget.css in the following way:
.editsamplewidget {
}
.viewsamplewidget {
}
.dropTopContainerForWidget .viewsamplewidget
{
border: 3px solid red;
}
.dropLeftContainerForWidget .viewsamplewidget
{
border: 3px solid blue;
}
.dropRightContainerForWidget .viewsamplewidget
{
border: 3px solid black;
}
.dropBottomContainerForWidget .viewsamplewidget
{
border: 3px solid green;
}
The result on the dashboard will look like this:

7. Scripts
You can also add JavaScript files into the Site/Dashboard/Scripts folder. (If this path doesn’t exist, please create it). They are not connected automatically. The widget behaves as a normal .NET web user control. How to attach a client script is explained in this article.
Go to the Dashboard area and your widget should be in the list at the top of the page. Drag and drop it in the layout.