Flashing ToolStripButton - c#

I am using a ToolStrip with a number of ToolStripButtons.
What I would like is to be able to flash one of the buttons to get the user's attention.
For example, if they have made changes to information and need to click the Save button.
If this were a normal button I could do this using a Timer and periodically changing the BackColor however this doesn't work with a ToolStrip.
I could create a Renderer subclass and assign it to the ToolStrip but this appears to only get used in specific situations - i.e. it's event driven.
Does anyone have any ideas?

Well, just use a custom renderer so you can change the color of the button's background. With a timer that blinks it. Add a new class to your project and paste this code:
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
class BlinkingButtonRenderer : ToolStripProfessionalRenderer {
public BlinkingButtonRenderer(ToolStrip strip) {
this.strip = strip;
this.strip.Renderer = this;
this.strip.Disposed += new EventHandler(strip_Disposed);
this.blinkTimer = new Timer { Interval = 500 };
this.blinkTimer.Tick += delegate { blink = !blink; strip.Invalidate(); };
}
public void BlinkButton(ToolStripButton button, bool enable) {
if (!enable) blinkButtons.Remove(button);
else blinkButtons.Add(button);
blinkTimer.Enabled = blinkButtons.Count > 0;
strip.Invalidate();
}
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) {
var btn = e.Item as ToolStripButton;
if (blink && btn != null && blinkButtons.Contains(btn)) {
Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size);
e.Graphics.FillRectangle(Brushes.Black, bounds);
}
else base.OnRenderButtonBackground(e);
}
private void strip_Disposed(object sender, EventArgs e) {
blinkTimer.Dispose();
}
private List<ToolStripItem> blinkButtons = new List<ToolStripItem>();
private bool blink;
private Timer blinkTimer;
private ToolStrip strip;
}
Sample usage in a form with a Toolstrip containing a button:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
blinker = new BlinkingButtonRenderer(toolStrip1);
}
private void toolStripButton1_Click(object sender, EventArgs e) {
blink = !blink;
blinker.BlinkButton(toolStripButton1, blink);
}
private bool blink;
private BlinkingButtonRenderer blinker;
}

Related

C# Winforms How to Understand Form object is moved

I want to trigger an event only when the form is moved via the mouse click over the title bar. I could not find proper event for this.
Currently I implemented a move event for my forms in winform. I only want the move event to be triggered when the user drags the form via clicking on the title bar. However, this event is triggered also when form is tried to be resized by mouse or minimized/maximized. How can I disable this? I only want to trigger an event only when the form is moved. I am trying to implement my own floating forms and I want to catch this specific event to change the MDiParent of the form.
You can check if the form is resized and the Form's FormWindowState is changed in the Move event, if not so, you can decide that Form is moving using the title bar.
To do so, you should cache both the window state and size each time when Move event is triggered.
Note: The Move event is triggered even when you change the location of the form via the Location property, not only using the title bar. So, the event FromDragged will be triggered in the case. This is a "false positive".
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WhenDraggingUsingCaptionBar
{
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
FormDragged += Form1_FormDragged;
}
private void Form1_FormDragged(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
public event EventHandler FormDragged;
private Size _cachedSize = new Size(0, 0);
private FormWindowState _cachedState = FormWindowState.Normal;
private void Form1_Move(object sender, EventArgs e)
{
if (_cachedSize == Size && _cachedState == WindowState)
if (FormDragged != null)
FormDragged(this, new EventArgs());
_cachedSize = Size;
_cachedState = WindowState;
}
}
}
As an addition, there is a low-level solution using Win32 API. This solution eliminates the "false-positive" issues in the approach above.
You can handle the WM_NCLBUTTONDOWN message. This message is sent when you down the mouse left button in the non-client area of the window. When we get the message, we set a variable to true. We also catch another message WM_EXITSIZEMOVE to understand when the dragging the window is stopped and set the variable to false.
If the variable is set to true when the move event is triggered, we can say the window is being dragged using the title bar.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace WhenDraggingUsingCaptionBar
{
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
FormDragged += Form1_FormDragged;
}
private void Form1_FormDragged(object sender, EventArgs e)
{
Debug.WriteLine("{1}: Move:{0}", _ncbuttonDown, DateTime.Now);
}
public event EventHandler FormDragged;
private const int WM_NCLBUTTONDOWN = 0x00A1;
private const int WM_EXITSIZEMOVE = 0x0232;
private bool _ncbuttonDown = false;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONDOWN)
_ncbuttonDown = true;
else if (m.Msg == WM_EXITSIZEMOVE)
_ncbuttonDown = false;
base.WndProc(ref m);
}
private void CustomForm_Move(object sender, EventArgs e)
{
if (_ncbuttonDown)
if (FormDragged != null)
FormDragged(this, new EventArgs());
}
}
}

