Show Tooltip programatically on a control inheriting from UserControl - c#

I have a custom control inheriting from the UserControl class. It has got a ToolTip property but I need to show it when the mouse is dragged over it.
The Tooltip.Show method expects the second argument of type Control. I'm not sure how to with it.
Any ideas/help appreciated.

SetToolTip() only needs to be called once
class Foo : Form {
ToolTip tooltip = new ToolTip();
public Foo(){
tooltip.SetToolTip(this, "This is a tooltip!");
foreach(Control ctrl in this.Controls) {
tooltip.SetToolTip(ctrl, "This is a tooltip!");
}
}
}

Instantiate your tooltip in constructor and show it on mouse hover event.
Taken from Joseph's answer in stackoverflow
public ToolTip tT { get; set; }
public ClassConstructor()
{
tT = new ToolTip();
}
private void MyControl_MouseHover(object sender, EventArgs e)
{
tT.Show("Why So Many Times?", this);
}
Hope it helps.

I needed this myself, and found the partial solution offered above could be improved upon.
Basically, you need to set the MouseHover event for all applicable child controls to the MouseHover of the parent UserControl.
this can be done with code as below in the MyUserControl class constructor:
class MyUserControl:UserControl
{
string strTooltip;
public ToolTip toolTip
{
get;
set;
}
public MyUserControl()
{
toolTip = new ToolTip();
foreach(Control ctrl in this.Controls)
{
ctrl.MouseHover += new EventHandler(MyUserControl_MouseHover);
ctrl.MouseLeave += new EventHandler(MyUserControl_MouseLeave);
}
}
void MyUserControl_MouseLeave(object sender, EventArgs e)
{
toolTip.Hide(this);
}
void MyUserControl_MouseHover(object sender, EventArgs e)
{
toolTip.Show(strToolTip, this, PointToClient(MousePosition));
}
}
Note that we are using the PointToClient(MousePosition) to position the tooltip where the usercontrol is located.
Otherwise, sometimes, it can cause the tooltip to get displayed at a random location on the screen.
Hope this helps someone! :)

Just hit this problem myself today, and came up with this rather simple solution.
Add the following source file to your codebase somewhere (I put it in a file ToolTipEx.cs)
namespace System.Windows.Forms
{
public static class ToolTipEx
{
private static void SetTipRecurse(ToolTip toolTip, Control control, string tip)
{
toolTip.SetToolTip(control, tip);
foreach (Control child in control.Controls)
SetTipRecurse(toolTip, child, tip);
}
public static void SetTooltip(this ToolTip toolTip, UserControl control, string tip)
{
SetTipRecurse(toolTip, control, tip);
}
}
}
If it is in another DLL, make sure the DLL is referenced.
Then all you have to do is make the normal call to toolTip.SetToolTip(myControl, "some tip"); and the compiler will take care of the rest for you.
Because the function essentially extends the ToolTip.SetToolTip() method to one having the signature
ToolTip(UserControl control, string tip);
which is higher up in the hierachy than the original
ToolTip(Control control, string tip);
When we are dealing with a UserControl, it will be called instead of the original.
The new method does a simple recursive call to give all sub controls then same tool tip as the parent control.
This code assumes that the UserControl will not have other controls added to it after the call to SetToolTip is made.

Related

Changing panel control from inside the panel

I am creating an application using WinForms. I have panel in which I show a user control. Inside this user control I have a button. When I click the button, I want to clear the panel and show a different user control. I am trying to do that using the following code:
private void btnCreateOffer_Click(object sender, EventArgs e)
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
MockUpForm.panMain.Controls.Clear();
MockUpForm.panMain.Controls.Add(myControl);
}
This works from the buttons placed directly in the parrent form, but when I use in inside the user control, it says:
'MockUpForm.panMain' is inaccessible due to its protection level
I suppose it has something to do with private/public classes. But I would rather have the "correct" solution, as opposed to just changing everything to public.
Any suggestions on how this is usually done?
Solution 1 (ugly):
Make panMain public in the designer:
Solution 2 (somewhat better):
Provide public methods to achieve such tasks safely:
// MockUpForm code:
public void ClearPanelControls()
{
panMain.Controls.Clear();
}
public void AddControlToPanel(Control c)
{
panMain.Controls.Add(c);
}
And then call these methods instead of publishing the full panel, which makes possible for example to dispose the whole panel and such things...
To access parent form's control from UserControl You can use delegate and event
something like this....
Windows Form (Parent Form) Code....
public Form1()
{
InitializeComponent();
userControl1.CreateOffer += UserControl1_CreateOffer;
}
private void UserControl1_CreateOffer()
{
var myControl = new WindowsFormsDemo.View.CreateOffer();
this.panMain.Controls.Clear();
this.panMain.Controls.Add(myControl);
}
User Control Code...
internal delegate void CreateOfferDelegate();
internal event CreateOfferDelegate CreateOffer;
public UserControl1()
{
InitializeComponent();
}
private void btnCreateOffer_Click(object sender, EventArgs e)
{
CreateOffer();
}

