Download the Solution Example here: CustomAssetTree.dbsln

  • Solution Name: Tree Asset Custom Data
  • Software Version: v10
  • Keywords: Asset Tree. Unified Namespace.

Summary

This example demonstrates how to create a fully customized asset tree with personalized icons and selected tags.


Technical Information

In this example, an asset tree is generated, starting with a root node labeled "Historian Tags." Custom icons are applied to each level of the tree, with different icons used for both individual tags and hierarchical levels. The tags displayed in this tree come from a specific data table within the historian database, and any modifications to the data are reflected dynamically in the tree structure.
The implementation steps are as follows:

Step 1. Asset Tree Initialization:
The root of the tree is labeled "Historian Tags."
The method InitializeCustom() is used to set up the tree with custom icons for different asset levels.

// Root name and icons
string rootName = "Historian Tags";
ImageBrush imageRoot = null;
ImageSource iconRoot = null;

// Initialize root icon
int imageID = await GetImageIDFromName("Resource1");
Stream stream = ConfigHelper.GetImage(imageID) as Stream;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();

// Assign icon to ImageBrush
imageRoot = new ImageBrush();
imageRoot.ImageSource = bitmapImage;
iconRoot = imageRoot.ImageSource;

// Initialize asset tree with root element
ElementItem tagRoot = new ElementItem(true, rootName, iconRoot, null, null);
control.InitializeCustom(new ElementItem[] { tagRoot }, this.GetObjectChildren);

The GetObjectChildren method plays a key role in managing and expanding the tree structure. It dynamically retrieves child elements from the historian or any associated asset, ensuring that the tree reflects any changes in the data.

Step 2. Custom Icons for Each Level:
Three types of icons are used: one for the root, another for levels, and one for individual tags.
These icons are retrieved using the GetImageIDFromName() method, which fetches the appropriate images from the project's resources.
ImageBrushes are created to hold these icons and are then applied to the tree elements accordingly.

int resourceImageID = await GetImageIDFromName("Resource1");
Stream resourceStream = ConfigHelper.GetImage(resourceImageID) as Stream;
BitmapImage resourceBitmapImage = new BitmapImage();
resourceBitmapImage.BeginInit();
resourceBitmapImage.StreamSource = resourceStream;
resourceBitmapImage.EndInit();

imageLevel = new ImageBrush();
imageLevel.ImageSource = resourceBitmapImage;
iconLevel = imageLevel.ImageSource;

Step 3. Tag Parsing and Hierarchical Structure:
Tags are fetched from the HistorianTable table 1  in the database.
Any tag names that contain ".Value" are automatically cleaned up to remove this suffix.
Tags that are not prefixed with "Tag." are modified accordingly to ensure consistent naming conventions.
The parsed tags are then added to a hash set to track which tags have been loaded into the asset tree.

DataTable table = await TK.ProjectDB.GetDataTableAsync("HistorianHistorianTags");
this.hashOfHistorian = new HashSet<string>();

foreach (DataRow row in table.Rows) {
    string tagName = TK.To<string>(row["TagName"]);
   
    // Remove ".Value" suffix if present
    if (tagName.EndsWith(".Value", StringComparison.CurrentCultureIgnoreCase)) {
        tagName = tagName.Remove(tagName.LastIndexOf('.'));
    }
   
    // Add "Tag." prefix if missing
    if (!tagName.StartsWith("Tag.", StringComparison.CurrentCultureIgnoreCase)) {
        tagName = "Tag." + tagName;
    }
   
    // Add tag to hash set
    this.hashOfHistorian.Add(tagName.ToLower());
}

Step 4. Tag Display in Tree:
As users expand the asset tree, the method GetObjectChildren() is called to fetch and display child elements (tags or levels) dynamically.
Tags with array structures (e.g., Tag[0], Tag[1]) are properly displayed in the tree with custom icons.

private async Task<ElementItem[]> GetObjectChildren(object sender, ElementItem item) {
    List<ElementItem> elements = new List<ElementItem>();
    string asset = (item.FullDisplayString ?? "").Replace('\\', '.').Replace('|', '.').Replace(".[", "[");

    // Fetch children based on asset type
    ObjRef objRef = TK.ObjServer.DB.GetObjRef(asset, false);
    if (objRef != null) {
        if (objRef.IsArrayBase) {
            // Handle array-based tags
            int[] dimensions = objRef.Dimensions;
            for (int i = 0; i < dimensions.Length; i++) {
                string strArray = "[" + i + "]";
                ElementItem elementItem = new ElementItem(true, strArray, iconTag, null, null);
                elements.Add(elementItem);
            }
        } else if (objRef.RunObj is ListObj) {
            // Handle list-based tags
            foreach (RunObj runObj in objRef.RunObj as ListObj) {
                string fullName = runObj.GetName().ToLower();
                if (this.hashOfHistorian.Contains(fullName)) {
                    ElementItem elementItem = new ElementItem(true, runObj.GetSimpleName(), iconLevel, null, null);
                    elements.Add(elementItem);
                }
            }
        }
    }
    return elements.ToArray();
}

And lastly, the event SelectedAssetEvent() handles the user’s selection of a tag, updating the display and showing the selected tag's name and value.

private void SelectedAssetEvent(object sender, string level, string asset) {
    string selectedTag = (level ?? "").Replace('\\', '.').Replace('|', '.').Replace(".[", "[");
    if (!string.IsNullOrEmpty(asset)) {
        selectedTag += "." + asset;
    }

    // Update the display with the selected tag
    this.CurrentDisplay.GetControl("SelectedTagText").Text = selectedTag;
}

Reference Information

→ See Asset Tree for more information.


In this section:

  • No labels