<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tarun Kumar </title>
    <description>The latest articles on DEV Community by Tarun Kumar  (@tarun06).</description>
    <link>https://dev.to/tarun06</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1071905%2F93c41d07-4151-4ab7-9777-2b73c452adde.jpeg</url>
      <title>DEV Community: Tarun Kumar </title>
      <link>https://dev.to/tarun06</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tarun06"/>
    <language>en</language>
    <item>
      <title>Grafana with JSON API</title>
      <dc:creator>Tarun Kumar </dc:creator>
      <pubDate>Fri, 27 Oct 2023 12:49:07 +0000</pubDate>
      <link>https://dev.to/tarun06/grafana-with-json-api-56mo</link>
      <guid>https://dev.to/tarun06/grafana-with-json-api-56mo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Grafana is an open-source, composable observability and data visualization platform. It empowers users to effortlessly query, visualize, alert on, and gain insights from their metrics, regardless of where they are stored.&lt;/p&gt;

&lt;p&gt;One of the notable strengths of Grafana is its extensive support for various data sources, including but not limited to Prometheus, Loki, Elasticsearch, InfluxDB, Postgres, and data fetched from APIs. This wide range of compatibility ensures that users can seamlessly integrate their preferred data sources into Grafana.&lt;/p&gt;

&lt;p&gt;Grafana is renowned for its visually appealing and informative dashboards, which enable users to visualize metrics, logs, and traces from multiple sources simultaneously. These dashboards can be dynamic and reusable, fostering a collaborative and data-driven culture within teams. Sharing dashboards with other team members is effortless, promoting effective collaboration and knowledge sharing.&lt;/p&gt;

&lt;p&gt;Among the key features offered by Grafana are comprehensive data visualization capabilities, customizable dashboards, robust alerting mechanisms, efficient data exploration tools, and the ability to combine data from various sources. These features empower users to gain valuable insights and make informed decisions based on their data.&lt;/p&gt;