How do I read a property from my main form in a user control

I have written a user control, MenuItem, which inherits from a Form Label.
I have a backgroundworker thread whose IsBusy property is exposed through a property in the MainForm as IsBackgroundBusy.
How do I read this property from the MenuItem usercontrol? I am currently using Application.UseWaitCursor and I set that in the backgroundworker and it works perfectly, however I do not want the cursor to change. That's why I figured a property that I could set would be much better.
Here is the code in my MainForm:
public partial class MainForm : Form
{
public bool IsBackgroundBusy
{
get
{
return bwRefreshGalleries.IsBusy;
}
}
Here is the code for my usercontrol:
public partial class MenuItem: Label
{
private bool _disableIfBusy = false;
[Description("Change color if Application.UseWaitCursor is True")]
public bool DisableIfBusy
{
get
{
return _disableIfBusy;
}
set
{
_disableIfBusy = value;
}
}
public MenuItem()
{
InitializeComponent();
}
protected override void OnMouseEnter( EventArgs e )
{
if ( Application.UseWaitCursor && _disableIfBusy )
{
this.BackColor = SystemColors.ControlDark;
}
else
{
this.BackColor = SystemColors.Control;
}
base.OnMouseEnter( e );
}
(Note: it's not clear to me whether you have an actual UserControl here or not. The MenuItem class you show inherits Label, not UserControl. You should probably avoid using the term "usercontrol" or "user control" when you are not actually dealing with a UserControl object).
Absent a complete code example, it's hard to know exactly what the right solution here is. However, assuming you are using the BackgroundWorker in a typical fashion, then you simply need for the owner of the control (i.e. the containing Form) to pass the necessary state to the control as it changes. E.g.:
class MenuItem : Label
{
public bool IsParentBusy { get; set; }
}
// I.e. some method where you are handling the BackgroundWorker
void button1_Click(object sender, EventArgs e)
{
// ...some other initialization...
bwRefreshGalleries.RunWorkerCompleted += (sender1, e1) =>
{
menuItem1.IsParentBusy = false;
};
menuItem1.ParentIsBusy = true;
bwRefreshGalleries.RunAsync();
}
If you already have a handler for the RunWorkerCompleted event, then just put the statement to set the IsParentBusy property there instead of adding another handler.
Then instead of using the Application.UseWaitCursor property, you can just look at the IsParentBusy property.
There are other mechanisms you could use; I do agree with the general sentiment that the MenuItem control should not be tied to your specific Form sub-class. If for some reason the above doesn't work in your case, you need to elaborate on your question: provide a good code example and explain exactly why simply having the container of the control manage its state directly doesn't work for you

UserControl with embedded MonthCalendar problems

I am hoping someone can help me out. I have created a usercontrol in C# for use on a Winform. The control contains various controls including a monthCalendar control however the monthCalendar control is where my problem lies.
I want the parent form that holds my usercontrol to trigger a block of code to query a database using linq when the dateSelected event of the monthCalendar within the usercontrol is triggered. The idea being that the usercontrol should not be aware of the data access side of things so that the usercontrol can be used in other projects.
What I was hoping was that there was a way that I could make the dateSelected event available to the parent form; I have done this successfully with click events etc for other controls I just can't seem to make this work for the monthCalendar as DateSelected uses DateRangeEventHandler rather than the standard EventHandler.
I hope this is clear as i have been around the block with this one so i'm not sure what makes any sense any more :) Any help or advice in how I could go about coding this would be very much appreciated.
Same way you would with the Button events.
In your UserControl, it would look something like this:
public event DateRangeEventHandler DateChanged {
add { monthCalendar1.DateChanged += value; }
remove { monthCalendar1.DateChanged -= value; }
}
Then in your form, like all controls with events:
userControl11.DateChanged += userControl11_DateChanged;
void userControl11_DateChanged(object sender, DateRangeEventArgs e) {
// do something...
}
You can make MonthCalendar public in your UserControl and then in your Form just subscribe to the event using:
this.userControl.Monthcalender.DateSelected += new DateRangeEventHandler(Monthcalender_DateSelected)
or you can create a new event in your UserControl which will be raised on MonthCalendar.DataSelected. and in your Form subscribe to that event, something like:
UserControl:
public UserControl1()
{
InitializeComponent();
this.monthCalendar1.DateSelected += new DateRangeEventHandler(monthCalendar1_DateSelected);
}
public void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e)
{
OnSeChanged(e);
}
public event DateRangeEventHandler SeChanged;
protected virtual void OnSeChanged(DateRangeEventArgs e)
{
if (SeChanged != null)
{
SeChanged(this, e);
}
}
Form:
userControl11.SeChanged += new DateRangeEventHandler(userControl11_SeChanged);

Pass Value Selected From a User Control to a Parent

I have a Winform dialog that contains several user controls - all of them are some sort of Datagridview. The main parent has details about a user, and the user controls each have additional details on that person. When my Dialog first loads all of the UserControls work but I am trying to figure out how to update the UserControl2 based on a position change in UserControl1.
So, I am trying to select a row in UserControl1 and have the data in UserControl2 update based on a value that I just selected.
I have tried using MouseDownEvents on the UserControl1 and BindingSourcePositionChanged but I can't figure out how to get the value selected back to my parent form and then use that value to refresh the other datagrids?
I looked at delegates and events but I guess the lack of sleep is making it incredibly hard to comprehend. I understand that I need to create my delegate and event on the UserControl1 and then somehow call it on my mainform but that's where I get stuck and have no clue where to start.
Is this the right direction? Or is there another way to get this done? Can anyone offer up any suggestions on how this works?
Yes this is the correct approach something like the following will provide an event handler that you can use to the retrieve a public property from the UserControl:
public class SomeClass : BaseControl
{
public event EventHandler PersonSelected;
public string Name{get;set;}
protected void FindUser()
{
var find = new Button {ID = (ToString() + "search"), Text = "Search"};
find.Click += delegate(object sender, EventArgs e)
{
if (PersonSelected!= null)
{
//forward this event to the page's event handler
PersonSelected(this, e);
}
};
}
}
public class SomeOtherClass : Page
{
public void Main()
{
var sp = (SomeClass)Control;
sp.PersonSelected += BtnClick;
}
public void BtnClick(object sender, EventArgs e)
{
//Get some value from the (SomeClass)Control here
}
}

How can I make sender in an eventhandler my custom control and not the labels inside it

I created a custom control which is actually just two labels inside a panel. I want to add an event so that when my custom control is clicked (which would really be clicking either one of the labels) it would return the properties of the whole control, I think that would mean that 'sender' in the event handler would be my custom control and not one of the lables. I don't know if I made myself clear but what I mean is to treat the control as a 'whole' when it is clicked mmm anyway hope you get my point.
How can I do this?
Thanks in advance
What you can do is let the custom control consume the event of the label, and in the custom control implement a new event. Then, when the label event fires, you can fire your own event from the custom control.
For example:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public event EventHandler MyCustomClickEvent;
protected virtual void OnMyCustomClickEvent(EventArgs e)
{
// Here, you use the "this" so it's your own control. You can also
// customize the EventArgs to pass something you'd like.
if (MyCustomClickEvent != null)
MyCustomClickEvent(this, e);
}
private void label1_Click(object sender, EventArgs e)
{
OnMyCustomClickEvent(EventArgs.Empty);
}
}
You can get the container object of your label and cast it to your custom control
private void Label1_Click(object sender, System.EventArgs e)
{
Box box = (object as Label).Parent as Box;
if(box != null)
{
//Do what you need here.
}
}

Categories