Advice on implementation for button which supports opacity - c#

I didn't find a way in C# for creating a button which will support opacity - instead of just appearing when show method is called, to have the ability to slowly fade it into view.
I created my own button and I would like to know what you think of the implementation.
Basically, I created a Windows Form, which supports opacity property and handled all the corner cases regarding "adding" a form to another form, specifically:
- location changed event
- the owner form lost focus
The form consist of a label, which represents the button's text and that's all.
The form constructor gets the text for the label, the desired size of the button and the speed (based on an Enum) for the button appear.
In the form load method the label is being located in the middle of the button
when the label or the form itself clicked a simple graphic is performed and an event is raised to whoever catches it.
My Code:
Created a Form - named buttonForm
Constructor
InitializeComponent();
this.Owner = owner;
_buttonText = buttonText;
_buttonSize = buttonSize;
_usedSpeedOpacity = SelectSpeedOpacity(showSpeed);
A method to illustrate click command - being called when the user clicks on the form and on the label itself:
this.Location = new Point(this.Location.X + 1, this.Location.Y);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
Thread.Sleep(50);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Location = new Point(this.Location.X - 1, this.Location.Y);
if (Button_Clicked != null)
{
Button_Clicked();
}
form load - locating the label in the middle of the control etc
labelButtonText.Text = _buttonText;
this.Size = _buttonSize;
double remainning = this.Width - labelButtonText.Size.Width;
Point labelNewLocation = new Point(
(int)(remainning / 2),
(int)(this.Height / 2 - this.Font.Height / 2));
labelButtonText.Location = labelNewLocation;
FadeShow (and FadeHide the same)
int tempCounter = 0;
Opacity = 0;
Show();
while (tempCounter <= 1000)
{
if (Opacity == 1.0)
{
break;
}
if (tempCounter % 10 == 0)
{
Opacity += _usedSpeedOpacity;
}
Refresh();
tempCounter++;
}
this.Visible = true;
this.BringToFront();
Update location method so when the parent form moves i call this method
this.Location = new Point(
this.Location.X - (ParentFormLocation.X - newLocation.X),
this.Location.Y - (ParentFormLocation.Y - newLocation.Y));
ParentFormLocation = newLocation;
An event of my button_click
Thanks in advance,
Oz.

I did something similar with a windows form that changed its height. The way I did it was to implement a timer within the code so every half a second the opacity changed.
While the below is not a 100% correct answer without your code its difficult to show you.
void timer_Tick(object sender, EventArgs e)
{
if (btnName.Opacity < 100)
{
btnName.Opacity++;
timer2.Stop();
timer2.Interval = 5000;
timer2.Start();
} else {
timer2.Stop();
}
}

Related

How to make label reappear as soon as it's going out of panel width