&lt;p&gt;While there are numerous online resources available discussing the utilization of popular data sources like InfluxDB or Postgres databases, there is a lack of comprehensive guides or informative posts explaining the correct usage of a JSON API. To address this gap, this blog post aims to provide a practical demonstration of effectively utilizing the JSON API Datasource in Grafana. The U.S. BUREAU OF LABOR STATISTICS' Public data source will serve as an illustrative example throughout the post. For more detailed information on the BLS public data API, please refer to the official documentation available at &lt;a href="https://www.bls.gov/developers/api_signature_v2.htm"&gt;https://www.bls.gov/developers/api_signature_v2.htm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Grafana JSON API is a data source plugin that allows you to query arbitrary JSON endpoints and parse the response into Grafana dataframes. This makes it possible to use Grafana to visualize data from a wide range of sources, including third-party APIs, custom data sources, and even log files. For more detailed information on the JSON API, please refer to the official documentation available at &lt;a href="https://grafana.github.io/grafana-json-datasource"&gt;https://grafana.github.io/grafana-json-datasource&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with Grafana to begin using JSON API Plugin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To access Grafana, launch your preferred web browser and navigate to the default Grafana port at &lt;a href="http://localhost:3000/"&gt;http://localhost:3000/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To proceed further, access the Grafana Toggle Menu or Hemberger Menu and locate the connection option. Within the connection section, select the Add New Connection feature. Utilize the search box to locate the JSON API Plugin as demonstrated in the image below, and select it to proceed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FdPPln7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0eelw4idxahfz5739bd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FdPPln7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0eelw4idxahfz5739bd.png" alt="Image description" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please proceed with the installation of the JSON API Data Source Plugin by clicking on the 'install' button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OqRLaF6j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuw0dj80e7nz3lliaqpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OqRLaF6j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kuw0dj80e7nz3lliaqpt.png" alt="Image description" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installation, a new option called "Add new data source" will be presented. Simply click on this option to proceed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uPww0TDB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o64bvms2ebaqf6byl0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uPww0TDB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o64bvms2ebaqf6byl0m.png" alt="Image description" width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide any text in the name field. I have entered BLSPublicData for Reference. Locate the Url field and insert the following JSON API URL: &lt;a href="https://api.bls.gov/publicAPI/v2/timeseries/data/"&gt;https://api.bls.gov/publicAPI/v2/timeseries/data/&lt;/a&gt;.  (The URL can be found at Shingle Series section of BLS Public Data API &lt;a href="https://www.bls.gov/developers/api_signature_v2.htm"&gt;https://www.bls.gov/developers/api_signature_v2.htm&lt;/a&gt;). Proceed to click on the Save &amp;amp; Test button, as demonstrated in the image provided below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3WT69V9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66r8l9r743tddrdxu863.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3WT69V9t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66r8l9r743tddrdxu863.png" alt="Image description" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Save &amp;amp; Test button's execution time may vary depending on the internet connection. After execution, an error will be thrown indicating "JSON API: Method Not Allowed". However, there is no need to be concerned as this error will be rectified once we provide the appropriate Payload to the API at a later stage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7aRD9QwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2css3ceqy7m1aniqhx22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7aRD9QwD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2css3ceqy7m1aniqhx22.png" alt="Image description" width="526" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the option 'Build Dashboard' depicted in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vame-KRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06xdcbuu4utptzfdb3bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vame-KRq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06xdcbuu4utptzfdb3bf.png" alt="Image description" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select BLSPublicData as the data source, resulting in the creation of a fresh panel within a newly generated dashboard, subsequently leading to the navigation into edit mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GnjntbGM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55xwtwhixwc5dtray3tl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GnjntbGM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/55xwtwhixwc5dtray3tl.png" alt="Image description" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the appearance of the panel while in edit mode. If you observe any sample data within the panel, Click the refresh button to begin with a clean slate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pFPgwISn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bugxd0v7ipn181fxqga.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pFPgwISn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3bugxd0v7ipn181fxqga.png" alt="Image description" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the Query Tab and locate the "A" section. Within this section, click on the "Path" tab and insert "/LAUCN040010000000005" as the payload in the GET API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g7wGZ52Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be4rjovs2x71dxjz4njv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g7wGZ52Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be4rjovs2x71dxjz4njv.png" alt="Image description" width="800" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please proceed to the "Fields" tab and input "$.Results" as directed. Once you enter, you can begin observing the data in panel view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LNkA56DF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw2zg71xhtog1vtg8hm4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LNkA56DF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw2zg71xhtog1vtg8hm4.png" alt="Image description" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To view the data for "year", input "$.Results.series[&lt;em&gt;].data[&lt;/em&gt;].year" and click enter. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0y0CI4uX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evtrwcuewb8rjshsttxr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0y0CI4uX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evtrwcuewb8rjshsttxr.png" alt="Image description" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For additional data, simply click on the "+" button to add a new field and enter the required details as illustrated in the image below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4JuwiGXh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8rbcn8cse5ymso74fh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4JuwiGXh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8rbcn8cse5ymso74fh7.png" alt="Image description" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the apply button to display the ultimate outcome in the panel on the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VKXFulID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1t5ld8e5lsry68yxr32m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VKXFulID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1t5ld8e5lsry68yxr32m.png" alt="Image description" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
The JSON API Plugin proves to be an invaluable tool for users who require real-time visualization of server data without the need for local or remote database storage. This ensures that the data retrieved is always current, allowing for further analysis to be conducted based on the latest information to meet any business needs.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stacking of Pop-Up Notification in WPF</title>
      <dc:creator>Tarun Kumar </dc:creator>
      <pubDate>Sun, 21 May 2023 06:00:05 +0000</pubDate>
      <link>https://dev.to/tarun06/stacking-of-pop-up-notification-in-wpf-2n8j</link>
      <guid>https://dev.to/tarun06/stacking-of-pop-up-notification-in-wpf-2n8j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The pop-up notification (or toast, desktop notification, notification bubble, or simply notification) is a graphical control element that communicates certain events to the user without forcing them to react to this notification immediately, unlike conventional pop-up windows. Desktop notifications usually disappear automatically after a short amount of time with or without any user intervention. &lt;/p&gt;