winforms: move a borderless form (without title bar)

I have a host class which inherits from Form. I use it to embed an WPF user control to use it in a winform application. This is customizable, I mean, you can choose to make form borderless or not.
If borderless, you cannot move the window, so I need to implement it manually. In this case I subscribe to some event which do the trick (I have removed the not necessary things from the class to focus only on the important parts):
public class MyHostDialog : Form
{
public MyDialogHost()
{
InitializeComponent();
}
public MyHostDialog(MyDialog myDialog) : this()
{
this.ElementHost.Child = new MyHostDialogView(myDialog);
if (this.Borderless)
{
this.ElementHost.Child.MouseDown += Child_MouseDown;
this.ElementHost.Child.MouseUp += Child_MouseUp;
this.ElementHost.Child.MouseMove += Child_MouseMove;
}
}
// BELOW CODE DOES NOT WORK, IT WOULD WORK IF Input.MouseButtonEventArgs and Input.MouseEventArgs WERE
// System.Windows.FORMS.MouseEventArgs INSTEAD.
private bool dragging = false;
private System.Drawing.Point startPoint = new System.Drawing.Point(0,0);
private void Child_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
dragging = true;
startPoint = new System.Drawing.Point(e.X, e.Y);
}
private void Child_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
dragging = false;
}
private void Child_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (dragging)
{
System.Drawing.Point p= PointToScreen(e.Location);
Location = new System.Drawing.Point(p.X - this.startPoint.X, p.Y - this.startPoint.Y);
}
}
}
Above code would work if instead of using Input.MouseButtonEventArgs and Input.MouseEventArgs, they were System.Windows.FORMS.MouseEventArgs. So, how to convert that code to work with MouseButtonEventArgs?

How to make an event span over multiple forms

The project I am working on needs a couple buttons on multiple forms, instead of doing the code shown below I was hoping it could be made global.
This is only one part of the project, all the code does is enlarge the button picture when the user hovers over it.
I've tried looking at classes, tags and attributes. I know classes can be made to use across multiple forms but I cant find out if they work with events.
private void btnEnter_MouseEnter(object sender, EventArgs e)
{
Button btn = (Button)sender;
btn.Size = new Size(299, 102);
}
private void btnLeave_MouseLeave(object sender, EventArgs e)
{
Button btn = (Button)sender;
btn.Size = new Size(289, 92);
}
You can create an inherited button. Add a new class then make sure you put : Button after the class name.
using System.Drawing;
using System.Windows.Forms;
namespace InheritedButton
{
public class ExpandButton : Button
{
public Size EnterSize { get; set; }
private Size _LeaveSize;
public Size LeaveSize
{
get
{
return (_LeaveSize);
}
set
{
_LeaveSize = value;
this.Size = LeaveSize;
}
}
public ExpandButton() : base()
{
}
protected override void OnMouseEnter(EventArgs e)
{
this.Size = EnterSize;
base.OnMouseEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
this.Size = LeaveSize;
base.OnMouseLeave(e);
}
}
}
Build your project and the new button will appear in the toolbox. Drop it onto a form/control and make sure you set the EnterSize and LeaveSize. EnterSize determines the size of the button when you mouse over and LeaveSize sets the initial size and sets the size of the button when you mouse out. You don't need to set the Size property, just set LeaveSize.
Any time you want to use the expanding/contracting button just use the inherited one instead.

windows form how to add an icon to the right or left of a text box

