I have created a custom control that gets highlighted when the mouse hovers over it. The custom control also has a checkbox. When the mouse goes over the checkbox, the highlighting of the custom control does not occur. I've tried using WS_EX_TRANSPARENT on the checkbox but it isn't working for me.
int cbStyle = GetWindowLong(CompletedCheckBox.Handle, GWL_EXSTYLE);
SetWindowLong(CompletedCheckBox.Handle, GWL_EXSTYLE, cbStyle | WS_EX_TRANSPARENT);
How can I do this?
Thanks
Transparent only affects drawing, not mouse events. The check box is getting the mouse events, this in turn means that when you mouse over the checkbox, your control receives a MouseLeave event. To ensure that the background color changes, even when a child control ( at any level) gets a MouseEnter event, you need to track that a control of interest -- or any child, grand-child ..etc-- has the mouse over it. To do this, recurse through all descendant controls and intercept the appropriate events for them. To do this, try something similar to the class below.
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
AttachMouseEnterToChildControls(this);
}
void AttachMouseEnterToChildControls(Control con)
{
foreach (Control c in con.Controls)
{
c.MouseEnter += new EventHandler(control_MouseEnter);
c.MouseLeave += new EventHandler(control_MouseLeave);
AttachMouseEnterToChildControls(c);
}
}
private void control_MouseEnter(object sender, EventArgs e)
{
this.BackColor = Color.AliceBlue;
}
private void control_MouseLeave(object sender, EventArgs e)
{
this.BackColor = SystemColors.Control;
}
}
Related
So I have built an application in C# using Winforms and my application uses a few different buttons. I'd like to have a highlight on the button that has been clicked to show what 'tab' you're in.
I've tried doing the following;
// BUTTONS //
private void dashboard_btn_Click(object sender, EventArgs e)
{
// Load Form
OpenChildForm(new FormDashboard());
dashboard_btn.FlatAppearance.BorderColor = Color.Red;
dashboard_btn.FlatAppearance.BorderSize = 1;
}
However, this of course doesn't work nicely since it adds a border around the button but when I click another button the border also stays around the previous button.
How would you implement a feature to add a border around the button that get's clicked but have the border disappear after you click another button?
Thank you for any feedback!
EDIT:
I've implemented Jimi's advice and used the Leave event to change the border around the button back to 0. However I'm not sure how to implement this in a global way so all my buttons are subscribed to this event.
My code now looks like this;
// BUTTONS //
private void dashboard_btn_Click(object sender, EventArgs e)
{
// Load Form
OpenChildForm(new FormDashboard());
// Button Highlight
dashboard_btn.FlatAppearance.BorderColor = Color.Red;
dashboard_btn.FlatAppearance.BorderSize = 1;
}
// BUTTON REMOVE HIGHLIGHT //
private void dashboard_btn_Leave(object sender, EventArgs e)
{
dashboard_btn.FlatAppearance.BorderSize = 0;
}
EDIT 2:
I ended up using Jimi's example and this worked for me :)
This might lend itself to a RadioButton style functionality because clicking a different radio button in the same container will uncheck the others. So, to implement the "generalized approach" that you mention in your comment, you could make a simple custom RadioButtonEx class where the Appearance property is set to Button then change your border style when the Checked property changes. In this example, the Click event has been changed to static so that clicking on any button directs the event to the common onAnyClick method.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
RadioButtonEx.Click += onAnyClick;
}
private void onAnyClick(object sender, EventArgs e)
{
label1.Text = ((RadioButtonEx)sender).Text;
}
}
public class RadioButtonEx : RadioButton
{
public static new event EventHandler Click;
public RadioButtonEx()
{
FlatAppearance.BorderColor = Color.Red;
FlatAppearance.BorderSize = 1;
Appearance = Appearance.Button;
}
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
if(Checked)
{
FlatStyle = FlatStyle.Flat;
Click?.Invoke(this, EventArgs.Empty);
}
else
{
FlatStyle = FlatStyle.Standard;
}
}
}
I'm trying to apply focus behavior, similar to button blue border to FlowLayoutPanel. I tried to use GotFocus and LostFocus, but clearly that's not the way to go.
private void FlowLayoutPanel_Click(object sender, EventArgs e)
{
(sender as Control).BackColor = SystemColors.GradientActiveCaption;
//More operations.
}
private void Panel_LostFocus(object sender, System.EventArgs e)
{
(sender as Control).BackColor = default(Color);
//More operations.
}
While clicking on the FlowLayoutPanel nothing happens and while using tab the two event are invoked one after another.
Any suggestions?
FlowLayoutPanel is not a selectable control by default. You can create a custom flow layout panel by deriving from FlowLayoutPanel and set Selectable and UserMouse control styles to make it selectable by mouse. Also to accept tab stop, set TabStop property to true:
class ExFlowLayoutPanel:FlowLayoutPanel
{
public ExFlowLayoutPanel():base()
{
SetStyle(ControlStyles.Selectable, true);
SetStyle(ControlStyles.UserMouse, true);
TabStop = true;
}
}
Then you can handle GotFocus and LostFocus or Enter and Leave events.
The only point of using a FLP is to get it to arrange child controls. It is always a child control that gets the focus, not the FLP. So sure, nothing happens. You'd have to subscribe the Enter events of all the child controls to see the focus entering the panel or one of its children. Leave is much harder to get right, that's going to flicker like a cheap motel.
Very ugly solution, you don't want to it that way. Use the Application.Idle event instead, the best alternative when getting a reliable event just isn't practical. Check the Parent of this.ActiveControl, like this:
public Form1() {
InitializeComponent();
Application.Idle += CheckFlpFocus;
this.Disposed += delegate { Application.Idle -= CheckFlpFocus; };
}
private bool FlpHasFocus;
private void CheckFlpFocus(object sender, EventArgs e) {
bool hasFocus = false;
for (var ctl = this.ActiveControl; ctl != null; ctl = ctl.Parent) {
if (ctl == flowLayoutPanel1) hasFocus = true;
}
if (hasFocus != FlpHasFocus) {
FlpHasFocus = hasFocus;
flowLayoutPanel1.BackColor = hasFocus ? Color.Black : Color.White;
}
}
I notice all the other controls I am using such as tabs, textboxes, rich textboxes with text already inside it and everything else resize automatically when I change the main Form font. But labels are an exception to this rule is there a property I can turn on or off in the Label that will make it resize font with the form? It is a pretty straightforward yes or no question I know how to work around it I just like how all my controls are automatically resizing without any unnecessary code.
I've a solution here:
If your controls have the same font with your Form's Font, you shouldn't change the particular Font of them (via the Properties window or via code), instead you just change your Form's Font. If that has already been done, you can find the code lines (normally in Form.Designer.cs) which specify Font for your controls (something like yourControl.Font = ...) and remove those lines.
If your controls need different Font from your Form's. You can register the FontChanged event handler of your Form and change its children controls' Fonts accordingly (just the font size):
private void Form1_FontChanged(object sender, EventArgs e){
UpdateChildrenFont(this);
}
private void UpdateChildrenFont(Control parent){
foreach(Control c in parent.Controls){
c.Font = new Font(c.Font.FontFamily, parent.Font.Size, c.Font.Style);
UpdateChildrenFont(c);
}
}
The recursive method UpdateChildrenFont works in most cases but if you have some TabControl on your form, that doesn't work, because TabControl has another kind of collection to hold its tab pages called TabPages... I've encountered such a case. Another solution is to create your own Controls and override the OnParentChanged to register the Parent.FontChanged event handler accordingly, like this:
public class YourControl : TheBaseControl {
protected override void OnParentChanged(EventArgs e){
if(Parent != null){
Parent.FontChanged -= ParentFontChanged;
Parent.FontChanged += ParentFontChanged;
}
}
private void ParentFontChanged(object sender, EventArgs e){
Font = new Font(Font.FontFamily, Parent.Font.Size, Font.Style);
}
}
You should apply that model on all your controls on your form. That's very clean although requires you to have custom classes for your controls.
The last solution I can think of is playing with ControllAdded event, this is applied only on your Containers such as Form, GroupBox, Panel... and every control which has some child control on your form. Here is the code:
public class YourContainerClass: YourContainerBaseClass {
protected override void OnControlAdded(ControlEventArgs e){
Binding bind = new Binding("Font", this, "Font");
bind.Format += (s, ev) =>
{
Font inFont = (Font)ev.Value;
Binding bin = (Binding)s;
ev.Value = new Font(bin.Control.Font.FontFamily, inFont.Size, bin.Control.Font.Style);
};
e.Control.DataBindings.Clear();
e.Control.DataBindings.Add(bind);
base.OnControlAdded(e);
}
}
Or simply if that's your Form:
public class Form1 : Form {
public Form1(){
ControlAdded += FormControlAdded;
InitializeComponent();
}
private void FormControlAdded(object sender, ControlEventArgs e){
Binding bind = new Binding("Font", this, "Font");
bind.Format += (s, ev) =>
{
Font inFont = (Font)ev.Value;
Binding bin = (Binding)s;
ev.Value = new Font(bin.Control.Font.FontFamily, inFont.Size, bin.Control.Font.Style);
};
e.Control.DataBindings.Clear();
e.Control.DataBindings.Add(bind);
}
}
I have the following problem: I have a panel which has a specific color, say red.
When the user presses his mouse, the color of this panel gets stored in a variable. Then the user moves, his mouse still pressed, over to another panel. When he releases the mouse there, this panel should get the background color of the first that had been stored in the variable. My code looks something like this:
public Color currentColor;
private void ColorPickMouseDown(object sender, MouseEventArgs e)
{
Panel pnlSender = (Panel)sender;
currentColor = pnlSender.BackColor;
}
private void AttempsColorChanger(object sender, MouseEventArgs e)
{
Panel pnl = (Panel)sender;
pnl.BackColor = currentColor;
}
I need to identify the sender first because there are many possible panels that can trigger this event. The first MouseDown method works totally fine, the color is stored nicely in the variable. The secon one however doesn't even get triggered when the user does what I described above. When the ser clicks on the second panel, it works (there is an MouseUp part in a click aswell I guess).
What's wrong here? Why is the event not triggered when the user holds the mouse key down before?
(This answer assumes you are using Windows Forms.)
It could be that you need to capture the mouse by setting this.Capture = true in the MouseDown of the source control. (See Control.Capture)
If you did that, the source window would get the MouseUp event, and it would be the source window that had to determine the destination window under the mouse coords. You can do that using Control.GetChildAtPoint() (see this answer on Stack Overflow).
Use Windows Forms Drag and Drop Support Instead! <- Click for more info
I'm going to suggest you bite the bullet and use the .Net Drag and Drop methods to do this. It requires some reading up, but it will be much better to use it.
You start a drag in response to a MouseDown event by calling Control.DoDragDrop().
Then you need to handle the Control.DragDrop event in the drop target control.
There's a few more things you might need to do to set it up; see the Control.DoDragDrop() documentation for an example.
(For WPF drag and drop support, see here.)
when your mouse enter the target control , mouse down triggerd ang get target BackColor! you need add an boolean flag to your code :
public Color currentColor;
bool flag=false;
private void ColorPickMouseDown(object sender, MouseEventArgs e)
{
if(flag==false)
{
flag=true
Panel pnlSender = (Panel)sender;
currentColor = pnlSender.BackColor;
}
}
//assume mouse up for panles
private void AttempsColorChanger(object sender, MouseEventArgs e)
{
if(flag==true)
{
Panel pnl = (Panel)sender;
pnl.BackColor = currentColor;
flag=flase;
}
}
and also you need change your flag in mouseMove( if )
As I mentioned in my comment Mouse Events are captured by the originating control, You would probably be better off using the DragDrop functionality built into Windows Forms. Something like this should work for you. I assigned common event handlers, so they can be assigned to all of your panels and just work.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void panel_MouseDown(object sender, MouseEventArgs e)
{
((Control)sender).DoDragDrop(((Control)sender).BackColor,DragDropEffects.All);
}
private void panel_DragDrop(object sender, DragEventArgs e)
{
((Control)sender).BackColor = (Color)e.Data.GetData(BackColor.GetType());
}
private void panel_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
}
I know it's an old question but I had the same issue and none of the above answers worked for me. In my case I had to handle the MouseMove event in the target control and check for the mouse to be released. I did set 'BringToFront' on my target panel just in case that helped at all.
public Color currentColor;
private void ColorPickMouseDown(object sender, MouseEventArgs e)
{
Panel pnlSender = (Panel)sender;
currentColor = pnlSender.BackColor;
}
private void panelTarget_MouseMove(object sender, MouseEventArgs e)
{
//the mouse button is released
if (SortMouseLocation == Point.Empty)
{
Panel pnl = (Panel)sender;
pnl.BackColor = currentColor;
}
}
I want to add MouseOver and MouseLeave events to dynamical created panels in a flowLayoutPanel.
I added all panels in a list named "panels" and they are accessible with "panels[index]".
Now I want to dynamical add a MouseOver and MouseLeave event to each panel.
I thought it could be possible to get the panelname the Mouse is over and use just one method for each event and identify the panel the mouse is over with its panelname (panel.Name) but I found nothing in "sender".
Is there a way to do this?
My code:
//Method
private void PanelsMouseEnter(object sender, EventArgs e)
{
var panel = sender as Control;
foreach (Control control in this.fLpKoerper.Controls)
{
if (control.Name == panel.Name)
{
foreach (Panel panels in panelsKoerper)
{
if (panels.Name == panel.Name)
panels.BackColor = Color.DarkGray;
}
}
}
}
//Event
panelsKoerper[y].MouseEnter += PanelsMouseEnter;
var panel = sender as Control;
var thePanelName = panel.Name;
I believe you can generate one mouseover event for a control, copy that event method name and then paste it into another controls mouseover event box and that should work
So you would have this event
private void label1_MouseHover(object sender, EventArgs e)
{
//Code...
}
and then you could put 'label1_MouseHover' in any controls mouseover event