---
title: Developing data source web parts
related:
  - https://docs.kentico.com/k9/developing-websites/loading-and-displaying-data-on-websites.md
  - https://docs.kentico.com/k9/custom-development/developing-web-parts/creating-new-web-parts.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

Data source [web parts](https://docs.kentico.com/k9/custom-development/developing-web-parts.md) are responsible for loading data. You can display the retrieved data on the website by connecting the data source to a listing web part.

To develop custom data source web parts, you need to create two separate user controls:

1. The first control loads the data. This control must inherit from **CMSBaseDataSource** (found in the _CMS.Controls_ namespace).
2. The second user control contains the first control in its markup and serves as the source file for the actual web part. Must inherit from **CMSAbstractWebPart**.

## Example - Creating a user data source

The following example shows how to create a custom data source that loads user data from the Kentico database.

Open your web project in Visual Studio and create two user controls according to the sections below.

> **Note:** **Note**
>
> Set the values of the _Inherits_ attributes in the user control declarations and the class names according to the names and locations of your controls. If your Kentico project was installed as a web application, you need to rename the _CodeFile_ attributes to _Codebehind_.
>
> The code in the example assumes that the control is placed inside the **\~/CMSGlobalFiles** folder (you may need to manually create this folder if you wish to use the default code). When developing your own controls, you can use another location and adjust the _Inherits_ attribute and the corresponding class names as required.

```html title="DataSourceControl.ascx"

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="DataSourceControl.ascx.cs" Inherits="CMSGlobalFiles_DataSourceControl" %>
<%--Leave the markup of the first user control empty--%>

```

```csharp title="DataSourceControl.ascx.cs"

using System;

using CMS.Controls;
using CMS.Membership;
using CMS.FormControls;

/// <summary>
/// User data source. Loads all users according to a specified Where condition.
/// </summary>
public partial class CMSGlobalFiles_DataSourceControl : CMSBaseDataSource
{
    /// <summary>
    /// Assigns a handler for the 'OnFilterChanged' event if there is a filter attached to the data source.
    /// You do not need to implement this method if you do not plan to use filters with the data source web part.
    /// </summary>
    protected override void OnInit(EventArgs e)
    {
        if (SourceFilterControl != null)
        {
            SourceFilterControl.OnFilterChanged += new ActionEventHandler(DataFilter_OnFilterChanged);
        }

        base.OnInit(e);
    }

    /// <summary>
    /// Handles the OnFilterChanged event.
    /// You do not need to implement this method if you do not plan to use filters with the data source web part.
    /// </summary>
    void DataFilter_OnFilterChanged()
    {
        // Clears the old data
        InvalidateLoadedData();

        // Raises the change event
        this.RaiseOnFilterChanged();
    }

    /// <summary>
    /// Loads the data of the data source.
    /// </summary>
    protected override object GetDataSourceFromDB()
    {
        // Initializes the data properties according to the filter settings
        if (SourceFilterControl != null)
        {
            SourceFilterControl.InitDataProperties(this);
        }

        // Loads the data. The WhereCondition and OrderBy properties are inherited from the parent class.
        DataSource = UserInfoProvider.GetUsersDataWithSettings()
                                        .Where(WhereCondition)
                                        .OrderBy(OrderBy);

        return DataSource;
    }
}

```

```html title="DataSourceWebPart.ascx"

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="~/CMSGlobalFiles/DataSourceWebPart.ascx.cs" Inherits="CMSGlobalFiles_DataSourceWebPart" %>

<%@ Register Src="~/CMSGlobalFiles/DataSourceControl.ascx" TagPrefix="cms" TagName="DataSourceControl" %>

<cms:DataSourceControl ID="srcUsers" runat="server" />

```

```csharp title="DataSourceWebPart.ascx.cs"

using CMS.PortalControls;
using CMS.Helpers;

public partial class CMSGlobalFiles_DataSourceWebPart : CMSAbstractWebPart
{
    /// <summary>
    /// Gets or sets the value of the WhereCondition web part property.
    /// </summary>
    public string WhereCondition
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("WhereCondition"), "");
        }
        set
        {
            this.SetValue("WhereCondition", value);
            srcUsers.WhereCondition = value;
        }
    }

    /// <summary>
    /// Gets or sets the value of the OrderBy web part property.
    /// </summary>
    public string OrderBy
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("OrderBy"), "");
        }
        set
        {
            this.SetValue("OrderBy", value);
            srcUsers.OrderBy = value;
        }
    }

    /// <summary>
    /// Gets or sets the value of the FilterName web part property.
    /// </summary>
    public string FilterName
    {
        get
        {
            return ValidationHelper.GetString(this.GetValue("FilterName"), "");
        }
        set
        {
            this.SetValue("FilterName", value);
            srcUsers.SourceFilterName = value;
        }
    }

    /// <summary>
    /// Content loaded event handler.
    /// </summary>
    public override void OnContentLoaded()
    {
        base.OnContentLoaded();
        SetupControl();
    }

    /// <summary>
    /// Initializes the properties of the internal data source control.
    /// </summary>
    protected void SetupControl()
    {
        if (this.StopProcessing)
        {
            // Does nothing if the web part is disabled
        }
        else
        {
            this.srcUsers.WhereCondition = this.WhereCondition;
            this.srcUsers.OrderBy = this.OrderBy;

            // Sets the inner data source's name according to the web part's 'Web part control ID' property.
            // This allows listing web parts to connect to the inner control.
            // The identifier property is named 'FilterName' because data sources inherit the property from 
            // a base class shared with filter objects.
            this.srcUsers.FilterName = (string)this.GetValue("WebPartControlID");

            // Connects the attached filter, as specified by the 'Filter name' property of the web part
            this.srcUsers.SourceFilterName = this.FilterName;
        }
    }
}

```

If your Kentico project was installed as a web application, you must **Build** the project.

### Registering the web part

Register the **DataSourceWebPart.ascx** control as a new web part in Kentico. 

1. Open the **Web parts** application.
2. Create a new web part. See [Creating new web parts](https://docs.kentico.com/k9/custom-development/developing-web-parts/creating-new-web-parts.md) for details.
3. Type _Custom user data source_ as the web part's **Display name**.
4. Set the **Type** of the web part to _Data source_ on the **General** tab.
5. On the **Properties** tab, define the following properties for the web part:

   - **Field name**: OrderBy
   - **Data type**: Text
   - **Size**: 500
   - **Field caption**: SQL Order by clause
   - **Form control**: Order by
   - **Field name**: WhereCondition
   - **Data type**: Text
   - **Size**: 500
   - **Field caption**: SQL Where condition
   - **Form control**: Where condition
   - **Field name**: FilterName
   - **Data type**: Text
   - **Size**: 200
   - **Field caption**: Filter name
   - **Form control**: Text box

### Result

The custom data source web part is now ready and you can try out its functionality.

Open the **Pages** application, create a new page and add the following web parts on the **Design** tab:

| Web part                | Instructions                                                                                                                                                                                                                                 |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom user data source | Type _UsersFilter_ into the **Filter name** property.                                                                                                                                                                                        |
| Users filter            | Leave the default property values. The **Web part control ID** is set to _UsersFilter_ by default, which connects the filter to the custom data source.                                                                                      |
| Basic repeater          | Enter the ID of the Custom user data source web part into the **Data source name** property: _CustomUserDataSource_<br>Set an appropriate **Transformation name** for displaying users, for example: _Community.Transformations.MembersList_ |

The page displays a list of all users in the system. You can modify the list by configuring the _SQL Where condition_ and _SQL Order by clause_ properties of the data source. Visitors can dynamically narrow down the list directly on the page using the connected filter.