I want to make a moving label seem nicer and smoother than just reappearing the whole thing to the left after it has all gone out of panel width .For example label 'Hello' , as soon as 'lo' goes out of bounds in the right I want it to reappear on the left. Is there any possible solution to this ?
Here's the code I have for the label now .
private void timer2_Tick(object sender, EventArgs e)
{
label5.Location = new Point(label5.Location.X + 3, label5.Location.Y);
if (label5.Location.X > this.Width)
{
label5.Location = new Point(0 - label5.Width, label5.Location.Y);
}
}
Try this, using a Label (here, named lblMarquee and a System.Windows.Forms.Timer).
The scrolling time is regulated by both the Timer.Interval and a float Field (marqueeStep).
The Timer.Tick event just calls lblMarquee.Invalidate(), causing the Label control to repaint itself.
When the scrolling text, in relation to its current position, goes beyond the limits of the Label.ClientRectangle, the section of the text which is not visible anymore is painted at start of the Label.ClientArea:
System.Windows.Forms.Timer marqueeTimer = new System.Windows.Forms.Timer();
string marqueeText = string.Empty;
float marqueePosition = 0f;
float marqueeStep = 4f;
private void form1_Load(object sender, EventArgs e)
{
marqueeText = lblMarquee.Text;
lblMarquee.Text = string.Empty;
marqueeTimer.Tick += (s, ev) => { this.lblMarquee.Invalidate(); };
marqueeTimer.Interval = 100;
marqueeTimer.Start();
}
private void lblMarquee_Paint(object sender, PaintEventArgs e)
{
var marquee = sender as Label;
SizeF stringSize = e.Graphics.MeasureString(marqueeText, marquee.Font, -1, marqueeFormat);
PointF stringLocation = new PointF(marqueePosition, (marquee.Height - stringSize.Height) / 2);
stringLength = marquee.ClientRectangle.Width - stringLocation.X;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, stringLocation, marqueeFormat);
if (marqueePosition >= marquee.ClientRectangle.Width) marqueePosition = 0f;
if (stringSize.Width + stringLocation.X > marquee.ClientRectangle.Width) {
PointF partialStringPos = new PointF(-stringLength, (marquee.Height - stringSize.Height) / 2);
e.Graphics.DrawString(marqueeText, marquee.Font, Brushes.Black, partialStringPos, marqueeFormat);
}
marqueePosition += marqueeStep;
}
A couple of other implementations you might find useful:
How to follow the end of a text in a TextBox with no NoWrap
How to draw a string on two not adjacent areas
You need to have two label controls to do this, but it's not really that difficult. First, create a backup label and set it's properties to look like label5:
// A backup label for our scrolling label5
private Label label5_backup;
private void Form1_Load(object sender, EventArgs e)
{
label5.Text = "This is a scrolling label!";
// Set label5_backup to look like label5
label5_backup = new Label
{
Size = label5.Size,
Text = label5.Text,
Top = label5.Top,
Visible = false
};
Controls.Add(label5_backup);
timer2.Interval = 1;
timer2.Start();
}
Then, in the Tick event, as soon as our label5 starts to leave the client rectangle, set our backup label to the proper distance from the left of the form so that it starts to appear on the other side. And as soon as label5 is completely off the form, set it's location to match the backup label and then hide the backup label again.
Note that you can just set the Left property instead of creating a new Location point each time, which simplifies the code a little:
private void timer2_Tick(object sender, EventArgs e)
{
label5.Left++;
// If label5 starts to go off the right, show our backup on the left side of the form
if (label5.Right > ClientRectangle.Width)
{
label5_backup.Left = label5.Right - ClientRectangle.Width - label5.Width;
label5_backup.Visible = true;
}
// If label5 is all the way off the form now, set it's location to match the backup
if (label5.Left > ClientRectangle.Width)
{
label5.Location = label5_backup.Location;
label5_backup.Visible = false;
}
}
Also, if you want to make the scrolling smoother, only increment the Left by 1 each time and reduce the timer2.Interval to a third of what it was before (unless it's already at 1).

How do you make AutoSize happen immediately?

The problem
I'm dynamically adding Buttons to the WinForm. As I do so, I'm repositioning existing Buttons to prevent overlap. The AutoSize property is being used to automatically set Width.
For longer text (that pushes Buttons beyond their default Width), the below code doesn't work.
For example:
b.Width is 75 before AutoSize is set
b.Width is 75 after AutoSize is set
When shifting other Buttons, it shifts them by b.Width + buffer = 83
However after addButton()completes, the AutoSize kicks in and sets the width to 150, overlapping the next Button which is only 83 pixels away instead of 158.
AutoSize appears to change the size of the control too late for it to be of use. How can I make it happen immediately?
Attempt 1 - Code
public void addButton(string text)
{
const int buffer = 8;
//Construct new button
Button b = new Button();
b.Text = text;
b.AutoSize = true;
b.Location = new Point(0, 0);
//Shift over all other buttons to prevent overlap
//b.Width is incorrect below, because b.AutoSize hasn't taken effect
for (int i = 0; i < Controls.Count; i++)
if (Controls[i] is Button)
Controls[i].Location = new Point(Controls[i].Location.X + b.Width + buffer, Controls[i].Location.Y);
Controls.add(b);
}
Attempt 2
Searched Google and StackOverflow for the following:
c# autosize immediately
c# autosize fast
c# autosize not working
Attempt 3
Asking here.
Last Resort
If nothing else works, a timer could be set to reposition Buttons on each tick. However this is very sloppy design, and doesn't aid in learning the intricacies of AutoSize. I'd like to avoid this workaround if possible.
The AutoSize and AutoSizeMode mode are applied only when the control is parented to the another control or form.
So invoke first
Controls.Add(b);
Now the b.Size will the adjusted accordingly and can be used in the calculations.
Alternatively, instead of Size property you can use the GetPreferredSize method to get the correct size without actually applying AutoSize and use it inside the calculations:
var bSize = b.GetPreferredSize(Size.Empty);
//Shift over all other buttons to prevent overlap
//b.Width is incorrect below, because b.AutoSize hasn't taken effect
for (int i = 0; i < Controls.Count; i++)
if (Controls[i] is Button)
Controls[i].Location = new Point(Controls[i].Location.X + bSize.Width + buffer, Controls[i].Location.Y);
The FlowLayoutPanel control does this work for you.
Place one on your form and try adding buttons in the following manner:
Button b = new Button();
b.AutoSize = true;
b.Text = text;
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel1.Controls.Add(b);
flowLayoutPanel1.Controls.SetChildIndex(b, 0);
flowLayoutPanel1.ResumeLayout();
You can subscribe to the Resize event of the last button added. This will allow you to accurately change the locations of all of the buttons because now all of the buttons have been AutoSized.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var button1 = NewButton(0);
button1.Location = new Point(10, 10);
var button2 = NewButton(1);
button2.Location = new Point(button1.Right, 10);
var button3 = NewButton(2);
button3.Location = new Point(button2.Right, 10);
button3.Resize += (s, e) =>
{
button2.Location = new Point(button1.Right, 10);
button3.Location = new Point(button2.Right, 10);
};
Controls.Add(button1);
Controls.Add(button2);
Controls.Add(button3);
}
private Button NewButton(int index)
{
return new Button()
{
Text = "ButtonButtonButton" + index.ToString(),
AutoSize = true
};
}
}

