How to Prevent Image Duplication in Alarm Manager with Custom Display

I want to display custom images and text effects in Alarm Manager, but the images are repeating.

I referred to the Smart Client Alarm Preview plugin example and added two image display frames.

Code:

AlarmPreviewWpfUserControl.xaml

<UserControl x:Class="AlarmPreview.Client.AlarmPreviewWpfUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:AlarmPreview.Client"
             mc:Ignorable="d"              
             d:DesignHeight="450" d:DesignWidth="600">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
 
        <!-- First Row - Large Snapshot -->
        <Border Grid.Row="0" Margin="5" BorderBrush="Gray" BorderThickness="1">
            <Image x:Name="largeSnapshotImage" Stretch="Uniform"/>
        </Border>
 
        <!-- Second Row - Two Columns -->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="200"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
 
            <!-- Small Snapshot -->
            <Border Grid.Column="0" Margin="5" BorderBrush="Gray" BorderThickness="1">
                <Image x:Name="smallSnapshotImage" Stretch="Uniform"/>
            </Border>
 
            <!-- Text Content -->
            <Border Grid.Column="1" Margin="5" BorderBrush="Gray" BorderThickness="1">
                <TextBox x:Name="textBox1"
                        Margin="5"
                        TextWrapping="Wrap" 
                        HorizontalAlignment="Stretch" 
                        VerticalAlignment="Stretch" 
                        AcceptsReturn="True" 
                        IsReadOnly="True"
                        BorderThickness="0"
                        Background="Transparent"
                        VerticalScrollBarVisibility="Auto"/>
            </Border>
        </Grid>
    </Grid>
</UserControl>

add some code

using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using VideoOS.Platform.Data;
using VideoOS.Platform;
using System.IO;
using System.Collections.Generic;
using System.Linq;
 
namespace AlarmPreview.Client
{
    /// <summary>
    /// Interaction logic for AlarmPreviewWpfUserControl.xaml
    /// </summary>
    public partial class AlarmPreviewWpfUserControl : UserControl
    {
        public AlarmPreviewWpfUserControl(object alarmOrBaseEvent)
        {
            InitializeComponent();
 
            textBox1.Text = GetContents(alarmOrBaseEvent);
            SetupSnapshots(alarmOrBaseEvent);
        }
 
        private void SetupSnapshots(object alarmOrBaseEvent)
        {
            largeSnapshotImage.Source = null;
            smallSnapshotImage.Source = null;
 
            IList<Snapshot> snapshotList = null;
 
            if (alarmOrBaseEvent is AnalyticsEvent analyticsEvent)
            {
                snapshotList = analyticsEvent.SnapshotList;
            }
            else if (alarmOrBaseEvent is Alarm alarm && (snapshotList == null || !snapshotList.Any()))
            {
                snapshotList = alarm.SnapshotList;
            }
            if (snapshotList != null && snapshotList.Count > 0)
            {
                var largeSnapshot = snapshotList[0];
                try
                {
                    byte[] imageData = largeSnapshot.Image;
                    if (imageData != null && imageData.Length > 0)
                    {
                        var bitmapImage = new BitmapImage();
                        bitmapImage.BeginInit();
                        bitmapImage.StreamSource = new MemoryStream(imageData);
                        bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                        bitmapImage.EndInit();
                        bitmapImage.Freeze(); 
                        largeSnapshotImage.Source = bitmapImage;
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"Error loading large snapshot: {ex.Message}");
                }
 
                if (snapshotList.Count > 1)
                {
                    var smallSnapshot = snapshotList[1];
                    try
                    {
                        byte[] imageData = smallSnapshot.Image;
                        if (imageData != null && imageData.Length > 0)
                        {
                            var bitmapImage = new BitmapImage();
                            bitmapImage.BeginInit();
                            bitmapImage.StreamSource = new MemoryStream(imageData);
                            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                            bitmapImage.EndInit();
                            bitmapImage.Freeze(); 
                            smallSnapshotImage.Source = bitmapImage;
                        }
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine($"Error loading small snapshot: {ex.Message}");
                    }
                }
            }
        }
 