&lt;p&gt;As a dotnet developer, you may have found yourself in need of displaying one or many such toast notifications in your application to communicate to user or make user aware in many cases not limited to &lt;br&gt;
1) Success/Fail response to an action. &lt;br&gt;
2) Internet connected/disconnected.&lt;br&gt;
3) Battery about to die etc.&lt;/p&gt;

&lt;p&gt;In this blog post, I will show you how to create a stacking of simple pop up notification and these notification will disappear automatically after a short time in WPF.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;To get started, we'll create a separate WPF user control library for our Custom Notification Controls. This will allow us to reuse the control in different projects without having to write the same code over and over again.&lt;br&gt;
Here are the steps to create a WPF user control library in Visual Studio:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Visual Studio and create a new project.&lt;/li&gt;
&lt;li&gt;In the New Project dialog box, select WPF User Control Library and click Next.&lt;/li&gt;
&lt;li&gt;Give your project a name ex."NotificationControls" and click Next.&lt;/li&gt;
&lt;li&gt;Select the dotnet version and click Create.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Designing the Notification Controls
&lt;/h2&gt;

&lt;p&gt;To start with, lets create a simple usercontrol and named it NotificationView and add below code.&lt;/p&gt;

&lt;p&gt;In the XAML file, we have 'StackPanel' to hold an Image to show icons for kinds (Success, Information, Warning etc.) and a Textblock to hold message. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NotificationView.xaml
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;UserControl x:Class="NotificationControls.NotificationView"
             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:NotificationControls"
             xmlns:draw="clr-namespace:System.Drawing;assembly=System.Drawing.Common"
             mc:Ignorable="d" 
             x:Name="notificationView"&amp;gt;
    &amp;lt;UserControl.Resources&amp;gt;
        &amp;lt;local:IconToImageSourceConverter x:Key="IconToImageSourceConverter"/&amp;gt;
    &amp;lt;/UserControl.Resources&amp;gt;
    &amp;lt;StackPanel Orientation="Horizontal"&amp;gt;
        &amp;lt;Image Source="{Binding ElementName=notificationView, Path=IconKind, Converter={StaticResource IconToImageSourceConverter}, Mode=OneWay}" Width="25" Height="25"/&amp;gt;
        &amp;lt;TextBlock Margin="2 0" Grid.Column="1" TextWrapping="WrapWithOverflow" Text="{Binding ElementName=notificationView, Path=Message}"
                   VerticalAlignment="Center" HorizontalAlignment="Left"/&amp;gt;
    &amp;lt;/StackPanel&amp;gt;
