.NET / Windows Forms: remember windows size and location - c#

I have a Windows Forms application with a normal window. Now when I close the application and restart it, I want that the main window appears at the same location on my screen with the same size of the moment when it was closed.
Is there an easy way in Windows Forms to remember the screen location and window size (and if possible the window state) or does everything have to be done by hand?

If you add this code to your FormClosing event handler:
if (WindowState == FormWindowState.Maximized)
{
Properties.Settings.Default.Location = RestoreBounds.Location;
Properties.Settings.Default.Size = RestoreBounds.Size;
Properties.Settings.Default.Maximised = true;
Properties.Settings.Default.Minimised = false;
}
else if (WindowState == FormWindowState.Normal)
{
Properties.Settings.Default.Location = Location;
Properties.Settings.Default.Size = Size;
Properties.Settings.Default.Maximised = false;
Properties.Settings.Default.Minimised = false;
}
else
{
Properties.Settings.Default.Location = RestoreBounds.Location;
Properties.Settings.Default.Size = RestoreBounds.Size;
Properties.Settings.Default.Maximised = false;
Properties.Settings.Default.Minimised = true;
}
Properties.Settings.Default.Save();
It will save the current state.
Then add this code to your form's OnLoad handler:
if (Properties.Settings.Default.Maximised)
{
Location = Properties.Settings.Default.Location;
WindowState = FormWindowState.Maximized;
Size = Properties.Settings.Default.Size;
}
else if (Properties.Settings.Default.Minimised)
{
Location = Properties.Settings.Default.Location;
WindowState = FormWindowState.Minimized;
Size = Properties.Settings.Default.Size;
}
else
{
Location = Properties.Settings.Default.Location;
Size = Properties.Settings.Default.Size;
}
It will restore the last state.
It even remembers which monitor in a multi monitor set up the application was maximised to.

You'll need to save the window location and size in your application settings. Here's a good C# article to show you how.
EDIT
You can save pretty much anything you want in the application settings. In the Type column of the settings grid you can browse to any .NET type. WindowState is in System.Windows.Forms and is listed as FormWindowState. There's also a property for FormStartPosition.

I tried a few different methods; this is what ended up working for me.
(In this case - on first launch - the defaults haven't been persisted yet, so the form will use the values set in the designer)
Add the settings to the project (manually - don't rely on visual studio):
Add the following code to your form:
private void Form1_Load(object sender, EventArgs e)
{
this.RestoreWindowPosition();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
this.SaveWindowPosition();
}
private void RestoreWindowPosition()
{
if (Settings.Default.HasSetDefaults)
{
this.WindowState = Settings.Default.WindowState;
this.Location = Settings.Default.Location;
this.Size = Settings.Default.Size;
}
}
private void SaveWindowPosition()
{
Settings.Default.WindowState = this.WindowState;
if (this.WindowState == FormWindowState.Normal)
{
Settings.Default.Location = this.Location;
Settings.Default.Size = this.Size;
}
else
{
Settings.Default.Location = this.RestoreBounds.Location;
Settings.Default.Size = this.RestoreBounds.Size;
}
Settings.Default.HasSetDefaults = true;
Settings.Default.Save();
}

Previous solutions didn't work for me. After playing a while I ended up with following code which:
preserves maximised and normal state
replaces minimised state with default position
in case of screen size changes (detached monitor, remote connection,...) it will not get user into frustrating state with application open outside of screen.
private void MyForm_Load(object sender, EventArgs e)
{
if (Properties.Settings.Default.IsMaximized)
WindowState = FormWindowState.Maximized;
else if (Screen.AllScreens.Any(screen => screen.WorkingArea.IntersectsWith(Properties.Settings.Default.WindowPosition)))
{
StartPosition = FormStartPosition.Manual;
DesktopBounds = Properties.Settings.Default.WindowPosition;
WindowState = FormWindowState.Normal;
}
}
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.IsMaximized = WindowState == FormWindowState.Maximized;
Properties.Settings.Default.WindowPosition = DesktopBounds;
Properties.Settings.Default.Save();
}
user settings:
<userSettings>
<WindowsFormsApplication2.Properties.Settings>
<setting name="WindowPosition" serializeAs="String">
<value>0, 0, -1, -1</value>
</setting>
<setting name="IsMaximized" serializeAs="String">
<value>False</value>
</setting>
</WindowsFormsApplication2.Properties.Settings>
</userSettings>
Note: the WindowsPosition is intentionally wrong, so during first launch application will use default location.
Note that IntersectsWith expects a Rectangle, not a Point. So unlike other answers, this answer is saving the DesktopBounds, not Location, into Properties.Settings.Default.WindowPosition

If you use the fabulous open source library - Jot, you can forget about the tedious .settings files and just do this:
public MainWindow()
{
InitializeComponent();
_stateTracker.Configure(this)
.IdentifyAs("MyMainWindow")
.AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
.RegisterPersistTrigger(nameof(Closed))
.Apply();
}
There's a Nuget package as well, and you can configure pretty much everything about how/when/where data is stored.
Disclaimer: I'm the author, but the library is completely open source (under MIT license).

Matt - to save the WindowState as a user setting, in the Settings Dialog, in the "Type" dropdown, scroll to the bottom and select "Browse".
In the "Select a Type" dialog, expand System.Windows.Forms and you can choose "FormWindowState" as the type.
(sorry, I don't see a button that allows me to comment on the comment...)

If you have more than 1 form you can use something like this...
Add this part all form load void
var AbbA = Program.LoadFormLocationAndSize(this);
this.Location = new Point(AbbA[0], AbbA[1]);
this.Size = new Size(AbbA[2], AbbA[3]);
this.FormClosing += new FormClosingEventHandler(Program.SaveFormLocationAndSize);
Save form location and size to app.config xml
public static void SaveFormLocationAndSize(object sender, FormClosingEventArgs e)
{
Form xForm = sender as Form;
Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
if (ConfigurationManager.AppSettings.AllKeys.Contains(xForm.Name))
config.AppSettings.Settings[xForm.Name].Value = String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height);
else
config.AppSettings.Settings.Add(xForm.Name, String.Format("{0};{1};{2};{3}", xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height));
config.Save(ConfigurationSaveMode.Full);
}
Load form location and size from app.config xml
public static int[] LoadFormLocationAndSize(Form xForm)
{
int[] LocationAndSize = new int[] { xForm.Location.X, xForm.Location.Y, xForm.Size.Width, xForm.Size.Height };
//---//
try
{
Configuration config = ConfigurationManager.OpenExeConfiguration(Application.ExecutablePath);
var AbbA = config.AppSettings.Settings[xForm.Name].Value.Split(';');
//---//
LocationAndSize[0] = Convert.ToInt32(AbbA.GetValue(0));
LocationAndSize[1] = Convert.ToInt32(AbbA.GetValue(1));
LocationAndSize[2] = Convert.ToInt32(AbbA.GetValue(2));
LocationAndSize[3] = Convert.ToInt32(AbbA.GetValue(3));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
//---//
return LocationAndSize;
}

You'll have to manually save the information somewhere. I'd suggest doing so as application settings, storing them in user specific isolated storage.
Once you load up, read the settings then resize/move your form.

My answer is adapted from ChrisF♦'s answer, but I've fixed one thing I didn't like - if the window is minimized at the time of closing, it would appear minimized on next start.
My code handles that case correctly by remembering whether the window was maximized or normal at the time of its minimization, and setting the persistent state accordingly.
Unfortunately, Winforms doesn't expose that information directly, so I needed to override WndProc and store it myself. See Check if currently minimized window was in maximized or normal state at the time of minimization
partial class Form1 : Form
{
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND)
{
int wparam = m.WParam.ToInt32() & 0xfff0;
if (wparam == SC_MAXIMIZE)
LastWindowState = FormWindowState.Maximized;
else if (wparam == SC_RESTORE)
LastWindowState = FormWindowState.Normal;
}
base.WndProc(ref m);
}
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MAXIMIZE = 0xf030;
private const int SC_RESTORE = 0xf120;
private FormWindowState LastWindowState;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (WindowState == FormWindowState.Normal)
{
Properties.Settings.Default.WindowLocation = Location;
Properties.Settings.Default.WindowSize = Size;
}
else
{
Properties.Settings.Default.WindowLocation = RestoreBounds.Location;
Properties.Settings.Default.WindowSize = RestoreBounds.Size;
}
if (WindowState == FormWindowState.Minimized)
{
Properties.Settings.Default.WindowState = LastWindowState;
}
else
{
Properties.Settings.Default.WindowState = WindowState;
}
Properties.Settings.Default.Save();
}
private void Form1_Load(object sender, EventArgs e)
{
if (Properties.Settings.Default.WindowSize != new Size(0, 0))
{
Location = Properties.Settings.Default.WindowLocation;
Size = Properties.Settings.Default.WindowSize;
WindowState = Properties.Settings.Default.WindowState;
}
}

