Click here to Skip to main content
15,881,248 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I was trying to make a Custom ToolTip, with multiple Labels and some Checkboxes, so I could use it on multiple scenarios...

The Tooltip itself works fine, but I want to get the "Tooltip" property with an string and bool array to be shown in the designer for every control too... So I could set tooltips in the designer AND in code...


My problem at the moment is that I can't get the strings property and bools property of my ToolTipInfo class get serialized...

It seems, that I've got it working, but not completely...

Now the properties were shown, but won't be saved...







C#
using MetroUI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Network_Manager
{
    [DesignerCategory("Code")]
    [ToolboxBitmap(typeof(ToolStrip))]
    [ProvideProperty("ToolTip", typeof(Control))]
    [ProvideProperty("tt_Labeltexts", typeof(Control))]
    [ProvideProperty("tt_Checkerstates", typeof(Control))]
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class MetroToolTip : Component, IExtenderProvider
    {
        //Fields
        private Dictionary<Control, TooltipInfo> _toolTips = new();


        private int _interval = 100;

        private double _step = 0.1;

        private List<MetroLabel> _labels;

        private List<MetroChecker> _checkers;

        private MetroUI.Components.ToolTipBase _base = new();

        private int _delay = 1000;

        private int _animationTime = 250;

        private int _maxOpacity = 95;

        private int _minOpacity = 0;

        private bool _fadeIn = true;

        private bool _fadeOut = true;

        private ContainerControl _parent = null;

        private bool _eventsSubscribed;


        //Settings
        [Category("Base")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<MetroLabel> Labels
        {
            get
            {
                if (this._labels == null)
                    this._labels = new();
                return this._labels;
            }
            set => RefreshControls();
        }

        [Category("Base")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<MetroChecker> Checkers
        {
            get
            {
                if (this._checkers == null)
                    this._checkers = new();
                return this._checkers;
            }

            set => RefreshControls();
        }

        [Category("Base")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MetroUI.Components.ToolTipBase Base
        {
            get
            {
                if (this._base == null)
                    this._base = new() { Visible = false };
                return this._base;
            }
        }

        [Category("Design")]
        public ContainerControl Parent
        {
            get => this._parent;
            set => this._parent = value;
        }

        [Category("Animation")]
        public int MaxOpacity
        {
            get => this._maxOpacity;
            set
            {
                if (value <= this._minOpacity)
                    throw new ArgumentException("value must be greater than MinOpacity");
                if (value < 0 || value > 100)
                    throw new ArgumentException("value must be between 0 and 100");

                this._maxOpacity = value;
                CalculateInterval();
            }
        }

        [Category("Animation")]
        public int MinOpacity
        {
            get => this._minOpacity;
            set
            {
                if (value >= this._maxOpacity)
                    throw new ArgumentException("value must be smaller than MaxOpacity");
                if (value < 0 || value > 100)
                    throw new ArgumentException("value must be between 0 and 100");

                this._minOpacity = value;
                CalculateInterval();

            }
        }

        [Category("Animation")]
        public int Delay
        {
            get => this._delay;
            set
            {
                if (value < 0)
                    throw new ArgumentException("value must be greater or equal 0");

                this._delay = value;
            }
        }

        [Category("Animation")]
        public int AnimationTime
        {
            get => this._animationTime;
            set
            {
                if (value < 0)
                    throw new ArgumentException("value must be greater than 0");

                this._animationTime = value;
                CalculateInterval();
            }
        }

        [Category("Animation")]
        public int Interval => this._interval;

        [Category("Animation")]
        public bool FadeIn
        {
            get => this._fadeIn;
            set => this._fadeIn = value;
        }

        [Category("Animation")]
        public bool FadeOut
        {
            get => this._fadeOut;
            set => this._fadeOut = value;
        }

        public override ISite Site
        {
            get { return base.Site; }
            set
            {
                base.Site = value;
                if (value == null)
                {
                    return;
                }

                IDesignerHost host = value.GetService(
                    typeof(IDesignerHost)) as IDesignerHost;
                if (host != null)
                {
                    IComponent componentHost = host.RootComponent;
                    if (componentHost is ContainerControl)
                    {
                        Parent = componentHost as ContainerControl;
                    }
                }
            }
        }



        //Constructor
        public MetroToolTip() { }



        //Control Events
        protected async void Control_MouseEnter(object sender, EventArgs e)
        {
            if (sender == null) return;

            Control control = sender as Control;

            if (control.Visible)
            {
                if (await isMouseHovering(control))
                    AnimateToolTip(control, true);
            }
        }

        protected void Control_MouseLeave(object sender, EventArgs e)
        {
            if (sender == null) return;

            Control control = sender as Control;

            if (control.Visible)
            {
                AnimateToolTip(control, false);
            }
            else
                HideInstant();
        }

        protected void Control_LostFocus(object sender, EventArgs e)
        {
            if (sender == null) return;

            Control control = sender as Control;

            if (control.Visible)
            {
                AnimateToolTip(control, false);
            }
            else
                HideInstant();
        }

        protected void Control_VisibleChanged(object sender, EventArgs e)
        {
            if (sender == null) return;
            Control control = sender as Control;

            if (control.Visible == false)
                HideInstant();
        }

        protected void Control_Disposed(object sender, EventArgs e)
        {
            if (sender == null) return;
            Control control = sender as Control;

            if (this._toolTips.ContainsKey(control))
            {
                this._toolTips.Remove(control);
            }

            control.MouseEnter -= Control_MouseEnter;
            control.MouseLeave -= Control_MouseLeave;
            control.VisibleChanged -= Control_VisibleChanged;
            control.Disposed -= Control_Disposed;
            control.LostFocus -= Control_LostFocus;

            HideInstant();

        }


        //Parent Events
        protected void Parent_LostFocus(object sender, EventArgs e)
        {
            HideInstant();
        }

        protected void Parent_VisibleChanged(object sender, EventArgs e)
        {
            if (sender == null) return;

            ContainerControl form = sender as ContainerControl;
            if (!form.Visible)
                HideInstant();
        }

        protected void Parent_Disposed(object sender, EventArgs e)
        {
            this._parent.LostFocus -= Parent_LostFocus;
            this._parent.VisibleChanged -= Parent_VisibleChanged;
            this._parent.Disposed -= Parent_Disposed;

            HideInstant();
            this._base.Close();
        }




        //Methods
        private void RefreshControls()
        {
            if (this._base == null) return;

            this._base.Controls.Clear();

            if (this._labels != null)
            {
                foreach (MetroLabel label in this._labels)
                {
                    if (!this._base.Controls.Contains(label))
                    {
                        this._base.Controls.Add(label);
                    }
                }
            }

            if (this._checkers != null)
            {
                foreach (MetroChecker checker in this._checkers)
                {
                    if (!this._base.Controls.Contains(checker))
                    {
                        this._base.Controls.Add(checker);
                    }
                }
            }
        }

        private void CalculateInterval()
        {
            double temp = this._animationTime / ((this._maxOpacity - this._minOpacity) * this._step);

            this._interval = Convert.ToInt32(temp);
        }

        private void HideInstant()
        {
            this._base.Opacity = Math.Round((double)this._minOpacity / 100, 2);
            this._base.Hide();
        }

        private async Task<bool> isMouseHovering(Control control)
        {
            await Task.Delay(this._delay);

            if (control.IsDisposed) return false;

            return control.ClientRectangle.Contains(control.PointToClient(Cursor.Position));
        }


        private async void AnimateToolTip(Control control, bool show)
        {
            bool animationDone = false;
            string[] strings = null;
            bool[] bools = null;
            double step = this._step;

            if (show)
            {
                //Check for Controls
                if (this._labels.Count == 0 && this._checkers.Count == 0)
                    return;

                RefreshControls();

                //Get Message
                if (this._toolTips.ContainsKey(control))
                {
                    if(this._toolTips[control].Strings != null)
                        strings = this._toolTips[control].Strings.ToArray();

                    if (this._toolTips[control].Bools != null)
                        bools = this._toolTips[control].Bools.ToArray();
                }
                else
                {
                    return;
                }

                //Apply Message
                if (strings != null)
                {
                    for (int s = 0; s < _labels.Count; s++)
                    {
                        if (s < strings.Length)
                        {
                            if (strings[s] != string.Empty)
                                _labels[s].Text = strings[s];
                        }
                    }
                }

                if (bools != null)
                {
                    for (int b = 0; b < _checkers.Count; b++)
                    {
                        if (b < bools.Length)
                        {
                            _checkers[b].Checked = bools[b];
                        }
                    }
                }

                //Set new Location
                Point mousePos = control.PointToScreen(Point.Empty);
                int posX = mousePos.X + control.Width - 10;
                int posY = mousePos.Y + control.Height - 10;
                this._base.DesktopLocation = new Point(posX, posY);

                this._base.Visible = true;
            }


            //Animate
            while (!animationDone)
            {
                if (show)
                {
                    if (this._base.Opacity < Math.Round((double)this._maxOpacity / 100, 2) && this._fadeIn)
                    {
                        this._base.Opacity += step;
                    }
                    else
                    {
                        this._base.Opacity = Math.Round((double)this._maxOpacity / 100, 2);
                        this._base.Show();

                        animationDone = true;
                    }
                }
                else
                {
                    if (this._base.Opacity > Math.Round((double)this._minOpacity / 100, 2) && this._fadeOut)
                    {
                        this._base.Opacity -= step;
                    }
                    else
                    {
                        this._base.Opacity = Math.Round((double)this._minOpacity / 100, 2);
                        this._base.Hide();

                        animationDone = true;
                    }
                }

                await Task.Delay(this._interval);
            }

        }



        //Public Methods
        public void SetToolTip(Control control, string[] strings, bool[] bools = null)
        {
            if (!this._toolTips.ContainsKey(control) && strings != null)
            {
                control.MouseEnter += Control_MouseEnter;
                control.MouseLeave += Control_MouseLeave;
                control.VisibleChanged += Control_VisibleChanged;
                control.Disposed += Control_Disposed;
                control.LostFocus += Control_LostFocus;
                List<string> temp1 = new();
                temp1.AddRange(strings);
                List<bool> temp2 = new();
                temp2.AddRange(bools);

                this._toolTips.Add(control, new TooltipInfo(null, temp1, temp2));
            }
            if (this._parent != null && !this._eventsSubscribed)
            {
                this._parent.LostFocus += Parent_LostFocus;
                this._parent.VisibleChanged += Parent_VisibleChanged;
                this._parent.Disposed += Parent_Disposed;

                this._eventsSubscribed = true;
            }

        }


        public void SetToolTip(Control control, TooltipInfo tooltipInfo)
        {
            if (!this._toolTips.ContainsKey(control))
            {
                control.MouseEnter += Control_MouseEnter;
                control.MouseLeave += Control_MouseLeave;
                control.VisibleChanged += Control_VisibleChanged;
                control.Disposed += Control_Disposed;
                control.LostFocus += Control_LostFocus;

                this._toolTips.Add(control, tooltipInfo);
            }
            if (this._parent != null && !this._eventsSubscribed)
            {
                this._parent.LostFocus += Parent_LostFocus;
                this._parent.VisibleChanged += Parent_VisibleChanged;
                this._parent.Disposed += Parent_Disposed;

                this._eventsSubscribed = true;
            }

        }

        public TooltipInfo GetToolTip(Control control)
        {
            if (this._toolTips.ContainsKey(control))
            {
                return this._toolTips[control];
            }

            return new TooltipInfo();
        }

        //Interface Implemetation
        public void Settt_Labeltexts(Control control, List<string> labelTexts)
        {
            if (!this._toolTips.ContainsKey(control))
            {
                TooltipInfo item = new(null, labelTexts);
                this._toolTips.Add(control, item);
            }
            else
            {
                this._toolTips[control].Strings = labelTexts;
            }

        }

        public void Settt_Checkerstates(Control control, List<bool> checkerStates)
        {
            if ( !this._toolTips.ContainsKey(control))
            {
                TooltipInfo item = new(null, null, checkerStates);
                this._toolTips.Add(control, item);
            }
            else
            {
                this._toolTips[control].Bools = checkerStates;
            }
        }

        public IList<string> Gettt_Labeltexts(Control control)
        {
            if (this._toolTips.ContainsKey(control))
            {
                return this._toolTips[control].Strings;
            }
            else
            {
                TooltipInfo item = new();
                this._toolTips.Add(control, item);

                return this._toolTips[control].Strings;
            }   
        }

        public List<bool> Gettt_Checkerstates(Control control)
        {
            if (this._toolTips.ContainsKey(control))
            {
                return this._toolTips[control].Bools;
            }
            else
            {
                TooltipInfo item = new();
                this._toolTips.Add(control, item);

                return this._toolTips[control].Bools;
            }
        }

        public bool CanExtend(object extendee)
        {
            return extendee is Control;
        }












        public class TooltipInfo
        {
            public string Title;

            public List<string> Strings = new();

            public List<bool> Bools = new();

            public TooltipInfo() { }

            public TooltipInfo(string title, List<string> strings = null, List<bool> bools = null)
            {
                this.Title = title;
                
                if (strings != null)
                    this.Strings = strings;

                if (bools != null)
                    this.Bools = bools;
            }
        }
    }
}


What I have tried:

I already tried to get it working like I did with the Labels and Checkers property and searched the web for "how to implement IExtenderProvider" which made the designer to create a setting for every control called "Tooltip on metroTooltip"...

I just created every Property as a single accessor, but the values wont be updated...
Posted
Updated 2-Sep-21 3:29am
v3

1 solution

I suppose your Problem comes with the use of ProvideProperty.
If you search under Articles inside this Forum you will get some Samples how to do that.
Your Search could be "ProvideProperty" or also "Tooltip".
Here are 2 Links for example :

Displaying a ToolTip when the Mouse Hovers Over a Disabled Control[^]

Balloon ToolTip Control[^]

I hope it helps a little ...
 
Share this answer
 
Comments
Electro_Attacks 2-Sep-21 8:38am    
Well, thanks for those links, but the problem has changed now...

Now the subclass is listed in Control Options, but when I change the values of the List<bool> or the Stringcollection they won't get saved...
Ralf Meier 2-Sep-21 9:03am    
Which Property do you mean exactly ?
Do you have set the DesignerVisibility to DesignerVisibility.Content at the Property-Attributes ?
Electro_Attacks 3-Sep-21 15:22pm    
I've updated the code and my question now...

Well it looks like the bools in my List<bool> can be created, but if I change their value from "false" to "true" it won't be saved..

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900