ElektroStudios
ElektroStudios

Reputation: 20464

Get the owner Form of a NotifyIcon?

In C# or Vb.Net, using managed or unmanaged code, how I could retrieve the owner Form of a NotifyIcon?

I've checked the base types of NotifyIcon Class and also the ComponentConverter trying to find out a possible type-casting to obtain the Form, but I was not able to.

I also seen the NotifyIcon.ContextMenuStrip.FindForm() function, but for any reason when I assign a contextmenustrip the FindForm() function always returns a null-reference exception, and anyways even if it could work will not be a safe approach because I could have a notifyicon without a contextmenu.

My intention is to pass a NotifyIcon as an argument to some methods that will perform common tasks to save me time and reduct code.

Pseudo example:

Public Shared Sub MinimizeToSystray(ByVal ntfy As NotifyIcon)

    If (ntfy Is Nothing) Then
        Throw New ArgumentNullException(paramName:="ntfy")

    ElseIf (ntfy.Icon Is Nothing) Then
        Throw New ArgumentException(message:="The NotifyIcon doesn't have an icon.",
                                    paramName:="ntfy")

    Else
        Dim f As Form = ntfy.GetOwnerForm()
        f.WindowState = FormWindowState.Minimized
        f.Hide()

        ntfy.Visible = True

    End If

End Sub

Upvotes: 3

Views: 674

Answers (2)

Hans Passant
Hans Passant

Reputation: 941665

The FindForm() method can only find the form for controls. The kind that derive from Control and are embedded in a form through its Parent property. But NotifyIcon is not a control, it is Component. A Component only has a Site property, its value is only defined at design time.

There is a casual relationship between a component and the form, Winforms promises to automatically dispose any components that have a constructor overload that takes an IContainer argument. Not all them do, OpenFormDialog and BackgroundWorker for example don't, NotifyIcon does. They omit the constructor when they don't need disposal.

Which makes it technically possible to find the form back. You'd need to iterate Application.OpenForms(). And use reflection to iterate their private components collection. Do note that this can only work when the form was actually opened, its Show() method must have been called.

That's a solution that scores -100 points, it is both ugly and error prone. The simple and always-correct solution is to just add an extra argument to the method to allow passing the "owner" form. With the assumption that, since the caller needs to know the NotifyIcon instance, it also should know the form. Typically Me.

Upvotes: 4

Reza Aghaei
Reza Aghaei

Reputation: 125227

Component doesn't have a FindForm method like controls and if you need such property you should customize the component and apply a workaround. Unfortunately NotifyIcon is sealed and can not be inherited.

As mentioned by Ivan Stoev in comments, you can use Tag property to store a reference to the container form.

But If you are looking for a designer based solution that works without writing such initialization codes to set the Tag, as a good option, you can create an extender component that adds a property ParentForm to your NotifyIcon component and set the property at design-time, then you can simply use it at run-time and when setting the ParentForm property at design-time you can set Tag property in component code, and then use it at run-time.

And here is the usage:

Add a the ComponentExtender (see the code at the end of post) to your project, then build the project. Then:

  1. Add the ComponentExtender from toolbox to your form
  2. Set the ParentForm on componentExtender1 property of your notifyIcon1 at designer using property grid:

enter image description here

Use this code to find the parent form of your notify icon:

var parent = this.notifyIcon1.Tag as Form`

Code

Here is an implementation that I used and works properly:

[ProvideProperty("ParentForm", typeof(NotifyIcon))]
public class ComponentExtender 
    : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider
{
    private Hashtable components;
    public ComponentExtender() : base() { components = new Hashtable(); }

    public bool CanExtend(object extendee)
    {
        if (extendee is Component)
            return true;
        return false;
    }

    public Form GetParentForm(NotifyIcon extendee)
    {
        return components[extendee] as Form;
    }

    public void SetParentForm(NotifyIcon extendee, Form value)
    {
        if (value == null)
        {
            components.Remove(extendee);
            component.Tag = null;
        }
        else
        {
            components[extendee] = value;
            component.Tag = value;
        }
    }
}

Upvotes: 2

Related Questions