You could also save it in your (let's say) config.xml when you close the form:
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
XmlDocument docConfigPath = new XmlDocument();
docConfigPath.Load(XML_Config_Path);
WriteNode(new string[] { "config", "Size", "Top", Top.ToString() }, docConfigPath);
WriteNode(new string[] { "config", "Size", "Left", Left.ToString() }, docConfigPath);
WriteNode(new string[] { "config", "Size", "Height", Height.ToString() }, docConfigPath);
WriteNode(new string[] { "config", "Size", "Width", Width.ToString() }, docConfigPath);
docConfigPath.Save(XML_Config_Path);
}
public static XmlNode WriteNode(string[] sNode, XmlDocument docConfigPath)
{
int cnt = sNode.Length;
int iNode = 0;
string sNodeNameLast = "/" + sNode[0];
string sNodeName = "";
XmlNode[] xN = new XmlNode[cnt];
for (iNode = 1; iNode < cnt - 1; iNode++)
{
sNodeName = "/" + sNode[iNode];
xN[iNode] = docConfigPath.SelectSingleNode(sNodeNameLast + sNodeName);
if (xN[iNode] == null)
{
xN[iNode] = docConfigPath.CreateNode("element", sNode[iNode], "");
xN[iNode].InnerText = "";
docConfigPath.SelectSingleNode(sNodeNameLast).AppendChild(xN[iNode]);
}
sNodeNameLast += sNodeName;
}
if (sNode[cnt - 1] != "")
xN[iNode - 1].InnerText = sNode[cnt - 1];
return xN[cnt - 2];
}
And the loading is on your:
private void Form1_Load(object sender, EventArgs e)
{
XmlDocument docConfigPath = new XmlDocument();
docConfigPath.Load(XML_Config_Path);
XmlNodeList nodeList = docConfigPath.SelectNodes("config/Size");
Height = ReadNodeInnerTextAsNumber("config/Size/Height", docConfigPath);
Width = ReadNodeInnerTextAsNumber("config/Size/Width", docConfigPath);
Top = ReadNodeInnerTextAsNumber("config/Size/Top", docConfigPath);
Left = ReadNodeInnerTextAsNumber("config/Size/Left", docConfigPath);
}
The config.xml should contain the following:
<?xml version="1.0" encoding="utf-8"?>
<config>
<Size>
<Height>800</Height>
<Width>1400</Width>
<Top>100</Top>
<Left>280</Left>
</Size>
</config>

I've been using this method so far and it's been working great. You don't have to fiddle around with application settings. Instead, it uses serialization to write a settings file to your working directory. I use JSON, but you can use .NET's native XML serialization or any serialization for that matter.
Put these static methods in a common extensions class. Bonus points if you have a common extensions project that you reference by multiple projects:
const string WINDOW_STATE_FILE = "windowstate.json";
public static void SaveWindowState(Form form)
{
var state = new WindowStateInfo
{
WindowLocation = form.Location,
WindowState = form.WindowState
};
File.WriteAllText(WINDOW_STATE_FILE, JsonConvert.SerializeObject(state));
}
public static void LoadWindowState(Form form)
{
if (!File.Exists(WINDOW_STATE_FILE)) return;
var state = JsonConvert.DeserializeObject<WindowStateInfo>(File.ReadAllText(WINDOW_STATE_FILE));
if (state.WindowState.HasValue) form.WindowState = state.WindowState.Value;
if (state.WindowLocation.HasValue) form.Location = state.WindowLocation.Value;
}
public class WindowStateInfo
{
public FormWindowState? WindowState { get; set; }
public Point? WindowLocation { get; set; }
}
You only need to write that code once and never mess with again. Now for the fun part: Put the below code in your form's Load and FormClosing events like so:
private void Form1_Load(object sender, EventArgs e)
{
WinFormsGeneralExtensions.LoadWindowState(this);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
WinFormsGeneralExtensions.SaveWindowState(this);
}
That is all you need to do. The only setup is getting those extensions into a common class. After that, just add two lines of code to your form's code and you're done.
This code will only really work if your WinForm's app has a single form. If it has multiple forms that you want to remember the positions of, you'll need to get creative and do something like this:
public static void SaveWindowState(Form form)
{
var state = new WindowStateInfo
{
WindowLocation = form.Location,
WindowState = form.WindowState
};
File.WriteAllText($"{form.Name} {WINDOW_STATE_FILE}", JsonConvert.SerializeObject(state));
}
I only save location and state, but you can modify this to remember form height and width or anything else. Just make the change once and it will work for any application that calls it.

Related

TabPage title alignment being wrong after drag'n'dropping