I am building a Windows form application and I have a text box for searching purposes.
I would like to put a search icon inside the text box, at the right or left
like this:
I would prefer at the right
Update 1
I am asking about Windows forms not ASP.net or MVC
You can use a Panel, a TextBox and a PictureBox.
The TextBox must be placed in a Panel so you can't write over your search picture.
You can create a new UserControl which will do the required job. You have to extend the TextBox class for that. Look at the code below:
public class IconTextBox : System.Windows.Forms.TextBox
{
public IconTextBox() : base() { SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true); this.Multiline = true; }
public System.Drawing.Bitmap BitmapImage
{
set;
get;
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaint(e);
System.Drawing.Image img = BitmapImage as System.Drawing.Image;
e.Graphics.DrawImage(img, new System.Drawing.Point(this.Width - (img.Width), 0));
}
}
And in the OnPaint method you can specify the image. Also you can extend this to have a custom property which can be the image path. Your choice.
Along the lines of Atanas's answer I found the following to work well. The SearchImage and CancelSearchImage properties can be set to control the images used.
public class SearchTextBox : TextBox
{
private const int EM_SETMARGINS = 0xd3;
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private PictureBox searchPictureBox;
private Button cancelSearchButton;
public SearchTextBox()
{
cancelSearchButton = new Button();
cancelSearchButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
cancelSearchButton.Size = new Size(16, 16);
cancelSearchButton.TabIndex = 0;
cancelSearchButton.TabStop = false;
cancelSearchButton.FlatStyle = FlatStyle.Flat;
cancelSearchButton.FlatAppearance.BorderSize = 0;
cancelSearchButton.Text = "";
cancelSearchButton.Cursor = Cursors.Arrow;
Controls.Add(cancelSearchButton);
cancelSearchButton.Click += delegate
{
Text = "";
Focus();
};
searchPictureBox = new PictureBox();
searchPictureBox.Anchor = AnchorStyles.Top | AnchorStyles.Right;
searchPictureBox.Size = new Size(16, 16);
searchPictureBox.TabIndex = 0;
searchPictureBox.TabStop = false;
Controls.Add(searchPictureBox);
// Send EM_SETMARGINS to prevent text from disappearing underneath the button
SendMessage(Handle, EM_SETMARGINS, (IntPtr)2, (IntPtr)(16 << 16));
UpdateControlsVisibility();
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
UpdateControlsVisibility();
}
private void UpdateControlsVisibility()
{
if (string.IsNullOrEmpty(Text))
{
cancelSearchButton.Visible = false;
searchPictureBox.Visible = true;
}
else
{
cancelSearchButton.Visible = true;
searchPictureBox.Visible = false;
}
}
[Browsable(true)]
public Image SearchImage
{
set
{
searchPictureBox.Image = value;
searchPictureBox.Left = Width - searchPictureBox.Size.Width - 4;
searchPictureBox.Top = Height - searchPictureBox.Size.Height - 4;
}
get { return searchPictureBox.Image; }
}
[Browsable(true)]
public Image CancelSearchImage
{
set
{
cancelSearchButton.Image = value;
cancelSearchButton.Left = Width - searchPictureBox.Size.Width - 4;
cancelSearchButton.Top = Height - searchPictureBox.Size.Height - 4;
}
get { return cancelSearchButton.Image; }
}
}
Using a user control, or adding code every time you want to do this can get very cumbersome. The way I handle this is to add an initializer class which can be called from my form at runtime. The behavior of this code is when the user starts typing, the image disappears. If the textbox has no content, then the image shows up. I handle the click event for the picture box to set focus to the textbox to preserve the illusion that it is part of the control, and offset the left in order to allow the | to be displayed showing that the textbox has focus and is ready to receive input.
By writing a controller instead of a user control, I avoid having to propagate all of the events and properties from the textbox through my user control. This class is dependent on System.Windows.Forms, and can either be included directly in your Windows Forms application, or added to a control library which can be called from multiple applications.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
namespace WindowsFormsApplication1
{
class TextBoxIcon
{
public static TextBoxIcon AddIcon(TextBox textbox, Image icon)
{
if (icon != null) {
return new TextBoxIcon(textbox, icon);
} else {
return null;
}
}
private TextBox _TextBox;
private PictureBox _PictureBox;
private TextBoxIcon(TextBox textbox, Image icon) {
this._TextBox = textbox;
this._PictureBox = new PictureBox();
this._PictureBox.BackColor = textbox.BackColor;
this._PictureBox.Image = ScaleImage(icon);
this._TextBox.Parent.Controls.Add(_PictureBox);
this._PictureBox.Location = new Point(textbox.Left + 5, textbox.Top + 2);
this._PictureBox.Size = new Size(textbox.Width - 10, textbox.Height - 4);
this._PictureBox.Anchor = textbox.Anchor;
this._PictureBox.Visible = _TextBox.Visible;
this._PictureBox.BringToFront();
textbox.Resize += TextBox_Resize;
textbox.TextChanged += TextBox_TextChanged;
textbox.Leave += TextBox_Leave;
_PictureBox.Click += PictureBox_Click;
textbox.VisibleChanged += TextBox_VisibleChanged;
}
public static Image ScaleImage(Image img) {
if (img.Height == 16) {
return img;
} else {
return new Bitmap(img, new Size((int)((img.Height / 16.0) * img.Width), 16));
}
}
private void TextBox_Resize(Object sender, EventArgs e) {
_PictureBox.Size = new Size(_TextBox.Width - 10, _TextBox.Height - 4);
}
private void TextBox_VisibleChanged(Object sender, EventArgs e) {
_PictureBox.Visible = _TextBox.Visible;
}
private void ShowPictureBox() {
_PictureBox.Visible = String.IsNullOrEmpty(_TextBox.Text);
}
private void TextBox_TextChanged(Object sender, EventArgs e) {
ShowPictureBox();
}
private void TextBox_Leave(Object sender, EventArgs e) {
ShowPictureBox();
}
public void PictureBox_Click(object sender, EventArgs e) {
_TextBox.Focus();
}
}
}
Here is how the class would be used from the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
TextBoxIcon.AddIcon(txtSearch, Properties.Resources.search);
}
}
}
As long as the text box exists, and has been added to the form, the call can happen at any time.
I suggest to use RichTextBox instead of TextBox .And you can set the BackgroundImage property in the Load of the form :
public Form1()
{
InitializeComponent();
richTextBox1.BackgroundImage = Image.FromFile("image path");
}
And you must add the handling of events like TextChanged and leave to show and Hide the background image

