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...
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
{
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;
[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;
}
}
}
}
public MetroToolTip() { }
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();
}
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();
}
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)
{
if (this._labels.Count == 0 && this._checkers.Count == 0)
return;
RefreshControls();
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;
}
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];
}
}
}
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;
}
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 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();
}
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...