I have the class that extends System.Windows.Forms.TabControl and had implemented drag'n'drop mechanism for its TabPages as following:
#region Overriden base methods
protected override void OnDragOver(DragEventArgs e)
{
if (PointedTabPage == null) return;
e.Effect = DragDropEffects.Move;
var dragTab = e.Data.GetData(typeof (ManagedTabPage)) as ManagedTabPage;
if (dragTab == null) return;
int dropIndex = TabPages.IndexOf(PointedTabPage);
int dragIndex = TabPages.IndexOf(dragTab);
if (dragIndex == dropIndex) return;
var modifiedTabPages = new List<ManagedTabPage>(from ManagedTabPage tabPage in TabPages
where TabPages.IndexOf(tabPage) != dragIndex
select TabPages[TabPages.IndexOf(tabPage)] as ManagedTabPage);
modifiedTabPages.Insert(dropIndex, dragTab);
for (byte i = 0; i < TabPages.Count; ++i)
{
var managedTabPage = TabPages[i] as ManagedTabPage;
if (managedTabPage != null && managedTabPage.Uid == modifiedTabPages[i].Uid)
continue;
TabPages[i] = modifiedTabPages[i];
}
SelectedTab = dragTab;
}
protected override void OnMouseDown(MouseEventArgs e)
{
try
{
switch (e.Button)
{
case MouseButtons.Left:
DoDragDrop(PointedTabPage, DragDropEffects.Move);
break;
case MouseButtons.Middle:
CloseTab(PointedTabPage);
break;
}
}
catch (InvalidOperationException)
{
}
finally
{
TabPages.Insert(0, String.Empty);
TabPages.RemoveAt(0);
}
}
#endregion
Nota bene that in the finally clause of OnMouseDown method there are 2 lines for workarounding the
problem: for some reason w/o these lines after drag'n'dropping any of TabPages alignment of their titles is being wrong:
What should I do to correct this behavior without this smelly workaround? Maybe sending some Windows messages could do the trick?
Thanks a lot for any suggestions.
Edit 1. Code of ManagedTabPage is 100% unimportant (it's just extends TabPage with some specific properties).
PointedTabPage is unimportant too, but this is it:
return (from ManagedTabPage tabPage in TabPages
let tabPageIndex = TabPages.IndexOf(tabPage)
where GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position))
select TabPages[tabPageIndex]).Single() as ManagedTabPage;
I'm trying to achieve fully-centered alignment of labels. You see, labels on the screenshot didn't centered horizontally?
I can't do much with the posted code. Let's take a completely different tack and create a tabcontrol that supports dragging a tabpage with the mouse. Add a new class to your project and paste this code:
using System;
using System.Drawing;
using System.Windows.Forms;
class TabControlEx : TabControl {
private Point downPos;
private Form draggingHost;
private Rectangle draggingBounds;
private Point draggingPos;
public TabControlEx() {
this.SetStyle(ControlStyles.UserMouse, true);
}
}
The usage of the variable becomes clear later. First thing we need is to get mouse events from the TabControl so we can see the user trying to drag a tab. That requires turning on the UserMouse control style, it is off by default for controls (like TabControl) that are built-in Windows controls and use their own mouse handling.
Use Build > Build and drag the new control from the top of the toolbox onto a form. Everything still looks and acts like a regular TabControl, but do note that clicking tabs no longer changes the active tab. A side-effect of turning the UserMouse style on. Let's fix that first, paste:
protected override void OnMouseDown(MouseEventArgs e) {
for (int ix = 0; ix < this.TabCount; ++ix) {
if (this.GetTabRect(ix).Contains(e.Location)) {
this.SelectedIndex = ix;
break;
}
}
downPos = e.Location;
base.OnMouseDown(e);
}
We are storing the click location, that's needed later to detect the user dragging the tab. That requires the MouseMove event, we need to start dragging when the user moved the mouse far enough away from the original click position:
protected override void OnMouseMove(MouseEventArgs e) {
if (e.Button == MouseButtons.Left && this.TabCount > 1) {
var delta = SystemInformation.DoubleClickSize;
if (Math.Abs(e.X - downPos.X) >= delta.Width ||
Math.Abs(e.Y - downPos.Y) >= delta.Height) {
startDragging();
}
}
base.OnMouseMove(e);
}
The startDragging method needs to create a toplevel window that can be moved around with the mouse, containing a facsimile of the tab we're dragging around. We'll display it as an owned window, so it is always on top, that has the exact same size as the tab control:
private void startDragging() {
draggingBounds = this.RectangleToScreen(new Rectangle(Point.Empty, this.Size));
draggingHost = createDraggingHost(draggingBounds);
draggingPos = Cursor.Position;
draggingHost.Show(this.FindForm());
}
The createDraggingHost needs to do the heavy lifting and create a window that looks just like the tab. A borderless form we'll move around with the mouse. We'll use the TransparencyKey property to make it look similar to the dragged TabPage with a tab sticking out at the top. And make it look the same by simply letting it display a screenshot of the tabpage:
private Form createDraggingHost(Rectangle bounds) {
var host = new Form() { FormBorderStyle = FormBorderStyle.None, ControlBox = false, AutoScaleMode = AutoScaleMode.None, Bounds = this.draggingBounds, StartPosition = FormStartPosition.Manual };
host.BackColor = host.TransparencyKey = Color.Fuchsia;
var tabRect = this.GetTabRect(this.SelectedIndex);
var tabImage = new Bitmap(bounds.Width, bounds.Height);
using (var gr = Graphics.FromImage(tabImage)) {
gr.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
gr.FillRectangle(Brushes.Fuchsia, new Rectangle(0, 0, tabRect.Left, tabRect.Height));
gr.FillRectangle(Brushes.Fuchsia, new Rectangle(tabRect.Right, 0, bounds.Width - tabRect.Right, tabRect.Height));
}
host.Capture = true;
host.MouseCaptureChanged += host_MouseCaptureChanged;
host.MouseUp += host_MouseCaptureChanged;
host.MouseMove += host_MouseMove;
host.Paint += (s, pe) => pe.Graphics.DrawImage(tabImage, 0, 0);
host.Disposed += delegate { tabImage.Dispose(); };
return host;
}
Note the use of the Capture property, that's how we detect that the user released the mouse button or interrupted the operation by any other means. We'll use the MouseMove event to move the window around:
private void host_MouseMove(object sender, MouseEventArgs e) {
draggingHost.Location = new Point(draggingBounds.Left + Cursor.Position.X - draggingPos.X,
draggingBounds.Top + Cursor.Position.Y - draggingPos.Y);
}
And finally we need to handle the completion of the drag. We'll swap tabs, inserting the dragged tab at the mouse position and destroy the window:
private void host_MouseCaptureChanged(object sender, EventArgs e) {
if (draggingHost.Capture) return;
var pos = this.PointToClient(Cursor.Position);
for (int ix = 0; ix < this.TabCount; ++ix) {
if (this.GetTabRect(ix).Contains(pos)) {
if (ix != this.SelectedIndex) {
var page = this.SelectedTab;
this.TabPages.RemoveAt(this.SelectedIndex);
this.TabPages.Insert(ix, page);
this.SelectedIndex = ix;
}
break;
}
}
draggingHost.Dispose();
draggingHost = null;
}
Looks pretty good.
since you hasn't shared ManagedTabPage code, i used default TabPage control
changes are made in method OnDragOver
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace Demo
{
public class MyTabControl : TabControl
{
public MyTabControl()
{
SizeMode = TabSizeMode.Fixed;
ItemSize = new Size(224, 20);
}
#region Overriden base methods
protected override void OnDragOver(DragEventArgs e)
{
if (DesignMode)
return;
if (PointedTabPage == null) return;
e.Effect = DragDropEffects.Move;
var dragTab = e.Data.GetData(typeof(TabPage)) as TabPage;
if (dragTab == null) return;
int dropIndex = TabPages.IndexOf(PointedTabPage);
int dragIndex = TabPages.IndexOf(dragTab);
if (dragIndex == dropIndex) return;
// change position of tab
TabPages.Remove(dragTab);
TabPages.Insert(dropIndex, dragTab);
SelectedTab = dragTab;
base.OnDragOver(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (DesignMode)
return;
switch (e.Button)
{
case MouseButtons.Left:
DoDragDrop(PointedTabPage, DragDropEffects.Move);
break;
case MouseButtons.Middle:
TabPages.Remove(PointedTabPage);
break;
}
}
#endregion
TabPage PointedTabPage
{
get
{
return TabPages.OfType<TabPage>()
.Where((p, tabPageIndex) => GetTabRect(tabPageIndex).Contains(PointToClient(Cursor.Position)))
.FirstOrDefault();
}
}
}
}

Trying to make a custom print class where the application tells when to print a page

I'm building a series of apps for my place of business so I'm trying to create my own printing class that I can refer to for all of my applications.
The issue is, I'm trying to figure out a way for the application to tell the class when to print the page, and I am unable to find a way to do so.
For example, this is what I have so far:
// Report Variables
private bool bPrinting = false;
private int iPage = 0;
private float fOverflow = 0.00F;
private string sPrintLine = null;
private Font fontTmpFont = null;
private PrintPageEventArgs ppeaEv = null;
private Margins mMargins = new System.Drawing.Printing.Margins(25, 25, 25, 25); // Set wide margins
// Clear the print line
public void LineClear()
{
sPrintLine = null;
}
// Insert a string into the print line at the specified position within the line
public void LineInsert(string _InsertString, int _InsertPosition)
{
if (sPrintLine.Length <= _InsertPosition)
sPrintLine = sPrintLine.PadLeft(_InsertPosition) + _InsertString;
else if (sPrintLine.Length <= (_InsertPosition + _InsertString.Length))
sPrintLine = sPrintLine.Substring(0, _InsertPosition) + _InsertString;
else
sPrintLine = sPrintLine.Substring(0, _InsertPosition) + _InsertString + sPrintLine.Substring(_InsertPosition + _InsertString.Length);
}
// Check to see if the line we're trying to print is at the end of the page
public bool AtEndOfPage()
{
return AtEndOfPage(new Font("Courier", 10));
}
public bool AtEndOfPage(Font _Font)
{
if ((fOverflow + _Font.GetHeight(ppeaEv.Graphics)) > ppeaEv.MarginBounds.Height)
return true;
else
return false;
}
// Attempt to print the line
public void LinePrint()
{
LinePrint(null, null);
}
public void LinePrint(Font _Font)
{
LinePrint(_Font, null);
}
public void LinePrint(Font _Font, Brush _Color)
{
if (_Font == null)
_Font = new Font("Courier", 10);
if (_Color == null)
_Color = Brushes.Black;
ppeaEv.Graphics.DrawString(sPrintLine, _Font, _Color,
ppeaEv.MarginBounds.Left, ppeaEv.MarginBounds.Top + fOverflow,
new StringFormat()); // 'Draw' line on page
fOverflow += _Font.GetHeight(ppeaEv.Graphics);
}
// We are done with the report, tell the Print Service to finish up
public void EndReport()
{
ppeaEv.HasMorePages = false;
}
// This is what gets called when the user clicks on 'Print'
private void Print_Click(object sender, EventArgs e)
{
PrintDocument printDocument = new PrintDocument();
printDocument.DefaultPageSettings.Margins = mMargins; // Set margins for pages
printDocument.DefaultPageSettings.Landscape = false; // Set Portrait mode
printDocument.BeginPrint += new PrintEventHandler(printDocument_BeginPrint);
printDocument.EndPrint += new PrintEventHandler(printDocument_EndPrint);
PrintDialog printDialog = new PrintDialog();
printDialog.Document = printDocument; // Set the Document for this print dialog
printDialog.AllowSomePages = true; // Allow the user to select only some pages to print
printDialog.ShowHelp = true; // Allow help button
DialogResult result = printDialog.ShowDialog();
if (result == DialogResult.OK)
{
printDocument.Print(); // Raises PrintPage event
}
printDocument.Dispose();
printDialog.Dispose();
}
void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
iPage = 0;
fOverflow = 0.00F;
}
void printDocument_EndPrint(object sender, PrintEventArgs e)
{
}
As you may be able to tell, I'm trying to fill the page externally to the class, and then have the app tell the class when to print the page when "AtEndOfPage"
I know about "printDocument.PrintPage", but that doesn't seem to be what I need; It builds the page internal to the method and then prints it. I'm not going to be building the print page within that method.
I also am trying to allow for multiple fonts on a single page.
Is there a way to do this?
Thank you all in advance,
Robert