Grow windows form with button click

I want to grow a form when I click the button and it should be in the center of the screen .so I wrote following code snippet.
private void ord_Click(object sender, EventArgs e)
{
this.StartPosition = FormStartPosition.CenterScreen;
this.Size = new Size(1308,599);
this.Show();
}
But when I click the button window grows but half of the window can not see.Here is the picture of that.
GUI after growing
How can I get rid of this problem.?
Whats wrong with my code?
You have to compute both Size and Location:
private void ord_Click(object sender, EventArgs e) {
// Ensure that suggested form size doesn't exceed the screen width and height
this.Size = new System.Drawing.Size(
Screen.GetWorkingArea(this).Width >= 1308 ? 1308 : Screen.GetWorkingArea(this).Width,
Screen.GetWorkingArea(this).Height >= 599 ? 599 : Screen.GetWorkingArea(this).Height);
// locate the form in the center of the working area
this.Location = new System.Drawing.Point(
(Screen.GetWorkingArea(this).Width - Width) / 2,
(Screen.GetWorkingArea(this).Height - Height) / 2);
}
You can use the PrimaryScreen property of the Screen class.
//this.StartPosition = FormStartPosition.CenterScreen;
//this.Show();
Omit these lines you've written, except setting the Size property of the form:
private void ord_Click(object sender, EventArgs e)
{
this.Size = new Size(1308,599);
CenterForm();
}
Create a method named CenterForm() which will set a new location of the form. You can achieve this by calling this method in your button click event.
private void CenterForm()
{
int getWidth = Screen.PrimaryScreen.Bounds.Width;
int getHeight = Screen.PrimaryScreen.Bounds.Height;
int X = getWidth - this.Width;
int Y = getHeight - this.Height;
this.Location = new Point(X / 2, Y / 2);
}
Note: Always remember to anchor your controls when the size of the form has changed.

Width of the form can not be less than 140 pixels. Why?