&amp;lt;/UserControl&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the NotificationControls.xaml.cs code behind file, &lt;a href="https://learn.microsoft.com/en-us/dotnet/desktop/wpf/properties/dependency-properties-overview?view=netdesktop-7.0" rel="noopener noreferrer"&gt;Dependency Property&lt;/a&gt; is used to exposed property to external world and for binding. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NotificationView.xaml.cs
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace NotificationControls
{
    /// &amp;lt;summary&amp;gt;
    /// Interaction logic for Notification.xaml
    /// &amp;lt;/summary&amp;gt;
    public partial class NotificationView : UserControl
    {
        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IconKindProperty =
            DependencyProperty.Register(nameof(IconKind), typeof(Icon), typeof(NotificationView), new PropertyMetadata(SystemIcons.Error));

        // Using a DependencyProperty as the backing store for Message.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MessageProperty =
            DependencyProperty.Register(nameof(Message), typeof(string), typeof(NotificationView), new PropertyMetadata(string.Empty));

        public NotificationView()
        {
            InitializeComponent();
            Loaded += NotificationView_Loaded;
        }

        public event Action Closed;

        private void NotificationView_Loaded(object sender, RoutedEventArgs e)
        {
            var task = Task.Delay(TimeSpan.FromSeconds(5));
            _ = task.ContinueWith(t =&amp;gt; Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, () =&amp;gt; Closed?.Invoke()));
        }

        public Icon IconKind
        {
            get { return (Icon)GetValue(IconKindProperty); }
            set { SetValue(IconKindProperty, value); }
        }

        public string Message
        {
            get { return (string)GetValue(MessageProperty); }
            set { SetValue(MessageProperty, value); }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Since the Image has Binding associated with Dependency property IconKind which is of type System.Drawing.Icon, An "IconToImageSourceConverter" converter is used to convert icon to image source. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IconToImageSourceConverter.cs
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class IconToImageSourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var icon = value as Icon;
            if (icon == null)
                return null;

            ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
                icon.Handle,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());
            return imageSource;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return DependencyProperty.UnsetValue;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With all the above, we have successfully create a notification control which can display a notification with an type and a message.&lt;/p&gt;

&lt;p&gt;Example, Error notification with message "Error occured, Please try again!" and IconKind as SystemIcons.Error, will display as below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrphw4q74n5gotzllkem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrphw4q74n5gotzllkem.png" alt="Error notification"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To let notification disappear after a short time (For this example, lets keep this to 5 seconds), A loaded event is subscribe and &lt;code&gt;Task.Delay(TimeSpan.FromSeconds(5))&lt;/code&gt; is used to wait for 5 second and then invoke close event. &lt;/p&gt;

&lt;p&gt;Now, It is time to stack one or many notification in control and allow them to disappear after 5 second. To achieve this, Create a  class with name StackedNoifications and derive it from StackPanel Control class. Now, add the below code to the StackedNoifications class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using NotificationControls;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;

namespace CompanionWindows.Views.Controls
{
    public class StackedNoifications : StackPanel
    {
        // Using a DependencyProperty as the backing store for Message.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NotificationMessageProperty =
            DependencyProperty.Register(nameof(NotificationMessage), typeof(Notification),
                typeof(StackedNoifications), new PropertyMetadata(null, OnMessageChanged));

        public Notification NotificationMessage
        {
            get =&amp;gt; (Notification)GetValue(NotificationMessageProperty);
            set =&amp;gt; SetValue(NotificationMessageProperty, value);
        }
        private static void OnMessageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            try
            {
                var stackednotification = (StackedNoifications)d;
                if (e.NewValue is Notification newMessage)
                {
                    stackednotification.Push(newMessage);
                }
            }
            catch (Exception)
            {
                // ignore
            }
        }

        private NotificationView CreateNotificationView(Notification newMessage)
        {
            var view = new NotificationView
            {
                Message = newMessage.Message,
                Margin = new Thickness(0, 0, 0, 8),
                IconKind = newMessage.IconKind
            };

            return view;
        }

        private void Push(Notification newMessage)
        {
            var alertPopupViews = Children.OfType&amp;lt;NotificationView&amp;gt;().ToList();

            if (!alertPopupViews.Any(v =&amp;gt; v.Message == newMessage.Message))
            {
                var notificationView = CreateNotificationView(newMessage);
                notificationView.Closed += () =&amp;gt; Pop(notificationView);

                Children.Add(notificationView);
            }
        }
        public void Pop(NotificationView view) =&amp;gt; Children.Remove(view);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The StackedNoifications has dependency property NotificationMessage which has implemented   &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.windows.propertychangedcallback?view=windowsdesktop-7.0" rel="noopener noreferrer"&gt;PropertyChangedCallback&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The PropertyChangedCallback that is invoked when the effective property value of a dependency property changes which is used to push new messages to stack panel.&lt;/p&gt;

&lt;p&gt;Once all code are in placed, The final result would be &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwwkmftfa4nrvzgjbfls.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwwkmftfa4nrvzgjbfls.gif" alt="Notification Control"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With these elements and classes in place, we can create a fully functional custom notification control which is capable of communicating certain events to the user without forcing them to react to this notification immediately in WPF.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>wpf</category>
      <category>programming</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