Manage windows inside a panel with a 'Windows'-like menu

I've a panel.
I add WinForms inside it. The WinForms added have the TopLevel and Visible properties set to FALSE and TRUE.
I can do a panel.SetChildIndex(WinForm1,0) to bring WinForm1 to front.
What I've not managed to do is keep a track of the actual ChildIndex of the panel.
The idea is to have buttons that opens forms inside the panel, and that when the panel opens a new button is added in a Windows menu.
Something like when many files are open on a VS Project, you can go to Window menu and select one. Also, if you change the active page by clicking the page, the Window menu auto-updates and checks the actual active page.
I want to do this, but with a panel container. I've managed to get done everithing, but not the the Window menu auto-updates and checks the actual active page part.
Isn't there an event fired when BringToFront() or SetChildIndex(form, index) are called? Any event when I click another form that's inside the panel and it becomes the "active one"? Or some property of the panel that I can keep track of that changes when active form changes?
It is taken from here
When Control's ZOrder is chaged layout operation is always performed
in control's container control.
When I subscribed to container's Layout event and called
BringToFront() it showed me Control that changed its
ZOrder(LayoutEventArgs.AffectedControl) and changed property
(LayoutEventArgs.AffectedProperty).
Found that when a form inside a panel is closed, the Controls property of the panel gets reindexed, where the index zero is the form that gets the new focus. Now that I've a way to check the form that's in front when I close another one, windows administration in panels is done.
Going to put the source code, maybe it can help someone :)
Please note that I'm using a RadRibbonForm, a standard panel, and RadForms inside the panel. Rad's are from Telerik. Some things should change to make this work on standardWinForms, but the changes are minimal.
Also, I'm not using a menu that shows the forms, I'm using RadButtonElement's in a page of the ribbon menu instead.
AddRadFormWindow must be called to put a window and manage it automatically.
Example of adding a window:
AddRadFormWindow(typeof (MyRadForm))
Now, the source. It must be inside the code of the RadRibbonForm's class.
public static class ExtensionsRadForm
{
[DllImport("user32.dll")]
private static extern int ShowWindow(IntPtr hWnd, uint msg);
public static void Deminimize(this RadForm form)
{
if (form.WindowState == FormWindowState.Minimized)
ShowWindow(form.Handle, 9);
}
}
private void RefreshButtonsChecks(string windowName)
{
if (windowName != null)
{
principalPanel.Controls[windowName].BringToFront();
}
if (principalPanel.Controls.Count > 0)
{
if (principalPanel.Controls.Cast<RadForm>().Any(radForm => radForm.WindowState != FormWindowState.Minimized))
{
foreach (RadItem item in radRibbonBarGroupOpenWindows.Items)
{
var buttonBorder = ((RadButtonElement) item).BorderElement;
if (item.Name == panelPrincipal.Controls[0].Name + "Button")
{
buttonBorder.ForeColor = Color.LimeGreen;
buttonBorder.BottomColor = Color.LimeGreen;
buttonBorder.TopColor = Color.LimeGreen;
buttonBorder.LeftColor = Color.LimeGreen;
buttonBorder.RightColor = Color.LimeGreen;
principalPanel.Controls[0].Focus();
}
else
{
buttonBorder.ForeColor = Color.Transparent;
buttonBorder.BottomColor = Color.Transparent;
buttonBorder.TopColor = Color.Transparent;
buttonBorder.LeftColor = Color.Transparent;
buttonBorder.RightColor = Color.Transparent;
}
}
}
else
{
foreach (RadItem item in radRibbonBarGroupAbiertas.Items)
{
var buttonBorder = ((RadButtonElement)item).BorderElement;
buttonBorder.ForeColor = Color.Transparent;
buttonBorder.BottomColor = Color.Transparent;
buttonBorder.TopColor = Color.Transparent;
buttonBorder.LeftColor = Color.Transparent;
buttonBorder.RightColor = Color.Transparent;
}
}
}
}
private void PrincipalPanelLayout(object sender, LayoutEventArgs e)
{
RefreshButtonsChecks(null);
}
private void RadButtonElementCloseAllWindowsClick(object sender, EventArgs e)
{
int limitButtons = radRibbonBarGroupOpenWindows.Items.Count;
for (int index = 0; index < limitButtons; index++)
{
RadItem radItem = radRibbonBarGroupOpenWindows.Items[0];
radItem.Dispose();
}
int limitControls = principalPanel.Controls.Count;
for (int index = 0; index < limitControls; index++)
{
Control control = principalPanel.Controls[0];
control.Dispose();
}
Update();
GC.Collect();
}
private void AddRadFormWindow(Type windowToAdd)
{
if (!principalPanel.Controls.ContainsKey(windowToAdd.Name))
{
var window = (RadForm) Activator.CreateInstance(windowToAdd);
window.TopLevel = false;
window.Visible = true;
window.FormClosing += (method, args) =>
{
radRibbonBarGroupOpenWindows.Items[window.Name + "Button"].Dispose();
GC.Collect();
};
window.Enter += (method, args) => RefreshButtonsChecks(window.Name);
var closeMenuItem = new RadMenuItem("Close");
closeMenuItem.MouseDown += (method, args) =>
{
panelPrincipal.Controls[window.Name].Dispose();
radRibbonBarGroupOpenWindows.Items[window.Name + "Button"].Dispose();
};
var contextMenu = new RadContextMenu();
contextMenu.Items.Add(closeMenuItem);
var button = new RadButtonElement(window.Text) {Name = window.Name + "Button"};
button.MouseDown += (method, args) =>
{
switch (args.Button)
{
case MouseButtons.Left:
if (((RadForm) principalPanel.Controls[window.Name]).WindowState ==
FormWindowState.Minimized)
((RadForm) principalPanel.Controls[window.Name]).Deminimize();
principalPanel.Controls[window.Name].BringToFront();
principalPanel.Controls[window.Name].Focus();
break;
case MouseButtons.Right:
contextMenu.Show(MousePosition);
break;
}
};
radRibbonBarGroupOpenWindows.Items.Add(button);
principalPanel.Controls.Add(window);
principalPanel.Controls[window.Name].BringToFront();
principalPanel.Controls[window.Name].Focus();
}
principalPanel.Controls[windowToAdd.Name].BringToFront();
principalPanel.Controls[windowToAdd.Name].Focus();
Update();
GC.Collect();
}
public Constructor()
{
panelPrincipal.Layout += PanelPrincipalLayout;
}