How to disable tab headers in tabcontrol

I have a wizard to make a project, I make us e of a tabcontrol. I have buttons to go to the next tab or the previous. My problem now is that even thought there is validation on the buttons for required field, you can still switch between the tabs by clicking the tab headers. If I disable the tabcontrol, the user can't use whatever is inside the tabs either. Can I solve this?
UPDATE: all the code of the wizard form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorldEstablisher
{
public partial class ProjectWizard : Form
{
#region variables
public Form1 MainForm { get; set; }
#endregion
#region constructor and page load
public ProjectWizard(Form1 form)
{
InitializeComponent();
MainForm = form;
}
private void ProjectWizard_Load(object sender, EventArgs e)
{
}
#endregion
#region navigation
private void nextButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex == 0)//field validation tab 1
{
if (folderLocationTextBox.Text != "" && worldNameTextBox.Text != "")
{
backButton.Visible = true;
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 1)//field validation tab 2
{
if (authorTextBox.Text != "")
{
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 2)
{
finishButton.Visible = true;
}
}
private void backButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex != 0)
{
tabs.SelectedIndex = tabs.SelectedIndex - 1;
if (tabs.SelectedIndex == 0)//Make the back button invisible
{
backButton.Visible = false;
}
if (tabs.SelectedIndex != 2)//Make the finish button invisible
{
finishButton.Visible = false;
}
}
}
private void finishButton_Click(object sender, EventArgs e)
{
World world = new World("test");
MainForm.CurrentWorld = world;
this.Close();
}
#endregion
private void selectFolderButton_Click(object sender, EventArgs e)
{
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
folderLocationTextBox.Text = folderBrowser.SelectedPath;
}
}
}
}
There should be a better way to do this, but in the SelectedIndexChanged event for the TabControl you can set the SelectedTab to be whichever TabPage you want it to be. Track the current TabPage when the user clicks on your navigation buttons.
private TabPage currentTabPage;
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
tabControl1.SelectedTab = currentTabPage;
}
Since you'd be basically eliminating the built-in navigation of a TabControl, I'd rethink your design. Use a stack of Panel objects that you show or hide based on your button navigation mechanism. Don't confuse the user by showing them tab buttons that they can't ever use.

Categories

Resources