        private string GetContents(object alarmOrBaseEvent)
        {
            Alarm alarm = alarmOrBaseEvent as Alarm;
            if (alarm != null)
            {
                string alarmInText = "Message: {0}" + Environment.NewLine +
                    "Definition {8}" + Environment.NewLine +
                    "Type: {1}" + Environment.NewLine +
                    "Source: {2}" + Environment.NewLine +
                    "CustomTag: {3}" + Environment.NewLine +
                    "Object {4}" + Environment.NewLine +
                    "Vendor {5}" + Environment.NewLine +
                    "Location {6}" + Environment.NewLine +
                    "Description: {7}";
 
                string alarmDef = alarm.RuleList != null && alarm.RuleList.Count > 0 ? alarm.RuleList[0].Name : "";
                string alarmObj = alarm.ObjectList != null && alarm.ObjectList.Count > 0 ? alarm.ObjectList[0].Value : "";
                string alarmVendorName = alarm.Vendor != null && alarm.Vendor.Name != null ? alarm.Vendor.Name : "";
                string alarmLocation = alarm.Location ?? "";
                string alarmDescription = alarm.Description ?? "";
 
                
                return string.Format(alarmInText,
                    alarm.EventHeader.Message,
                    alarm.EventHeader.Type,
                    alarm.EventHeader.Source.Name,
                    alarm.EventHeader.CustomTag,
                    alarmObj,
                    alarmVendorName,
                    alarmLocation,
                    alarmDescription,
                    alarmDef);
            }
            else
            {
                AnalyticsEvent analyticsEvent = alarmOrBaseEvent as AnalyticsEvent;
                if (analyticsEvent != null)
                {
                    string analyticsEventInText = "Message: {0}" + Environment.NewLine +
                        "Type: {1}" + Environment.NewLine +
                        "Source: {2}" + Environment.NewLine +
                        "CustomTag: {3}" + Environment.NewLine +
                        "Object {4}" + Environment.NewLine +
                        "Vendor {5}" + Environment.NewLine +
                        "Location {6}" + Environment.NewLine +
                        "Description: {7}";
 
                    string analyticsObj = analyticsEvent.ObjectList != null && analyticsEvent.ObjectList.Count > 0 ? analyticsEvent.ObjectList[0].Value : "";
                    string analyticsEventVendorName = analyticsEvent.Vendor != null && analyticsEvent.Vendor.Name != null ? analyticsEvent.Vendor.Name : "";
                    string analyticsEventLocation = analyticsEvent.Location ?? "";
                    string analyticsEventDescription = analyticsEvent.Description ?? "";
 
                    return string.Format(analyticsEventInText,
                        analyticsEvent.EventHeader.Message,
                        analyticsEvent.EventHeader.Type,
                        analyticsEvent.EventHeader.Source.Name,
                        analyticsEvent.EventHeader.CustomTag,
                        analyticsObj,
                        analyticsEventVendorName,
                        analyticsEventLocation,
                        analyticsEventDescription);
                }
                else
                {
                    BaseEvent baseEvent = alarmOrBaseEvent as BaseEvent;
                    if (baseEvent != null)
                    {
                        string baseEventInText = "Message: {0}" + Environment.NewLine +
                            "Type: {1}" + Environment.NewLine +
                            "Source: {2}" + Environment.NewLine +
                            "CustomTag: {3}";
 
                        return string.Format(baseEventInText,
                            baseEvent.EventHeader.Message,
                            baseEvent.EventHeader.Type,
                            baseEvent.EventHeader.Source.Name,
                            baseEvent.EventHeader.CustomTag
                            );
                    }
                }
                return "Unknown object";
            }
        }
    }
}

The Alarm Preview will display all of these:

  • The source camera.
  • Related cameras. (ReferenceList)
  • Snapshots added. (Snapshotlist)
  • Preview plugins.

You cannot control the Alarm Preview to omit any of the built-in displays. As an example; if your preview plugin displays an image that has already been added as a snapshot image, then the image will be displayed twice, once by the Alarm Preview built-in functionality and once by your Alarm Preview plugin.

Thank you for your response, Andersen.

Currently, my data format is as follows:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<AnalyticsEvent xmlns="urn:milestone-systems" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <EventHeader>
        <ID>00000000-0000-0000-0000-000000000000</ID>
        <Timestamp>2025-02-10T13:39:59+08:00</Timestamp>
        <Type>being_type</Type>
        <Message>NEURO</Message>
        <CustomTag>d1</CustomTag>
        <Source>
            <FQID>
                <ServerId>
                    <ID>1d362ceb-c06a-4097-873f-9b95cbffe0b0</ID>
                </ServerId>
                <ObjectId>1d362ceb-c06a-4097-873f-9b95cbffe0b0</ObjectId>
                <Kind>5135ba21-f1dc-4321-806a-6ce2017343c0</Kind>
            </FQID>
        </Source>
    </EventHeader>
    <Description>Testing</Description>
    <Location>Front Door</Location>
    <Vendor>
        <Name>TEST</Name>
    </Vendor>
    <SnapshotList>
        <Snapshot>
            <TimeOffset>0</TimeOffset>
            <Width>150</Width>
            <Height>200</Height>
            <HasOverlay>true</HasOverlay>
            <SizeInBytes>2552</SizeInBytes>
            <Image>image base64 string</Image>
        </Snapshot>
		<Snapshot>
            <TimeOffset>0</TimeOffset>
            <Width>150</Width>
            <Height>200</Height>
            <HasOverlay>true</HasOverlay>
            <SizeInBytes>2552</SizeInBytes>
            <Image>image base64 string</Image>
        </Snapshot>
    </SnapshotList>
</AnalyticsEvent>

Since I added the image to Snapshots (SnapshotList), the image is displayed twice.

If I want to display it as shown in the image above, I cannot place the image in Snapshots (SnapshotList).

Could you suggest where else I can put the image in AnalyticsEvent?

A possible workaround: Instead of submitting the image as a snapshot on the snapshotlist you could submit it as a string in Vendor.CustomData. Nobody would be able to see the pictures unless they have your plugin and you would have the control how your alarm preview plugin should display the pictures.

Thank you! I’ll try it right away.