create custom object (combination of two objects)

hello creating a custom object may be a widely published topic, but my lack of coding skills proves problematic in actually implementing what i'm trying to do.
in a nutshell i'm adding controls at runtime in a flowpanelLayout. right now it's just listboxes, that code is all working fine. i would like a way to label the listboxes that are getting added, i can't think of a better way to do this than to use a text label. i was thinking it would be slick to create some sort of custom control (if possible) which is a listbox and a textlabel like one above the other or something. this way i can add the new custom control in my current code and assign the listbox attributes and label text, etc all in one motion.
this is what i was thinking, maybe there's even a better way to do this.
my current listview creation code:
public void addListView()
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
newListView.DragDrop += listView_DragDrop;
newListView.DragEnter += listView_DragEnter;
newListView.MouseDoubleClick += listView_MouseDoubleClick;
newListView.MouseDown += listView_MouseDown;
newListView.DragOver += listView_DragOver;
newListView.Width = 200;
newListView.Height = 200;
newListView.View = View.Tile;
newListView.MultiSelect = false;
flowPanel.Controls.Add(newListView);
numWO++;
numberofWOLabel.Text = numWO.ToString();
}
maybe the actual best answer is simply to also add a textlabel here and define some set coordinates to put it. let me know what you think.
if a custom control is the way to go, please provide some resource or example for me - i'd appreciate it.
Here is a custom user control that can do that:
You just need to set TitleLabelText to set the title.
[Category("Custom User Controls")]
public class ListBoxWithTitle : ListBox
{
private Label titleLabel;
public ListBoxWithTitle()
{
this.SizeChanged +=new EventHandler(SizeSet);
this.LocationChanged +=new EventHandler(LocationSet);
this.ParentChanged += new EventHandler(ParentSet);
}
public string TitleLabelText
{
get;
set;
}
//Ensures the Size, Location and Parent have been set before adding text
bool isSizeSet = false;
bool isLocationSet = false;
bool isParentSet = false;
private void SizeSet(object sender, EventArgs e)
{
isSizeSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void LocationSet(object sender, EventArgs e)
{
isLocationSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void ParentSet(object sender, EventArgs e)
{
isParentSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void PositionLabel()
{
//Initializes text label
titleLabel = new Label();
//Positions the text 10 pixels below the Listbox.
titleLabel.Location = new Point(this.Location.X, this.Location.Y + this.Size.Height + 10);
titleLabel.AutoSize = true;
titleLabel.Text = TitleLabelText;
this.Parent.Controls.Add(titleLabel);
}
}
Example use:
public Form1()
{
InitializeComponent();
ListBoxWithTitle newitem = new ListBoxWithTitle();
newitem.Size = new Size(200, 200);
newitem.Location = new Point(20, 20);
newitem.TitleLabelText = "Test";
this.Controls.Add(newitem);
}

Displaying tooltip over a disabled control

I'm trying to display a tooltip when mouse hovers over a disabled control. Since a disabled control does not handle any events, I have to do that in the parent form. I chose to do this by handling the MouseMove event in the parent form. Here's the code that does the job:
void Form1_MouseMove(object sender, MouseEventArgs e)
{
m_toolTips.SetToolTip(this, "testing tooltip on " + DateTime.Now.ToString());
string tipText = this.m_toolTips.GetToolTip(this);
if ((tipText != null) && (tipText.Length > 0))
{
Point clientLoc = this.PointToClient(Cursor.Position);
Control child = this.GetChildAtPoint(clientLoc);
if (child != null && child.Enabled == false)
{
m_toolTips.ToolTipTitle = "MouseHover On Disabled Control";
m_toolTips.Show(tipText, this, 10000);
}
else
{
m_toolTips.ToolTipTitle = "MouseHover Triggerd";
m_toolTips.Show(tipText, this, 3000);
}
}
}
The code does handles the tooltip display for the disabled control. The problem is that when mouse hovers over a disabled control, the tooltip keeps closing and redisplay again. From the display time I added in the tooltip, when mouse is above the parent form, the MouseMove event gets called roughly every 3 seconds, so the tooltip gets updated every 3 seconds. But when mouse is over a disabled control, the tooltip refreshes every 1 second. Also, when tooltip refreshes above form, only the text gets updated with a brief flash. But when tooltip refreshes above a disabled control, the tooltip windows closes as if mouse is moving into a enabled control and the tooltip is supposed to be closed. but then the tooltip reappears right away.
Can someone tell me why is this? Thanks.
you can show the tooltip only once when mouse hits the disbled control and then hide it when mouse leaves it. Pls, take a look at the code below, it should be showing a tooltip message for all the disabled controls on the form
private ToolTip _toolTip = new ToolTip();
private Control _currentToolTipControl = null;
public Form1()
{
InitializeComponent();
_toolTip.SetToolTip(this.button1, "My button1");
_toolTip.SetToolTip(this.button2, "My button2");
_toolTip.SetToolTip(this.textBox1, "My text box");
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Control control = GetChildAtPoint(e.Location);
if (control != null)
{
if (!control.Enabled && _currentToolTipControl == null)
{
string toolTipString = _toolTip.GetToolTip(control);
// trigger the tooltip with no delay and some basic positioning just to give you an idea
_toolTip.Show(toolTipString, control, control.Width/2, control.Height/2);
_currentToolTipControl = control;
}
}
else
{
if (_currentToolTipControl != null) _toolTip.Hide(_currentToolTipControl);
_currentToolTipControl = null;
}
}
hope this helps, regards
The answer turned out to be a bit simpler, but needed to be applied at all times.
void OrderSummaryDetails_MouseMove(object sender, MouseEventArgs e)
{
Control control = GetChildAtPoint(e.Location);
if (control != null)
{
string toolTipString = mFormTips.GetToolTip(control);
this.mFormTips.ShowAlways = true;
// trigger the tooltip with no delay and some basic positioning just to give you an idea
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height / 2);
}
}
In case of TextBox control, making it as readonly solved the issue.
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
}
Since no one ever pointed this out, this works for any control that exposes ToolTipService:
ToolTipService.ShowOnDisabled="True"
As in this example:
<Button Content="OK"
ToolTipService.ShowOnDisabled="True" />
/*
Inspired by the suggestions above in this post, i wrapped it up as an extended ToolTip control specially works for disabled control.
// Reference example
var td = new ToolTipOnDisabledControl();
this.checkEdit3.Enabled = false;
td.SetTooltip(this.checkEdit3, "tooltip for disabled 3333333333333");
*/
using System;
using System.Windows.Forms;
namespace TestApp1
{
public class ToolTipOnDisabledControl
{
#region Fields and Properties
private Control enabledParentControl;
private bool isShown;
public Control TargetControl { get; private set; }
public string TooltipText { get; private set; }
public ToolTip ToolTip { get; }
#endregion
#region Public Methods
public ToolTipOnDisabledControl()
{
this.ToolTip = new ToolTip();
}
public void SetToolTip(Control targetControl, string tooltipText = null)
{
this.TargetControl = targetControl;
if (string.IsNullOrEmpty(tooltipText))
{
this.TooltipText = this.ToolTip.GetToolTip(targetControl);
}
else
{
this.TooltipText = tooltipText;
}
if (targetControl.Enabled)
{
this.enabledParentControl = null;
this.isShown = false;
this.ToolTip.SetToolTip(this.TargetControl, this.TooltipText);
return;
}
this.enabledParentControl = targetControl.Parent;
while (!this.enabledParentControl.Enabled && this.enabledParentControl.Parent != null)
{
this.enabledParentControl = this.enabledParentControl.Parent;
}
if (!this.enabledParentControl.Enabled)
{
throw new Exception("Failed to set tool tip because failed to find an enabled parent control.");
}
this.enabledParentControl.MouseMove += this.EnabledParentControl_MouseMove;
this.TargetControl.EnabledChanged += this.TargetControl_EnabledChanged;
}
public void Reset()
{
if (this.TargetControl != null)
{
this.ToolTip.Hide(this.TargetControl);
this.TargetControl.EnabledChanged -= this.TargetControl_EnabledChanged;
this.TargetControl = null;
}
if (this.enabledParentControl != null)
{
this.enabledParentControl.MouseMove -= this.EnabledParentControl_MouseMove;
this.enabledParentControl = null;
}
this.isShown = false;
}
#endregion
#region Private Methods
private void EnabledParentControl_MouseMove(object sender, MouseEventArgs e)
{
if (e.Location.X >= this.TargetControl.Left &&
e.Location.X <= this.TargetControl.Right &&
e.Location.Y >= this.TargetControl.Top &&
e.Location.Y <= this.TargetControl.Bottom)
{
if (!this.isShown)
{
this.ToolTip.Show(this.TooltipText, this.TargetControl, this.TargetControl.Width / 2, this.TargetControl.Height / 2, this.ToolTip.AutoPopDelay);
this.isShown = true;
}
}
else
{
this.ToolTip.Hide(this.TargetControl);
this.isShown = false;
}
}
private void TargetControl_EnabledChanged(object sender, EventArgs e)
{
if (TargetControl.Enabled)
{
TargetControl.EnabledChanged -= TargetControl_EnabledChanged;
enabledParentControl.MouseMove -= EnabledParentControl_MouseMove;
}
}
#endregion
}
}
Here is how I solved this problem
I have an application that generates code automatically for a PIC32MX.
The application has 3 Tab Pages text = PWM, ADC and UART.
On each Tab Page I have one Check Box text = RPA0
The intention is, when a peripheral uses RPA0, the other peripheral is prevented
from using that pin, by disabling it on the other pages, and a tooltip text must pop up
on the disabled check boxs saying (example "Used by PWM")
what peripheral is using that pin.
The problem is that the tooltip text won't pop up on a disabled check box.
To solve the problem, I just removed the text of the check boxes and inserted labels with the text the check box should have.
When a check box is checked, the other check boxes are disabled and the label next to it takes a tool tip text.
As the label is enabled, the tooltip text pops up, even on a disabled check box.
Double the work, half the complexity.
Here is the code and the designer for C# 2010
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void cb_ADC_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_UART_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_PWM_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_PWM_RPA0, lbl_UART_RPA0, "ADC");
}
private void cb_PWM_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_UART_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_ADC_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_ADC_RPA0, lbl_UART_RPA0, "PWM");
}
private void cb_UART_RPA0_CheckedChanged(object sender, EventArgs e)
{
/* Disable pin on other peripherals */
cb_ADC_RPA0.Enabled = !((CheckBox)sender).Checked;
cb_PWM_RPA0.Enabled = !((CheckBox)sender).Checked;
SetTootTip((CheckBox)sender, lbl_ADC_RPA0, lbl_PWM_RPA0, "UART");
}
void SetTootTip(CheckBox sender, Label lbl1, Label lbl2, string text)
{
/* Update tooltip on the other labels */
if (sender.Checked)
{
toolTip1.SetToolTip(lbl1, "Used by " + text);
toolTip1.SetToolTip(lbl2, "Used by " + text);
}
else
{
toolTip1.SetToolTip(lbl1, "");
toolTip1.SetToolTip(lbl2, "");
}
}
}
}
namespace WindowsFormsApplication1
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tpPWM = new System.Windows.Forms.TabPage();
this.tpUART = new System.Windows.Forms.TabPage();
this.tpADC = new System.Windows.Forms.TabPage();
this.cb_PWM_RPA0 = new System.Windows.Forms.CheckBox();
this.cb_ADC_RPA0 = new System.Windows.Forms.CheckBox();
this.lbl_PWM_RPA0 = new System.Windows.Forms.Label();
this.lbl_ADC_RPA0 = new System.Windows.Forms.Label();
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
this.lbl_UART_RPA0 = new System.Windows.Forms.Label();
this.cb_UART_RPA0 = new System.Windows.Forms.CheckBox();
this.tabControl1.SuspendLayout();
this.tpPWM.SuspendLayout();
this.tpUART.SuspendLayout();
this.tpADC.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tpPWM);
this.tabControl1.Controls.Add(this.tpUART);
this.tabControl1.Controls.Add(this.tpADC);
this.tabControl1.Location = new System.Drawing.Point(12, 12);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(629, 296);
this.tabControl1.TabIndex = 0;
//
// tpPWM
//
this.tpPWM.Controls.Add(this.lbl_PWM_RPA0);
this.tpPWM.Controls.Add(this.cb_PWM_RPA0);
this.tpPWM.Location = new System.Drawing.Point(4, 22);
this.tpPWM.Name = "tpPWM";
this.tpPWM.Padding = new System.Windows.Forms.Padding(3);
this.tpPWM.Size = new System.Drawing.Size(621, 270);
this.tpPWM.TabIndex = 0;
this.tpPWM.Text = "PWM";
this.tpPWM.UseVisualStyleBackColor = true;
//
// tpUART
//
this.tpUART.Controls.Add(this.cb_UART_RPA0);
this.tpUART.Controls.Add(this.lbl_UART_RPA0);
this.tpUART.Location = new System.Drawing.Point(4, 22);
this.tpUART.Name = "tpUART";
this.tpUART.Padding = new System.Windows.Forms.Padding(3);
this.tpUART.Size = new System.Drawing.Size(621, 270);
this.tpUART.TabIndex = 1;
this.tpUART.Text = "UART";
this.tpUART.UseVisualStyleBackColor = true;
//
// tpADC
//
this.tpADC.Controls.Add(this.lbl_ADC_RPA0);
this.tpADC.Controls.Add(this.cb_ADC_RPA0);
this.tpADC.Location = new System.Drawing.Point(4, 22);
this.tpADC.Name = "tpADC";
this.tpADC.Padding = new System.Windows.Forms.Padding(3);
this.tpADC.Size = new System.Drawing.Size(621, 270);
this.tpADC.TabIndex = 2;
this.tpADC.Text = "ADC";
this.tpADC.UseVisualStyleBackColor = true;
//
// cb_PWM_RPA0
//
this.cb_PWM_RPA0.AutoSize = true;
this.cb_PWM_RPA0.Location = new System.Drawing.Point(17, 65);
this.cb_PWM_RPA0.Name = "cb_PWM_RPA0";
this.cb_PWM_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_PWM_RPA0.TabIndex = 0;
this.cb_PWM_RPA0.UseVisualStyleBackColor = true;
this.cb_PWM_RPA0.CheckedChanged += new System.EventHandler(this.cb_PWM_RPA0_CheckedChanged);
//
// cb_ADC_RPA0
//
this.cb_ADC_RPA0.AutoSize = true;
this.cb_ADC_RPA0.Location = new System.Drawing.Point(17, 65);
this.cb_ADC_RPA0.Name = "cb_ADC_RPA0";
this.cb_ADC_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_ADC_RPA0.TabIndex = 1;
this.cb_ADC_RPA0.UseVisualStyleBackColor = true;
this.cb_ADC_RPA0.CheckedChanged += new System.EventHandler(this.cb_ADC_RPA0_CheckedChanged);
//
// lbl_PWM_RPA0
//
this.lbl_PWM_RPA0.AutoSize = true;
this.lbl_PWM_RPA0.Location = new System.Drawing.Point(38, 65);
this.lbl_PWM_RPA0.Name = "lbl_PWM_RPA0";
this.lbl_PWM_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_PWM_RPA0.TabIndex = 1;
this.lbl_PWM_RPA0.Text = "RPA0";
//
// lbl_ADC_RPA0
//
this.lbl_ADC_RPA0.AutoSize = true;
this.lbl_ADC_RPA0.Location = new System.Drawing.Point(38, 66);
this.lbl_ADC_RPA0.Name = "lbl_ADC_RPA0";
this.lbl_ADC_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_ADC_RPA0.TabIndex = 2;
this.lbl_ADC_RPA0.Text = "RPA0";
//
// lbl_UART_RPA0
//
this.lbl_UART_RPA0.AutoSize = true;
this.lbl_UART_RPA0.Location = new System.Drawing.Point(37, 65);
this.lbl_UART_RPA0.Name = "lbl_UART_RPA0";
this.lbl_UART_RPA0.Size = new System.Drawing.Size(35, 13);
this.lbl_UART_RPA0.TabIndex = 4;
this.lbl_UART_RPA0.Text = "RPA0";
//
// cb_UART_RPA0
//
this.cb_UART_RPA0.AutoSize = true;
this.cb_UART_RPA0.Location = new System.Drawing.Point(16, 65);
this.cb_UART_RPA0.Name = "cb_UART_RPA0";
this.cb_UART_RPA0.Size = new System.Drawing.Size(15, 14);
this.cb_UART_RPA0.TabIndex = 5;
this.cb_UART_RPA0.UseVisualStyleBackColor = true;
this.cb_UART_RPA0.CheckedChanged += new System.EventHandler(this.cb_UART_RPA0_CheckedChanged);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(758, 429);
this.Controls.Add(this.tabControl1);
this.Name = "Form1";
this.Text = "Form1";
this.Load += new System.EventHandler(this.Form1_Load);
this.tabControl1.ResumeLayout(false);
this.tpPWM.ResumeLayout(false);
this.tpPWM.PerformLayout();
this.tpUART.ResumeLayout(false);
this.tpUART.PerformLayout();
this.tpADC.ResumeLayout(false);
this.tpADC.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tpPWM;
private System.Windows.Forms.Label lbl_PWM_RPA0;
private System.Windows.Forms.CheckBox cb_PWM_RPA0;
private System.Windows.Forms.TabPage tpUART;
private System.Windows.Forms.TabPage tpADC;
private System.Windows.Forms.Label lbl_ADC_RPA0;
private System.Windows.Forms.CheckBox cb_ADC_RPA0;
private System.Windows.Forms.ToolTip toolTip1;
private System.Windows.Forms.CheckBox cb_UART_RPA0;
private System.Windows.Forms.Label lbl_UART_RPA0;
}
}
I created a new UserControl which only contains a button.
public partial class TooltipButton : UserControl
{
public TooltipButton()
{
InitializeComponent();
}
public new bool Enabled
{
get { return button.Enabled; }
set { button.Enabled = value; }
}
[Category("Appearance")]
[Description("The text displayed by the button.")]
[EditorBrowsable(EditorBrowsableState.Always)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Bindable(true)]
public override string Text
{
get { return button.Text; }
set { button.Text = value; }
}
[Category("Action")]
[Description("Occurs when the button is clicked.")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public new event EventHandler Click;
private void button_Click(object sender, EventArgs e)
{
// Bubble event up to parent
Click?.Invoke(this, e);
}
}
I found Serge_Yubenko's code worked on disabled buttons , but in order to stop the flashing make sure the tooltip pops up away from the button - just don't position it half way down the control but do this:
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height);
instead of
mFormTips.Show(toolTipString, control, control.Width / 2, control.Height / 2);
This seems to follow the usual tooltip placement too...
So, I came across this post in my efforts to do the same thing, being the top result on Google. I had already considered the mouse move event and while the answers here did help, they didn't provide me with exactly what I wanted - that being a perfect recreation of the original show tooltip event.
The problem I discovered was this: For whatever reason in the API, ToolTip.Show turns the Mouse Move Event into effectively a Mouse Hover Event. Which is why the tooltip keeps flashing.
The workaround as suggested was to keep the tooltip on always show, or to display the tooltip away from the control, but that wouldn't be a faithful recreation, from the show to the timed fade. The answer would suggest that a block to prevent further execution of the code is needed - the reality was 2 blocks in the event code (One of which has no earthly reason existing and yet without it a timed event fires twice ***), a double delclaration of the control location, one inside the event, one class wide, and another class wide to check if the mouse is over a control, a class wide timer, and a Mouse Leave event to clean up due to too fast mouse movement away from the panel housing the control.
As you will see there are two events on the timer, both functions for them are in the event code as they need to reference variables get/set in the code. They can be moved out, but would then need class wide declarations on the variables, and they cause no harm where they are. FYI: "ToolTips" in the code is referencing the ToolTip control I have on the form.
*** Just to expand. If you look at the code you'll see that IsTipReset could be replaced with IsShown - after all they end up at the same value as each other. The reason for IsTipRest is this: If IsShown is used then while moving the mouse inside the control while the tootip is showing will cause a slight hiccup when the tooltip fades and very very very briefly another tooltip will popup. Using IsTipReset stops that. I have no idea why and maybe someone will spot it because I sure can't! Lol.
This is my first post here, and I realise it is an old thread, but I just wanted to share the fruits of my labour. As I said, my goal was a faithful recreation of tooltip and I think I achieved it. Enjoy!
using Timer = System.Windows.Forms.Timer;
private readonly Timer MouseTimer = new();
private Control? Ctrl;
private bool IsControl = false;
private void TopMenuMouseMove (object sender, MouseEventArgs e) {
Panel Pnl = (Panel)sender;
Control Area = Pnl.GetChildAtPoint (e.Location);
bool IsShown = false;
bool IsTipReset = false;
if (Area != null && Area.Enabled == false && Area.Visible == true) {
Ctrl = Pnl.GetChildAtPoint (e.Location);
Point Position = e.Location;
if (IsControl) { IsShown = true; } else if (!IsControl) { IsControl = true; IsShown = false; }
if (!IsShown) {
MouseTimer.Interval = ToolTips.InitialDelay;
MouseTimer.Tick += new EventHandler (TimerToolTipShow!);
MouseTimer.Start ();
}
void TimerToolTipShow (object sender, EventArgs e) {
if (!IsTipReset) {
MouseTimer.Dispose ();
string Txt = ToolTips.GetToolTip (Ctrl) + " (Disabled)";
Position.Offset (-Ctrl.Left, 16);
ToolTips.Show (Txt, Ctrl, Position);
MouseTimer.Interval = ToolTips.AutoPopDelay;
MouseTimer.Tick += new EventHandler (TimerToolTipReset!);
MouseTimer.Start ();
IsShown = true;
IsTipReset = true;
}
}
void TimerToolTipReset (object sender, EventArgs e) {
if (IsShown) {
MouseTimer.Dispose ();
IsShown = false;
ToolTips.Hide (Ctrl);
}
}
}
else if (Area == null) {
if (Ctrl != null) {
MouseTimer.Dispose ();
IsShown = false;
IsControl = false;
ToolTips.Hide (Ctrl);
Ctrl = null;
}
}
}
private void TopMenuMouseLeave (object sender, EventArgs e) {
if (Ctrl != null) {
MouseTimer.Dispose ();
IsControl = false;
ToolTips.Hide (Ctrl);
Ctrl = null;
}
}

Categories