C# Apply focused properties to FlowLayoutPanel like button behavior - c#

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;
}
}

Related

Prevent selecting text in a TextBox

Similar questions have been already asked (e.g., here), however I've not found an answer for my specific case. I'm building a custom control based on a DevExpress control, which in turns is based on standard TextBox and I've a flickering problem that seems due to the base TextBox component, which tries to update selection.
Without explaining all the details of my custom control, to reproduce the problem you just need to place a TextBox inside a Form and then use this code:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
textBox1.MouseMove += TextBox1_MouseMove;
}
private void TextBox1_MouseMove(object sender, MouseEventArgs e) {
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
}
}
If you launch it, you click on the TextBox and then you move the cursor toward right you will notice the flickering problem (see video here). For my custom control I would need to avoid this flickering. I'm bound to use a TextBox (so no RichTextBox). Any idea?
The solution has been provided in the meantime by Reza Aghaei by overriding WndProc and intercepting WM_SETFOCUS messages. See here
Depending on what u want to do there are several solutions:
If you want to prevent the selection it would be:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
(sender as TextBox).SelectionLength = 0;
}
Or for selecting all:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
(sender as TextBox).SelectAll();
}
And besides that u also could specify the conditions for selecting, for example:
private void TextBox1_MouseMove(object sender, MouseEventArgs e)
{
(sender as TextBox).Text = DateTime.Now.Ticks.ToString();
if (MouseButtons == MouseButtons.Left) (sender as TextBox).SelectAll();
else (sender as TextBox).SelectionLength = 0;
}
But as long as you want to select the text you always will get some flickering, because a normal Textbox has not the possibility to use things like BeginEdit and EndEdit and so it will change the text first and then select it.
Looking at the video it looks like your textbox is calling WM_ERASEBKGND unnecessarily. In order to remedy this problem you can subclass the textbox class and intercept these messages. Below is sample code which should do the trick (untested) Disclaimer: I have used this technique for other WinForm controls that had the type of flicker shown in your video but not TextBox. If it does work for you, please let me know. Good luck!
// textbox no flicker
public partial class TexttBoxNF : TextBox
{
public TexttBoxNF()
{
}
public TexttBoxNF(IContainer container)
{
container.Add(this);
InitializeComponent();
//Activate double buffering
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
//Enable the OnNotifyMessage event so we get a chance to filter out
// Windows messages before they get to the form's WndProc
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
//http://stackoverflow.com/questions/442817/c-sharp-flickering-listview-on-update
protected override void OnNotifyMessage(Message m)
{
//Filter out the WM_ERASEBKGND message
if (m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}

Register MouseEnter/MouseLeave events for disabled controls in windows form?

I want to register MouseEnter/MouseLeave events for disabled buttons. It does not work allthough it does work for enabled buttons..
//Enable Disable controls on form load
EnableDisableControls("Load");
var grupButtons = control.Controls.OfType<Button>();
foreach (Button btns in grupButtons)
{
//btns.MouseMove += new MouseEventHandler(MainframeDataExchangeTool_MouseMove);
btns.MouseEnter += new EventHandler(btns_MouseEnter);
btns.MouseLeave += new EventHandler(btns_MouseLeave);
}
private void btns_MouseEnter(object sender, EventArgs e)
{
}
private void btns_MouseLeave(object sender, EventArgs e)
{
var parent = sender as Control;
string tipstring = string.Empty;
if (parent == null)
{
return;
}
string enter = sender.GetType().ToString() + ": MouseEnter";
}
It is working for enable button ...but what to do for disable button ... I have to show tooltip operation on mouseenter and make it disapper immediately on mouseleave ?
yes , when you disable button , events will disable.
you can use this trick:
put your button in panel1 ,
then use the same event of button for panel1. like this:
btns.MouseEnter += new EventHandler(btns_MouseEnter);
btns.MouseLeave += new EventHandler(btns_MouseLeave);
panel1.MouseEnter += new System.EventHandler(btns_MouseEnter);
panel1.MouseLeave += new System.EventHandler(btns_MouseLeave);
it will work.
You can try some Form-wide Mouse message solution like this:
//Suppose your disabled Button is button1
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
button1.Enabled = false;
button1.BackColor = Color.Green;
//Try this to see it in action
button1.MouseEnter += (s, e) => {
button1.BackColor = Color.Red;
};
button1.MouseLeave += (s, e) => {
button1.BackColor = Color.Green;
};
Application.AddMessageFilter(this);//Add the IMessageFilter to the current Application
}
bool entered;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x200) //WM_MOUSEMOVE = 0x200
{
if (Control.FromHandle(m.HWnd) == button1.Parent &&
button1.ClientRectangle.Contains(button1.PointToClient(MousePosition)))
{
if (!entered) {
entered = true;
//Raise the MouseEnter event via Reflection
typeof(Button).GetMethod("OnMouseEnter", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.Invoke(button1, new[] { EventArgs.Empty });
}
}
else if (entered) {
//Raise the MouseLeave event via Reflection
typeof(Button).GetMethod("OnMouseLeave", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
.Invoke(button1, new []{EventArgs.Empty});
entered = false;
}
}
return false;
}
}
Why can't you try this?
By adding MouseMove event on the form...
Alternatively, if you want an easy way of maintaining the event handling, you could just never actually disable the button. Add some sort of wrapper class around a button that changes the the implementation of your buttons.
The new disable property could just change some CSS on the button and change a property that would make the click handler not do anything (or other relevant events).
I tried many but ended up using this simple trick which I think it is more effective.
Create a subclass(CustomControl with just base control in it) which extends UserControl
then instead of setting "Enabled" property to false create a Method which disables just basecontrol in it instead of whole CustomControl.
Set the tool tip on CustomControl still will be able to fire eventhandlers setting the basecontrol disabled. This works wherever CustomControl is in use rather than coding on every form you use with.
Here is the hint.. :)
public partial class MyTextBox : UserControl
{
...
...
...
public void DisableMyTextBox()
{
this.txt.Enabled = false; //txt is the name of Winform-Textbox from my designer
this.Enabled = true;
}
public void EnableMyTextBox()
{
this.txt.Enabled = true;
this.Enabled = true;
}
//set the tooltip from properties tab in designer or wherever
}