I created default Windows Forms Application project in Visual Studio 2012. When I run program then saw that width of form can not be less than 140 pixels. Why? And how to overcome this strange restriction?
I was looking for a solution and MinimumSize(0,0) didn't had any effect. Figured out, that MinimumSize set to (1,1) actually fixed the problem and after showing my form it was properly sized smaller than 140px.
Column click event on (ListView)_csvLv that should trigger a popup dialog:
var topAnchor = _csvLv.PointToScreen(new Point(
_csvLv
.Columns
.OfType<ColumnHeader>()
.Where(c => c.DisplayIndex < e.Column)
.Sum(c => c.Width),
0));
Left = topAnchor.X;
Top = topAnchor.Y;
MinimumSize = new Size(1,1);
ClientSize = new Size(_csvLv.Columns[e.Column].Width, 100);
ShowDialog();
Users wouldn't be able to use the window's minimize, maximize, and close buttons at that top. I don't believe you can change that behaviour with the Sizable FormBorderStyle. It's a usability thing.
If you remove the border, by setting it to None for example, you can set it to whatever you want programmatically by doing:
form.Width = [...];
You can resize further forms with border types: None, FixedToolWindow, and SizableToolWindow. The ToolWindows won't let you go below a certain amount as well, but None will let you do anything above 2px. You could set it to some value below that, without getting an exception, but it won't do anything.
Try this.
Autosize no
AutosizeMode growOnly
FormBorderStyle SizableToolWindow <== this one did it
I still can move the form and resize it (width) less tan 112
I never use formborders.. I always like to go with FormBorderstyle.None
To resize, you have to add some code.
Put a pictureBox, add a grip img in it and place it in the corner.
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += new MouseEventHandler(Form1_MouseDown);
pictureBox1.MouseMove += new MouseEventHandler(Form1_MouseMove);
pictureBox1.MouseUp += new MouseEventHandler(Form1_MouseUp);
}
void Form1_MouseUp(object sender, MouseEventArgs e)
{
isHolding = false;
}
void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (isHolding)
{
int diffX = this.Width - pictureBox1.Left;
int diffY = this.Height - pictureBox1.Top;
pictureBox1.Left += e.X - curX;
pictureBox1.Top += e.Y - curY;
this.Width = pictureBox1.Left + diffX;
this.Height = pictureBox1.Top + diffY;
}
}
void Form1_MouseDown(object sender, MouseEventArgs e)
{
isHolding = true;
curX = e.X;
curY = e.Y;
}
int curX = 0, curY = 0;
bool isHolding = false;

FormStartPosition.CenterParent does not work

