Posts Tagged .net

Using Exception Helpers but avoiding CA1062 errors


I generally like to activate Code Analysis on projects that I’m working on, but this does add overhead in terms of the restrictions of what the analysis can cope with.

A perfect example of this is adhering to CA1062: Validate Arguments of public methods, especially when checking for null. A prime example would be:

public void MyMethod(string parameter1, object parameter2)
{
    return string.Format(CultureInfo.InvariantCulture, parameter1, parameter2);
}

Now, if we were to run that past the code analysis engine, we would be on the receiving end of (at least) 2 violations for CA1062 and would then need to quadruple the codebase for this method:

public void MyMethod(string parameter1, object parameter2)
{
    if (!string.IsNullOrEmpty(parameter1))
    {
        throw new ArgumentNullException("parameter1");
    }

    if (parameter2 == null)
    {
        throw new ArgumentNullException("parameter2");
    }

    return string.Format(CultureInfo.InvariantCulture, parameter1, parameter2);
}

Ugh. That’s a 10:1 ratio of validation code to actual work.

What would be much nicer would be to use an exception helper to allow us to do something like:

public void MyMethod(string parameter1, object parameter2)
{
    Throw.IfNotNull(parameter1, "parameter1");
    Throw.IfNotNull(parameter2, "parameter2");
    return string.Format(CultureInfo.InvariantCulture, parameter1, parameter2);
}

By default, if we do this then the code analysis engine is unable to delve into the IfNotNull method to see what its doing and so cannot confirm whether we are validating the parameter.

However, it turns out we can let it know that its doing this by creating a ValidatedNotNullAttribute:

[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
public sealed class ValidatedNotNullAttribute : Attribute
{
}

then use this in the helper method:

public static class Throw
{
    public void IfNotNull([ValidatedNotNull]object toCheck, string name)
    {
        if (ReferenceEquals(toCheck, null))
        {
            throw new ArgumentNullException(name);
        }
    }
}

And no more CA1062 warnings!

, , , ,

Leave a comment

Stopping Visual Studio breaking on exceptions in a specific method


I have just discovered this nifty little trick to stop VS breaking on Exceptions in a specific method, irrespective of the settings under Debug -> Exceptions.

The DebuggerStepThrough attribute on a method does not just stop the debugger stepping into the method when you are hitting F11 on it, it will also not pick up thrown exceptions.

So, if you, like me, have an exception that may get thrown that you can’t do anything about (ie if a user cancels an operation inside a COM call that throws an E_ABORT COMException), this is one way to ignore it in the debugger.

Of course, you can’t have your cake and eat it, if you set this attribute then you also can’t step into and debug the method in question either, but with a little bit of refactoring you should be able to keep the offending method as short as possible.

Example:

        [DebuggerStepThrough]  
        private void SendCrashLogs(string logFileName)
        {
            FileInfo logFile = new FileInfo(logFileName);
            if (logFile.Exists)
            {
                List<dynamic> toRelease = new List<dynamic>();
                dynamic outlookApplication = null;
                Process[] existingOutlookProcess = Process.GetProcessesByName("Outlook");
                bool alreadyRunning = existingOutlookProcess.Length > 0;
                try
                {
                    outlookApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Outlook.Application"));
                    toRelease.Add(outlookApplication);

                    // Create a new message and set the contents);
                    if (outlookApplication != null)
                    {
                        dynamic outlookMessage = outlookApplication.CreateItem(0);
                        toRelease.Add(outlookMessage);
                        if (outlookMessage != null)
                        {
                            // At the following line the user can cancel the operation
                            // which causes a COMException to be fired.
                            dynamic recipients = outlookMessage.Recipients;
                            toRelease.Add(recipients);
                            dynamic sender = recipients.Add(Environment.UserName);
                            toRelease.Add(sender);
                            foreach (string crashEmailRecipient in this.crashEmailRecipients)
                            {
                                recipients.Add(crashEmailRecipient);
                            }

                            foreach (dynamic recipient in recipients)
                            {
                                recipient.Resolve();
                                toRelease.Add(recipient);
                            }

                            outlookMessage.Subject = "Sending Logs";
                            string html = GenerateMessageBody();
                            outlookMessage.HTMLBody = html;

                            // Add the logs as an attachment
                            dynamic outlookMessageAttachments = outlookMessage.Attachments;
                            toRelease.Add(outlookMessageAttachments);
                            outlookMessageAttachments.Add(
                                logFileName, Type.Missing, outlookMessage.Body.Length + 1, logFileName);
                            outlookMessage.Send();
                        }
                    }
                }
                catch (COMException cEx)
                {
                    if (cEx.Message.Contains("E_ABORT"))
                    {
                        this.log.Warn("User aborted sending of error logs via email");
                    }
                    else
                    {
                        this.log.Error("Could not send error logs via email", cEx);
                    }
                }
                catch (Exception ex)
                {
                    this.log.Error("Could not send error logs via email", ex);
                }
                finally
                {
                    // Need to quit the Outlook application if it wasn't already running
                    if (outlookApplication != null && !alreadyRunning)
                    {
                        outlookApplication.Quit();
                    }

                    toRelease.Where(i => i != null).ForEach(i => Marshal.FinalReleaseComObject(i));
                    toRelease.Clear();
                }
            }
        }

, , , , ,

Leave a comment

An Outlook-style Desktop Alert Control in WPF part 2


This is the 2nd part in the series. For the first part, see here.

In the first post, I considered the overall requirements & composition of the control and put together the code to display the first pass at an alert. Following on from this, we need to consider a few extra things:

  • Adding pre-defined status images to the content area, alongside the text
  • Styling the control to make it a bit more attractive (after all, there’s little use writing things in WPF if they are going to look functional at best
  • Catering for more than 1 alert being visible at any one time
  • The user should be able to close the alert before the timer fires
  • Configurable actions to perform when the user clicks on the alert
  • One essential piece of information missing from the first part of the series is the ability to configure the length of time that each alert is displayed for. This is a simple addition to the DesktopAlertBase class:

            /// <summary>The default Alert Duration</summary>
            public static readonly TimeSpan DefaultAlertDuration = new TimeSpan(0, 0, 0, 5);
    
            /// <summary>The dependency property for the duration of the alert</summary>
            [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Default Dependency Property setup")] 
            public static readonly DependencyProperty AlertDurationProperty =
                    DependencyProperty.Register("AlertDuration", typeof(TimeSpan), typeof(DesktopAlertBase));
    
            /// <summary>Gets or sets the duration of the alert.</summary>
            [Bindable(true)]
            public TimeSpan AlertDuration
            {
                get { return (TimeSpan)this.GetValue(AlertDurationProperty); }
                set { SetValue(AlertDurationProperty, value); }
            }
    
    // Add the following code to the constructor
    this.AlertDuration = DefaultAlertDuration;
    
    // Update the following method as follows:
            /// <summary>
            /// Called when the alert is loaded. Kicks off the time to hide the control after the requisite time
            /// </summary>
            /// <param name="sender">The sender.</param>
            /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                this.activeTimer = new DispatcherTimer { Interval = this.AlertDuration };
    
                // Attach an anonymous method to the timer so that we can start fading out the alert when the timer is done.
                this.activeTimer.Tick += delegate { Close(); };
                this.activeTimer.Start();
            }
    
    

    Taking a step back, if we need to allow for multiple alerts to be visible at any one time, it seems sensible to have a singleton which controls the display of our alerts. This will restrict us slightly with regards to the flexibility of different types of alerts that we can use (as we’ll need calls for each one), but this can be refactored out at a later date.

    So, lets create a new class, DesktopAlertService:

        /// <summary>Service which is responsible for generating and positioning the Desktop Alerts</summary>
        public class DesktopAlertService : IDesktopAlertService
        {
            #region&nbsp;Private&nbsp;Fields
    
            /// <summary>Default alert height</summary>
            private const int DefaultHeight = 80;
    
            /// <summary>Default alert width</summary>
            private const int DefaultWidth = 350;
    
            /// <summary>The gap to keep between the bottom-most alert and the bottom of the screen</summary>
            private const int BottomOffset = 15;
    
            /// <summary>The gap to keep between the right-hand side of the alerts and the side of the screen</summary>
            private const int SideOffset = 5;
    
            /// <summary>The screen dimensions</summary>
            private readonly Rect screenRectangle;
            
            #endregion&nbsp;Private&nbsp;Fields
    
            /// <summary>Initializes a new instance of the <see cref="DesktopAlertService"/> class.</summary>
            public DesktopAlertService()
            {
                Rect rect = SystemParameters.WorkArea;
                this.screenRectangle = new Rect(rect.X, rect.Y, rect.Width - SideOffset, rect.Height - BottomOffset);
                this.DefaultProperties = new DesktopAlertProperties
                                    {
                                        AlertDuration = DesktopAlertBase.DefaultAlertDuration,
                                        Width = DefaultWidth,
                                        Height = DefaultHeight
                                    };
            }
    
            /// <summary>Gets the settings.</summary>
            public IDesktopAlertProperties DefaultProperties { get; private set; }
    
            #region&nbsp;Public&nbsp;Methods
    
            /// <summary>
            /// Displays an alert with the given title and message
            /// </summary>
            /// <param name="title">The title.</param>
            /// <param name="message">The message.</param>
            public void DisplayAlert(string title, string message)
            {
                this.DisplayAlert(title, message, DesktopAlertImage.None);
            }
    
            /// <summary>
            /// Displays an alert with the given category, title, message and image
            /// </summary>
            /// <param name="title">The title.</param>
            /// <param name="message">The message.</param>
            /// <param name="image">The image.</param>
            public void DisplayAlert(string title, string message, DesktopAlertImage image)
            {
                this.DisplayAlert(new DesktopAlertProperties { Title = title, Message = message, Image = image });
            }
    
            /// <summary>
            /// Displays the alert with the given properties
            /// </summary>
            /// <param name="properties">The properties for the alert.</param>
            public void DisplayAlert(IDesktopAlertProperties properties)
            {
                if (!Application.Current.Dispatcher.CheckAccess())
                {
                    Application.Current.Dispatcher.Invoke(
                        new Action<IDesktopAlertProperties>(this.DisplayAlert), properties);
                    return;
                }
    
                    // Figure out where to place the window based on the current screen resolution
                    SimpleAlert alert = new SimpleAlert
                                            {
                                                Title = properties.Title,
                                                Message = properties.Message,
                                                Width = properties.Width ?? this.DefaultProperties.Width ?? DefaultWidth,
                                                Height = properties.Height ?? this.DefaultProperties.Height ?? DefaultHeight,
                                                AlertDuration =
                                                    properties.AlertDuration
                                                    ?? this.DefaultProperties.AlertDuration ?? DesktopAlertBase.DefaultAlertDuration
                                            };
                    alert.Left = this.screenRectangle.Right - alert.Width - alert.BorderThickness.Right;
                    alert.Top = this.screenRectangle.Bottom - (alert.Height + alert.BorderThickness.Bottom);
                    if (properties.Image != DesktopAlertImage.None)
                    {
                        alert.Image = this.GetImage(properties.Image);
                    }
    
                    alert.Closed += this.OnClosed;
            }
    
            #endregion&nbsp;Public&nbsp;Methods
    
            #region&nbsp;Private&nbsp;Methods
    
            /// <summary>
            /// Handles the Closed event of the alert control.
            /// </summary>
            /// <param name="sender">The source of the event.</param>
            /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
            private void OnClosed(object sender, EventArgs e)
            {
                DesktopAlertBase alert = sender as DesktopAlertBase;
                if (alert != null)
                {
                   alert.Closed -= this.OnClosed;
                }
            }
    
            #endregion&nbsp;Private&nbsp;Methods
        }
    
    

    I have left the GetImage() method out for now – this can be viewed in the full source code which will be posted alongside the final part of this series.

    I have also defined a new interface, IDesktopAlertProperties which determines what is displayed as part of the alert:

        /// <summary>A collection of properties for a single Desktop Alert </summary>
        public interface IDesktopAlertProperties
        {
            /// <summary>Gets or sets the title.</summary>
            string Title { get; set; }
    
            /// <summary>Gets or sets the message.</summary>
            string Message { get; set; }
    
            /// <summary>Gets or sets the image.</summary>
            DesktopAlertImage Image { get; set; }
    
            /// <summary>Gets or sets the duration of the alert.</summary>
            TimeSpan? AlertDuration { get; set; }
    
            /// <summary>Gets or sets the width.</summary>
            int? Width { get; set; }
    
            /// <summary>Gets or sets the height.</summary>
            int? Height { get; set; }
        }
    
    

    Again, we have the DesktopAlertImage, which is an enum, specified here. This is just a list of available images.

    Our application entry point now looks like this:

    public partial class MainWindow : Window
    {
        /// <summary>The desktop service</summary>
        private readonly DesktopAlertService service = new DesktopAlertService();
    
        public MainWindow()
        {
            InitializeComponent();
            this.service.DisplayAlert(
                "Simple Alert", 
                "This is the text for the simple alert popup, which should wrap if too long to display on a single line");
        }
    }
    
    

    By adding this service to our implementation, the call is simplified for our users. This can easily be converted to a singleton or extended to allow multiple alert types. However, by keeping our properties on the controls as Dependency Properties, it is still possible to use them in the more tradition* WPF way.

    * – if there is such a thing.

    The next parts in this series will cover the styling, images, triggers (ie mouse-over) and additional functionality.

    Full source code will be posted alongside the final part.

, , ,

Leave a comment

WPF ResourceDictionary Precedence


There doesn’t seem to be a huge amount of information available on the precedence of ResourceDictionary entries in XAML. Knowing which styles will be used when you have more than one dictionary containing styles with the same name is clearly necessary.

According to the official MSDN documentation:

Resources in a merged dictionary occupy a location in the resource lookup scope that is just after the scope of the main resource dictionary they are merged into. Although a resource key must be unique within any individual dictionary, a key can exist multiple times in a set of merged dictionaries. In this case, the resource that is returned will come from the last dictionary found sequentially in the MergedDictionaries collection. If the MergedDictionaries collection was defined in XAML, then the order of the merged dictionaries in the collection is the order of the elements as provided in the markup. If a key is defined in the primary dictionary and also in a dictionary that was merged, then the resource that is returned will come from the primary dictionary. These scoping rules apply equally for both static resource references and dynamic resource references.

This can come in handy if you need to override styles from a 3rd party Resource Dictionary within your own – provided your dictionary is referenced after the original, everything works nicely.

, , , , ,

Leave a comment

Using UAC to request Administrative Privileges from c#


With the advent of Windows Vista and Windows 7, we need to consider access rights more than ever. Previously, applications running on windows-based operating systems up to and including XP could just rely on having wide access to perform almost any task. Although many corporate systems are more locked down, the software running on them is generally heavily tailored to do so.

With Windows Vista/7, this locked down approach is brought to the consumer versions of the operating system – by default users do not get administrative privileges – to get these privileges the OS will request confirmation from the user that they are happy for the application to be run in the elevated context. There are 2 main ways of causing the OS to display the prompts:

  1. Creating / updating the application manifest file to include
  2. Launching the application setting the “runas” verb from code

As this blog is centered around c#, lets take a look at the latter.

Once the application is launched, there is no way to elevate the privileges from code. We must therefore look at making sure it is started correctly. Adding the following code to the program start does just this:


            WindowsPrincipal pricipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            bool hasAdministrativeRight = pricipal.IsInRole(WindowsBuiltInRole.Administrator);
            if (!hasAdministrativeRight)
            {
                // relaunch the application with admin rights
                string fileName = Assembly.GetExecutingAssembly().Location;
                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.Verb = "runas";
                processInfo.FileName = fileName;

                try
                {
                    Process.Start(processInfo);
                }
                catch (Win32Exception)
                {
                    // This will be thrown if the user cancels the prompt
                }

                return;
            }

As you can see, what we are actually doing is re-launching the app, setting the ProcessStartInfo.Verb property to “runas”. This automatically causes the OS to prompt the user for admin access.

This can cause a few problems debugging – the easiest way round is to ensure the Visual Studio process is run as an administrator, otherwise we will just launch a new instance of the application whenever we attempt to debug.

, ,

1 Comment

An Outlook-style Desktop Alert control in WPF (part 1)


One frequently useful way of notifying users of information for an application is via some sort of transient popup, similar to the Microsoft Outlook Desktop Alert. There are a variety of paid-for controls out there which contain similar functionality, but I decided to build one in WPF.

Requirements

Before we start, its always best to work out exactly what we need the alerts to do.

  • Display an alert, on demand which contains, as a minimum a title and some content
  • The alert should automatically appear in the correct location – just inside the Windows system tray, irrespective of the location.
  • The alert should have a configurable opacity
  • The alert should stay visible for a configurable period of time, then disappear automatically
  • Multiple alerts can be displayed at the same time without overlapping (within reason – where there are more alerts than screen real-estate, they can overlap as necessary).
  • When the mouse cursor hovers over the alert, it should, as a minimum have an opacity of 1 to be fully visible and should not disappear
  • When the mouse moves away from the alert, the opacity should return to the configured value and any timing related to the alert disappearing should restart
  • The title can be set
  • The content can be configured and should as a minimum be some text and an icon
  • Users can close the alert prior to the planned close.
  • A configurable action can be fired when the user clicks on an alert, configurable per alert.
  • <<CONTEXT>> some way of giving context and setting options, etc.

NOTE: One of my original requirements for this alert was that it should fade-in and fade-out in the same way that the default Outlook one does. However, after doing some testing on this (and comparing it to other solutions), it appears that there are frequent OutOfMemoryExceptions thrown. Removing the fading resolves the problem, so I have removed the requirement for now until I can find a way round this.

Designing the Alert

Basic layout for the Desktop Alert

The Desktop Alert simple mockup

It makes sense to keep the content of the alert as flexible as possible. However, all alerts will we require a title area, a close button and the content area. The obvious layout is shown here.

To emphasise the title it will be shown in bold. The close button is at the far right of the title area. Taking up the rest of the control is the content area, which will be configurable.

Configuring the Content

Whilst keeping the content configurable to allow many different types of alert, for the purposes of this post I will concentrate on using a simple alert with an icon and message text. The icon will be displayed to the left of the content area (with a fixed size), with the text taking up the rest.

To build this up in WPF, it makes sense to split the control into 2 parts:

  1. A Custom Control containing the basic layout: title area, close box & content area
  2. A User Control inheriting this which has the content (icon & message).

In this way, we can just use a different User Control to display different content, keeping the same behaviour of the overall alert.

DesktopAlertBase

Add a new Custom Control to the project called DesktopAlertBase. Enter the following xaml into the Themes\Generic.xaml file:

<Style TargetType="{x:Type DesktopAlert:DesktopAlertBase}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type DesktopAlert:DesktopAlertBase}">
                    <Border>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="15"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <Grid Grid.Row="0">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="15"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock Grid.Column="0" 
                                               Margin="5, 0, 0, 0" 
                                               FontWeight="Bold" 
                                               FontSize="12" 
                                               Opacity="1.0" 
                                               Text="{TemplateBinding Title}"/>
                                    <Button Grid.Column="1"
                                            HorizontalAlignment="Right"
                                            VerticalAlignment="Top"
                                            Width="15" 
                                            Height="15" 
                                            Foreground="Red">x</Button>
                                </Grid>
                            </Grid>
                            <ContentControl Grid.Row="1" Content="{TemplateBinding Content}"/>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Update the new custom control as follows:

/// <summary>Desktop alert class</summary>
public abstract class DesktopAlertBase : Window
{
	/// <summary>Initializes a new instance of the <see cref="DesktopAlertBase"/> class.</summary>
	public DesktopAlertBase()
	{
		Visibility = Visibility.Visible;

		// Set some default properties for the alerts.
		// These can be changed by the derived alerts.
		this.ShowInTaskbar = false;
		WindowStyle = WindowStyle.None;
		ResizeMode = ResizeMode.NoResize;
		Topmost = true;
		AllowsTransparency = true;
		Opacity = 0.9;
		BorderThickness = new Thickness(1);
		BorderBrush = Brushes.Black;
		Background = Brushes.White;
	}

	/// <summary>Initializes static members of the <see cref="DesktopAlertBase"/> class.</summary>
	static DesktopAlertBase()
	{
		DefaultStyleKeyProperty.OverrideMetadata(
			typeof (DesktopAlertBase), new FrameworkPropertyMetadata(typeof (DesktopAlertBase)));
	}
}

This will give us a new control, but without hooking up either the timer or the close button to close the window. To do this:

  • Amend the close button in the template and give it a name: “PART_CloseButton”
  • Add “Loaded += this.OnLoaded;” to the constructor.
  • Add the line Loaded += this.OnLoaded; to the constructor
  • Add the following code:
  • /// <summary>The timer that determines the length of time that the control is visible for</summary>
    private DispatcherTimer activeTimer;
    
    /// <summary>Hook up the button click handlers, etc</summary>
    public override void OnApplyTemplate()
    {
    	ButtonBase closeButton = Template.FindName("PART_CloseButton", this) as ButtonBase;
    	if (closeButton != null)
    	{
    		closeButton.Click += this.OnCloseButtonClick;
    	}
    }
    
    /// <summary>
    /// If the user clicks the close button, stop the timer that counts how long the alert and simply close it
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
    private void OnCloseButtonClick(object sender, RoutedEventArgs e)
    {
    	this.activeTimer.Stop();
    	Close();
    }
    
    /// <summary>
    /// Called when the alert is loaded. Kicks off the time to hide the control after the requisite time
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
    private void OnLoaded(object sender, RoutedEventArgs e)
    {
    	this.activeTimer = new DispatcherTimer { Interval = TimeSpan.Parse("0:0:05") };
    
    	// Attach an anonymous method to the timer so that we can start fading out the alert when the timer is done.
    	this.activeTimer.Tick += delegate { Close(); };
    	this.activeTimer.Start();
    }
    

    Text Alerts

    Now we have the basis of the alert, we can create a new User Control to implement a simple text style alert. To do this, add a new User Control called SimpleControl. Add the following code for the xaml & c#:

        /// <summary>
        /// Interaction logic for SimpleAlert.xaml
        /// </summary>
        public partial class SimpleAlert
        {
            public static DependencyProperty MessageProperty = DependencyProperty.Register(
                "Message",
                typeof(string),
                typeof(SimpleAlert));
    
            public static DependencyProperty ImageProperty = DependencyProperty.Register(
                "Image",
                typeof(ImageSource),
                typeof(SimpleAlert));
    
            [Bindable(true)]
            public string Message
            {
                get { return (string)GetValue(MessageProperty); }
                set { SetValue(MessageProperty, value); }
            }
    
            [Bindable(true)]
            public ImageSource Image
            {
                get { return (ImageSource) GetValue(ImageProperty); }
                set { SetValue(ImageProperty, value); }
            }
    
            public SimpleAlert()
            {
                InitializeComponent();
            }
        }
    
    <!-- XAML for the user control content --> 
        <Grid Background="White">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="40"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Image Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Image}"/>
            <TextBlock FontSize="12" 
                       Padding="5" 
                       TextWrapping="Wrap"
                       Grid.Column="1"
                       Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Message}"/>
        </Grid>
    
    

    You will also need to change the XAML definition from a UserControl to a DesktopAlertBase. Now we have this, we can create the alert from the MainWindow.cs and position it in the relevant place on the screen:

        public partial class MainWindow : Window
        {
            private SimpleAlert alert;
            public MainWindow()
            {
                InitializeComponent();
                alert = new SimpleAlert
                            {
                                Title = "Simple Alert",
                                Message =
                                    "This is the text for the simple alert popup, which should wrap if too long to display on a single line",
                                Width = 300,
                                Height = 75,
                };
                alert.Left = SystemParameters.WorkArea.Right - alert.Width - alert.BorderThickness.Right;
                alert.Top = SystemParameters.WorkArea.Bottom - (alert.Height + alert.BorderThickness.Bottom);
            }
        }
    
    

    And there we have it – the first desktop alert. In the next posts on the subject I will go into more detail on the images, styling of the control, positioning, displaying multiple alerts simultaneously and user interactions & effects (ie mouseover, context menus, etc).

, , ,

2 Comments