MouseEnter & MouseLeave objectname

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

How can I disable a tab inside a TabControl?

Is there a way to disable a tab in a TabControl?
Cast your TabPage to a Control, then set the Enabled property to false.
((Control)this.tabPage).Enabled = false;
Therefore, the tabpage's header will still be enabled but its contents will be disabled.
The TabPage class hides the Enabled property. That was intentional as there is an awkward UI design problem with it. The basic issue is that disabling the page does not also disable the tab. And if try to work around that by disabling the tab with the Selecting event then it does not work when the TabControl has only one page.
If these usability problems do not concern you then keep in mind that the property still works, it is merely hidden from IntelliSense. If the FUD is uncomfortable then you can simply do this:
public static void EnableTab(TabPage page, bool enable) {
foreach (Control ctl in page.Controls) ctl.Enabled = enable;
}
You can simply use:
tabPage.Enabled = false;
This property is not shown, but it works without any problems.
You can program the Selecting event on TabControler to make it impossible to change to a non-editable tab:
private void tabControler_Selecting(object sender, TabControlCancelEventArgs e)
{
if (e.TabPageIndex < 0) return;
e.Cancel = !e.TabPage.Enabled;
}
You could register the "Selecting" event and cancel the navigation to the tab page:
private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
{
if (e.TabPage == tabPage2)
e.Cancel = true;
}
Another idea is to put all the controls on the tabpage in a Panel control and disable the panel! Smiley
You could also remove the tabpage from the tabControl1.TabPages collection. That would hide the tabpage.
Credits go to littleguru # Channel 9.
Presumably, you want to see the tab in the tab control, but you want it to be "disabled" (i.e., greyed, and unselectable). There is no built-in support for this, but you can override the drawing mechanism to give the desired effect.
An example of how to do this is provided here.
The magic is in this snippet from the presented source, and in the DisableTab_DrawItem method:
this.tabControl1.DrawMode = TabDrawMode.OwnerDrawFixed;
this.tabControl1.DrawItem += new DrawItemEventHandler( DisableTab_DrawItem );
Extending upon Cédric Guillemette answer, after you disable the Control:
((Control)this.tabPage).Enabled = false;
...you may then handle the TabControl's Selecting event as:
private void tabControl_Selecting(object sender, TabControlCancelEventArgs e)
{
e.Cancel = !((Control)e.TabPage).Enabled;
}
This will remove the tab page, but you'll need to re-add it when you need it:
tabControl1.Controls.Remove(tabPage2);
If you are going to need it later, you might want to store it in a temporary tabpage before the remove and then re-add it when needed.
The only way is to catch the Selecting event and prevent a tab from being activated.
The most tricky way is to make its parent equals null (make the tab alone without parent):
tabPage.Parent = null;
And when you want to return it back (will return it back at the end of pages collection) :
tabPage.Parent = tabControl;
And if you want to return it back in a specific location among the pages you can use :
tabControl.TabPages.Insert(indexLocationYouWant, tabPage);
I had to handle this a while back. I removed the Tab from the TabPages collection (I think that's it) and added it back in when the conditions changed. But that was only in Winforms where I could keep the tab around until I needed it again.
I've removed tab pages in the past to prevent the user from clicking them. This probably isn't the best solution though because they may need to see that the tab page exists.
Using events, and the properties of the tab control you can enable/disable what you want when you want. I used one bool that is available to all methods in the mdi child form class where the tabControl is being used.
Remember the selecting event fires every time any tab is clicked. For large numbers of tabs a "CASE" might be easier to use than a bunch of ifs.
public partial class Form2 : Form
{
bool formComplete = false;
public Form2()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
formComplete = true;
tabControl1.SelectTab(1);
}
private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
{
if (tabControl1.SelectedTab == tabControl1.TabPages[1])
{
tabControl1.Enabled = false;
if (formComplete)
{
MessageBox.Show("You will be taken to next tab");
tabControl1.SelectTab(1);
}
else
{
MessageBox.Show("Try completing form first");
tabControl1.SelectTab(0);
}
tabControl1.Enabled = true;
}
}
}
I've solved this problem like this:
I've got 3 tabs and I want to keep user at the first tab if he didnt log in,
so on the SelectingEvent of TabControl I wrote
if (condition) { TabControl.Deselect("2ndPage"); TabControl.Deselect("3dPage"); }
The user cannot click on tabs to navigate, but they can use the two buttons (Next and Back). The user cannot continue to the next if the //conditions are no met.
private int currentTab = 0;
private void frmOneTimeEntry_Load(object sender, EventArgs e)
{
tabMenu.Selecting += new TabControlCancelEventHandler(tabMenu_Selecting);
}
private void tabMenu_Selecting(object sender, TabControlCancelEventArgs e)
{
tabMenu.SelectTab(currentTab);
}
private void btnNextStep_Click(object sender, EventArgs e)
{
switch(tabMenu.SelectedIndex)
{
case 0:
//if conditions met GoTo
case 2:
//if conditions met GoTo
case n:
//if conditions met GoTo
{
CanLeaveTab:
currentTab++;
tabMenu.SelectTab(tabMenu.SelectedIndex + 1);
if (tabMenu.SelectedIndex == 3)
btnNextStep.Enabled = false;
if (btnBackStep.Enabled == false)
btnBackStep.Enabled = true;
CannotLeaveTab:
;
}
private void btnBackStep_Click(object sender, EventArgs e)
{
currentTab--;
tabMenu.SelectTab(tabMenu.SelectedIndex - 1);
if (tabMenu.SelectedIndex == 0)
btnBackStep.Enabled = false;
if (btnNextStep.Enabled == false)
btnNextStep.Enabled = true;
}
tabControl.TabPages.Remove(tabPage1);
This is an old question, but someone may benefit from my addition. I needed a TabControl that would show hidden tabs successively (after an action was performed on the current tab). So, I made a quick class to inherit from and called HideSuccessive() on Load:
public class RevealingTabControl : TabControl
{
private Action _showNextRequested = delegate { };
public void HideSuccessive()
{
var tabPages = this.TabPages.Cast<TabPage>().Skip(1);
var queue = new ConcurrentQueue<TabPage>(tabPages);
tabPages.ToList().ForEach(t => t.Parent = null);
_showNextRequested = () =>
{
if (queue.TryDequeue(out TabPage tabPage))
tabPage.Parent = this;
};
}
public void ShowNext() => _showNextRequested();
}
There is the XtraTabPage.PageEnabled property allowing you to disable certain pages.
Here the solution that i implement:
private void switchTapPage(TabPage tabPage)
{
foreach(TabPage page in tabControl1.TabPages)
{
tabControl1.TabPages.Remove(page);
}
tabControl1.TabPages.Add(tabPage);
}
Basically, i just call this method sending the tabPage that i currently need to show, the method will remove all the tabPages on the tabControl and after that it will just add the one that i sent it.
So the rest of the tabHeaders will not shown and they will be inaccessible, because they dont even exists in the tabControl.
I took the idea from the #stormenet answer.
You can do it through the tabpages: tabPage1.Hide(), tabPage2.Show() etc.
In the form load event if we write this.tabpage.PageEnabled = false, the tabpage will be disabled.
Assume that you have these controls:
TabControl with name tcExemple.
TabPages with names tpEx1 and tpEx2.
Try it:
Set DrawMode of your TabPage to OwnerDrawFixed;
After InitializeComponent(), make sure that tpEx2 is not enable by adding this code:
((Control)tcExemple.TabPages["tpEx2").Enabled = false;
Add to Selection tcExemple event the code below:
private void tcExemple_Selecting(object sender, TabControlCancelEventArgs e)
{
if (!((Control)e.TabPage).Enabled)
{
e.Cancel = true;
}
}
Attach to DrawItem event of tcExemple this code:
private void tcExemple_DrawItem(object sender, DrawItemEventArgs e)
{
TabPage page = tcExemple.TabPages[e.Index];
if (!((Control)page).Enabled)
{
using (SolidBrush brush = new SolidBrush(SystemColors.GrayText))
{
e.Graphics.DrawString(page.Text, page.Font, brush, e.Bounds);
}
}
else
{
using (SolidBrush brush = new SolidBrush(page.ForeColor))
{
e.Graphics.DrawString(page.Text, page.Font, brush, e.Bounds);
}
}
}
It will make the second tab non-clickable.
I could not find an appropriate answer to the question. There looks to be no solution to disable the specific tab. What I did is to pass the specific tab to a variable and in SelectedIndexChanged event put it back to SelectedIndex:
//variable for your specific tab
int _TAB = 0;
//here you specify your tab that you want to expose
_TAB = 1;
tabHolder.SelectedIndex = _TAB;
private void tabHolder_SelectedIndexChanged(object sender, EventArgs e)
{
if (_TAB != 0) tabHolder.SelectedIndex = _TAB;
}
So, you don't actually disable the tab, but when another tab is clicked it always returns you to the selected tab.
in C# 7.0, there is a new feature called Pattern Matching. You can disable all tabs via Type Pattern.
foreach (Control control in Controls)
{
// the is expression tests the variable and
// assigned it to a new appropriate variable type
if (control is TabControl tabs)
{
tabs.Enabled = false;
}
}
Use:
tabControl1.TabPages[1].Enabled = false;
By writing this code, the tab page won't be completely disabled (not being able to select), but its internal content will be disabled which I think satisfy your needs.
The solution is very simple.
Remove/comment this line
this.tabControl.Controls.Add(this.YourTabName);
in IntializeComponent() method in MainForm.cs
MyTabControl.SelectedTab.Enabled = false;

Transparent Checkbox in Custom Control Using C#

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;
}
}

Categories

Resources