In the following code, only the second method works for me (.NET 4.0). FormStartPosition.CenterParent does not center the child form over its parent.
Why?
Source: this SO question
using System;
using System.Drawing;
using System.Windows.Forms;
class Program
{
private static Form f1;
public static void Main()
{
f1 = new Form() { Width = 640, Height = 480 };
f1.MouseClick += f1_MouseClick;
Application.Run(f1);
}
static void f1_MouseClick(object sender, MouseEventArgs e)
{
Form f2 = new Form() { Width = 400, Height = 300 };
switch (e.Button)
{
case MouseButtons.Left:
{
// 1st method
f2.StartPosition = FormStartPosition.CenterParent;
break;
}
case MouseButtons.Right:
{
// 2nd method
f2.StartPosition = FormStartPosition.Manual;
f2.Location = new Point(
f1.Location.X + (f1.Width - f2.Width) / 2,
f1.Location.Y + (f1.Height - f2.Height) / 2
);
break;
}
}
f2.Show(f1);
}
}
This is because you are not telling f2 who its Parent is.
If this is an MDI application, then f2 should have its MdiParent set to f1.
Form f2 = new Form() { Width = 400, Height = 300 };
f2.StartPosition = FormStartPosition.CenterParent;
f2.MdiParent = f1;
f2.Show();
If this is not an MDI application, then you need to call the ShowDialog method using f1 as the parameter.
Form f2 = new Form() { Width = 400, Height = 300 };
f2.StartPosition = FormStartPosition.CenterParent;
f2.ShowDialog(f1);
Note that CenterParent does not work correctly with Show since there is no way to set the Parent, so if ShowDialog is not appropriate, the manual approach is the only viable one.
If you set the owner of the child form like so:
Form2 f = new Form2();
f.Show(this);
You can then center it easily like this:
Form2_Load(object sender, EventArgs e)
{
if (Owner != null)
Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
I'm using this code inside my main form, hope it helps:
var form = new MyForm();
form.Show();
if (form.StartPosition == FormStartPosition.CenterParent)
{
var x = Location.X + (Width - form.Width) / 2;
var y = Location.Y + (Height - form.Height) / 2;
form.Location = new Point(Math.Max(x, 0), Math.Max(y, 0));
}
I found setting the location manually is the only reliable option in some more complex cases when form is auto-sized and dynamically modified.
However rather than computing the coordinates manually, I'd suggest using existing method:
this.CenterToParent();
I had the same problem, I eventually went with this:
protected override void OnActivated(EventArgs e) {
if(this.Modal == false && this.StartPosition == FormStartPosition.CenterParent) {
if(!(this.Owner is Form)) {
// Center to the last form opened before this one
int numforms = Application.OpenForms.Count;
this.Owner = Application.OpenForms[numforms - 2];
}
this.CenterToParent();
Application.DoEvents();
}
base.OnActivated(e);
}
Used as:
MyForm form = new MyForm();
form.Show(this); // with or without
The main advantage is that it does what your colleagues expect it to do, without requiring any hack in the calling form.
I found a solution that will center modeless window position to parent's position, and the child window can be still covered by parent window.
You just have to call
f2.Show(f1);
which will set f2 owner to f1, f2 will show over the f1 at it's center position.
Next you set
f2.Owner = null;
and there you go, f2 is a separate window, with correct startup position.
I realize this is an old question, but I was recently having the same problem and for reasons I won't get in to, I did not want to use the form.ShowDialog() method and my application was not an MDI application, therefore the CenterParent method was not having any effect. This is how I solved the problem, by computing the coordinates for the form that I wanted centered and triggering the new location in the main form's LocationChanged event. Hopefully this will help someone else having this problem.
In the example below, the parent form is called MainForm and the form I want centered in MainForm is called pleaseWaitForm.
private void MainForm_LocationChanged(object sender, EventArgs e)
{
Point mainFormCoords = this.Location;
int mainFormWidth = this.Size.Width;
int mainFormHeight = this.Size.Height;
Point mainFormCenter = new Point();
mainFormCenter.X = mainFormCoords.X + (mainFormWidth / 2);
mainFormCenter.Y = mainFormCoords.Y + (mainFormHeight / 2);
Point waitFormLocation = new Point();
waitFormLocation.X = mainFormCenter.X - (pleaseWaitForm.Width / 2);
waitFormLocation.Y = mainFormCenter.Y - (pleaseWaitForm.Height / 2);
pleaseWaitForm.StartPosition = FormStartPosition.Manual;
pleaseWaitForm.Location = waitFormLocation;
}
If you have a resizable parent form and you wanted your sub form to also be centered whenever the main form is resized, you should, in theory, be able to place this code in a method and then call the method on both the LocationChanged and SizeChanged events.
JYelton's answer worked for me, but the form is only centered the first time Show() is called.
If you want to Hide() the form, and then have it re-centered on the parent every time Show() is called you need use the following in your form:
public new void Show(IWin32Window owner)
{
base.Show(owner);
if (Owner != null)
Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
Maybe this can help somebody.
Form frmMessage = new Form();
From experience, although they look similar, they behave different:
This variant doesn't work:
if (frmMessage.Parent != null)
frmMessage.CenterToParent();
else
frmMessage.CenterToScreen();
And this variant works
if (frmMessage.Parent != null)
frmMessage.StartPosition = FormStartPosition.CenterParent;
else
frmMessage.StartPosition = FormStartPosition.CenterScreen;
Using
form.Show(this);
throws an exception if you call it a second time. Manually setting the location seems to be the only reliable option :/ (wasn't it fairly recently that CenterParent used to work?)
just put the code in the constructor of your form.
public FrmSample()
{
InitializeComponent();
// must be after the InitializeComponent()
this.StartPosition = FormStartPosition.CenterParent;
}
Small Change to JYelton's answer
Form2_Load(object sender, EventArgs e)
{
if (Owner != null && Parent == null && StartPosition == FormStartPosition.CenterParent)
Location = new Point(Owner.Location.X + Owner.Width / 2 - Width / 2,
Owner.Location.Y + Owner.Height / 2 - Height / 2);
}
An old question, I know, but I had the same issue but for a different reason.
The Form I was opening had an overridden OnLoad method:
protected override void OnLoad(EventArgs e)
{
//... etc.
}
but was not calling the base implementation as it must do:
protected override void OnLoad(EventArgs e)
{
//... etc.
base.OnLoad(e);
}
When overriding OnLoad(EventArgs) in a derived class, be sure to call the base class's OnLoad(EventArgs) method so that registered delegates receive the event.
Building off the answer by deerchao,
if (form.StartPosition == FormStartPosition.CenterParent) {
form.Location = new Point(Location.X + (Width - form.Width) / 2, Location.Y + (Height - form.Height) / 2);
}
Do this after form.Show(), this will work on multi-monitors also

Categories