I am writing a screensaver program in C#. And I want it to do the following:
Start with a text. After about 3 seconds or more, hide the text and show an image. After the next 3 seconds, hide the image show the text and keep going round the loop until the user does something that exits the screensaver.
What I have done:
I started with a simple textlabel and a timer control. I was able to get the textlabel change positions on screen after every 3 seconds. I updated my code to include a picturebox, and in my timer_tick method, I inserted an if-else statement to check, when the method is called,
if the textlabel is shown, hide it and show the picturebox.
else if the picturebox is shown, hide it and show the textbox. Code is shown below:
private void Form1_Load(object sender, EventArgs e)
{
Cursor.Hide();
TopMost = true;
moveTimer.Interval = 3000;
moveTimer.Tick += new EventHandler(moveTimer_Tick);
moveTimer.Start();
}
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (pictureBox1.Enabled == true)
{
pictureBox1.Hide();
textLabel.Show();
}
if (textLabel.Enabled == true)
{
textLabel.Hide();
pictureBox1.Show();
}
}
Here's the problem:
WHen I run the screensaver program, the screen starts with the text, changes to the picture after 3 seconds and stops there.
What do I do to get it moving in a continous loop, showing/hiding the textlabel or picturebox?
Have I implemented this in the right way?
Please clear and concise explanations/answers will be highly appreciated.
Thanks!
Perhaps you could keep the state in a variable that you can switch
private bool state = false;
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (state)
{
pictureBox1.Hide();
textLabel.Show();
}
else
{
textLabel.Hide();
pictureBox1.Show();
}
state = !state;
}
How about something like this?
Enabled says whether the object can receive input.
Visible is what says if its visible or not.
You see it change once and only once because all the objects are enabled. The first if succeeds, hiding the picture and showing the text. But then the second if also succeeds, showing the picture and hiding the text. Since this is all in one event callback, you never see the first if happen, because the second overrides it.
As you're realizing in the comments, the answer is to not check enabled. Instead, check Visible. Make sure to use an else as well, otherwise you may still get the same issue of both being true.
private void moveTimer_Tick(object sender, System.EventArgs e)
{
//Move text to new Location
//textLabel.Left = rand.Next(Math.Max(1, Bounds.Width - textLabel.Width));
//textLabel.Top = rand.Next(Math.Max(1, Bounds.Height - textLabel.Height));
if (pictureBox1.Visible == true)
{
pictureBox1.Hide();
textLabel.Show();
}
else
{
textLabel.Hide();
pictureBox1.Show();
}
}
Related
Let's take an example from android. Assume you send a message to someone and after message was send you can see (for a few seconds) kind of notification on the screen like Your message was send.
That is exactly what I would like to find in Winform. In my Winform app user click on the button and I would like to make kind of UI response, show him a message for a few sec, like Button clicked.
How to do it?
P.S. Actually I tried to find out how to do it, but everything that I found is kind of notification on the screen at the right bottom corner. It is not actually what I am looking for. I need something like you can see on screenshot. This text should appear in the form, not on the corner of the screen.
P.S 2 Tooltip also not what I am looking for. Tooltip is something that binded close to the button(view). I need kind of general UI response. User click on buttons and instead to show him a dialog that force user to move his mouse and close the dialog, I need kind of softy message that disappear after a few sec.
I need kind of softy message that disappear after a few sec.
I think the tooltips are what you are looking for. The idea is you can programmatically control where to show a tooltip and when to hide it.
Please start a new WinForms project. Add three buttons, ToolTip and Timer to the form. Write the next event handlers (and bind them to the corresponding components):
private void button_Click(object sender, EventArgs e)
{
toolTip1.Show(((Button)sender).Text + " is pressed", this, 300, 300);
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
toolTip1.Hide(this);
}
After demo starting you'll see a tooltip with certain text appearing at the same position for the 1 second.
#Miamy's solution not a bad one, however you have to center text in notification box, and you don't need to use timer. Here the custom tooltip class which centers the tooltip text location and you can see the output below:
Moreover I added some coloring features along with default timer implementation.
class CustomToolTip : ToolTip
{
public int SIZE_X = 500;
public int SIZE_Y = 50;
public CustomToolTip()
{
this.OwnerDraw = true;
this.Popup += new PopupEventHandler(this.OnPopup);
this.Draw += new DrawToolTipEventHandler(this.OnDraw);
}
string m_EndSpecialText;
Color m_EndSpecialTextColor = Color.Black;
public Color EndSpecialTextColor
{
get { return m_EndSpecialTextColor; }
set { m_EndSpecialTextColor = value; }
}
public string EndSpecialText
{
get { return m_EndSpecialText; }
set { m_EndSpecialText = value; }
}
private void OnPopup(object sender, PopupEventArgs e) // use this event to set the size of the tool tip
{
e.ToolTipSize = new Size(SIZE_X, SIZE_Y);
}
private void OnDraw(object sender, DrawToolTipEventArgs e) // use this event to customise the tool tip
{
Graphics g = e.Graphics;
LinearGradientBrush b = new LinearGradientBrush(e.Bounds,
Color.AntiqueWhite, Color.LightCyan, 45f);
g.FillRectangle(b, e.Bounds);
g.DrawRectangle(new Pen(Brushes.Black, 1), new Rectangle(e.Bounds.X, e.Bounds.Y,
e.Bounds.Width - 1, e.Bounds.Height - 1));
System.Drawing.Size toolTipTextSize = TextRenderer.MeasureText(e.ToolTipText, e.Font);
g.DrawString(e.ToolTipText, new Font(e.Font, FontStyle.Bold), Brushes.Black,
new PointF((SIZE_X - toolTipTextSize.Width)/2, (SIZE_Y - toolTipTextSize.Height) / 2));
b.Dispose();
}
}
You can call this tooltip as below code:
CustomToolTip notifyError = new CustomToolTip();
notifyError.Show("Please enter a number.", this, 100, 100, 2000);
Above code creates notification box at 100, 100 location for 2 seconds.
So i tried to make a button more animated by making that when i hover my mouse over the button its back colour goes slowly from a darker gray to lighter gray, sadly the the MouseHover didn't worked out great for me because i had to use it with if (while, do and for isn't working at all) so i changed it to MouseMove And well that created its own problems, right now the colour getting lighter only when i move my mouse on it, here is the code:
private void BtnPlay_MouseMove(object sender, MouseEventArgs e)
{
byte coloDat = btnPlay.FlatAppearance.MouseOverBackColor.B;
if (btnPlay.FlatAppearance.MouseOverBackColor != Color.FromArgb(68, 68, 68))
{
coloDat++;
btnPlay.FlatAppearance.MouseOverBackColor = Color.FromArgb(coloDat, coloDat, coloDat);
System.Threading.Thread.Sleep(1);
}
}
Im gonna use the code multiple times in the project so is there a way to do this without making a wall of text?
Edit: For avoiding confusion; im trying to do my project with Button.FlatAppearance.MouseOverBackColor and not Button.BackgroundColour.
If you want to create this in WPF, just create a style with a storyboard. In windows forms, you need to use a timer.
The reason a loop didn't work for you is that the loop was running on the same thread as the UI, so the UI wasn't being updated until after your loop was over. To animate an effect in windows forms, you have to let the event function end so that the UI can update and then have your function called again for the next frame. That is what the timer element does.
I created a demo program with two animated buttons. To create buttons with animated background colors I first set up a start color, end color, the amount I want the color to change in each frame, the button the mouse is currently over and the transition progress of each button. I added that last so that I could have the buttons gradually transition back after the mouse was over something else.
private Color startColor = Color.AliceBlue;
private Color endColor = Color.BlueViolet;
private double step = 0.01;
private Button lastOver = null;
private Dictionary<Button, double> transitionProgress = new Dictionary<Button, double>();
Then I attached the event handlers of both of my buttons to the same functions, the functions below. The first uses ContainsKey so that I can make more buttons animated by just assigning them to these event handler functions.
private void demoButton_MouseHover(object sender, EventArgs e)
{
if (sender != lastOver)
{
lastOver = (Button)sender;
if (!transitionProgress.ContainsKey(lastOver))
{
transitionProgress[lastOver] = 0.0;
}
}
}
private void demoButton_MouseLeave(object sender, EventArgs e)
{
lastOver = null;
}
Then I created a Timer with the following event handler. It goes through each button and transitions it based on whether the mouse is currently over that button. It also only updates the background color if it has changed to improve performance.
private void styleUpdate_Tick(object sender, EventArgs e)
{
for (int i = 0; i < transitionProgress.Count; i++)
{
Button button = transitionProgress.Keys.ElementAt(i);
bool changing = false;
if (button == lastOver)
{
if (transitionProgress[button] < 1.0)
{
transitionProgress[button] = Math.Min(1.0, transitionProgress[button] + step);
changing = true;
}
}
else
{
if (transitionProgress[button] > 0.0)
{
transitionProgress[button] = Math.Max(0.0, transitionProgress[button] - step);
changing = true;
}
}
if (changing)
{
double progress = transitionProgress[button];
button.BackColor = Color.FromArgb(
(int)Math.Floor((endColor.R - startColor.R) * progress + startColor.R),
(int)Math.Floor((endColor.G - startColor.G) * progress + startColor.G),
(int)Math.Floor((endColor.B - startColor.B) * progress + startColor.B)
);
}
}
}
The timer has to be enabled and the interval set to 16
this.styleUpdate.Enabled = true;
this.styleUpdate.Interval = 16;
this.styleUpdate.Tick += new System.EventHandler(this.styleUpdate_Tick);
That does seem like a lot of code, but to add it to another button, you just need two more lines of code.
this.yourButtonName.MouseLeave += new System.EventHandler(this.demoButton_MouseLeave);
this.yourButtonName.MouseHover += new System.EventHandler(this.demoButton_MouseHover);
I have created a text box, inside a PictureBox, on mouse click at run time. Now I want to resize it using mouse drag. Is there some simple way to do this ?
Here is the code I have so far:
public partial class Form1 : Form
{
public static TextBox PTB; //declaring text box to be created
public static bool textOption; //stores the state of button , i.e whether or not text box button is clicked before or not
public Form1()
{
InitializeComponent();
this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click);
}
private void pictureBox1_Click(object sender, EventArgs e)
{
if (textOption == true)//if user selected option to draw text box
{
MouseEventArgs eM = (MouseEventArgs)e; //create an instance of mouse event
PTB = new TextBox();//dynamically creating text box
PTB.Location = new System.Drawing.Point(eM.X, eM.Y);//settign position of textbox where mouse was clicked
PTB.Name = "textBox1";
PTB.Size = new System.Drawing.Size(100, 20);//size of text box
this.pictureBox1.Controls.Add(PTB);//adding the textbox to the picture box
}
}
Update
I'm sorry I have totally misread your question.
The reason is probably that you seem to be under the impression that Paint programs have TextBoxes sitting on their canvas. They don't. They draw text the same way as they draw lines or circles etc..
Also: Resizing the TextBox will not change the Font size, in case that's is what you want.
Finally: A TextBox will never be transparent, it will always sit on the PictureBox and look, well, like a TextBox. Not like anything in a Paint programm..
But: If you actually do want to resize the TextBox here are a few hints:
You need some way to show the user they are on the right spot by changing the cursor to the right icon
You need to store the mouse down point (attention: it will be inside the TextBox!) and keep track of the increments in the MouseMove. As long as the Button is down all rported e.Location will still be in the TextBox coordinates.
Use the increments (not the absolute values) to increase the size of the TextBox.
It is really hard to get the resizing right on the top and left sides. (Because it will involve moving at the same time), so better don't try!
Do include moving, which is easy and will suffice for all you need.
No, this is a good deal harder than increasing font size. 200-300 lines of code, the last time I did it..
But you may find another somewhat simpler answer; look for "Resize Control with Mouse winforms c#" on Google..
I leave the old answer in place, even if it not what you were looking for..
Old answer about changing font size while placing text:
It is not very hard but you need to get it right; it is basically the same as drawing a Rectangle
with a live preview. You need these things: four events, a Point or two, a Size and a font variable..
The events are:
MouseDown
MouseMove
MouseUp
Paint
You need to store a point for the placement (in the MouseDown event) and a size you update in the MouseMove.
From that size you can calculate the maximum Font size you can fit in the Rectangle.
On MouseUp you finalize things.
In the Paint event you draw string at the down Point with the current Font size.
In the MouseMove you call Invalidate on the PictureBox to trigger the Paint event.
in the MouseMouve you should check the Button to be the left one.
For extra good UI you can also check the keyboard for space and use it to move the DownPoint..
The Click event is rather useless, btw..
Here is a minimal code example to get you started:
Point mDown = Point.Empty;
float fSize = 12f;
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
using (Font font = new Font("Consolas", fSize))
e.Graphics.DrawString("Hello World", font, Brushes.Black, mDown);
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
pictureBox1.Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
fSize = Math.Abs(e.Y - mDown.Y) / 2f + 1;
pictureBox1.Invalidate();
}
I left out the MouseUp. Here you would store the final state (font, location..) of the drawn string somewhere or persist it by drawing into a Bitmap etc..
I also didn't do a full Rectangle calculation but determined the font size by simply scaling the y-movement down a little.. You could improve with a little pythagoras ;-)
The ability to resize a window is innate behavior, provided by the default window procedure built into Windows. All you have to do is give the control a resizable border. Everything else is for free, you'll get the correct cursor and dragging a corner or edge resizes the control.
using System;
using System.Windows.Forms;
class ResizeableTextBox : TextBox {
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
cp.Style |= 0x840000; // Turn on WS_BORDER + WS_THICKFRAME
return cp;
}
}
}
This sample will create the TextBox on the MouseDown event and start resizing, the final size of the TextBox will be where the mouse button is released on the MouseUp event. It will let you create multiple textboxes as well.
I realize this might not be exactly what you want but it might be a start.
private int _textBoxCounter;
private TextBox _textBoxCurrentResizing;
public Form1()
{
InitializeComponent();
pictureBox1.MouseDown += PictureBox1OnMouseDown;
pictureBox1.MouseUp += PictureBox1OnMouseUp;
pictureBox1.MouseMove += PictureBox1OnMouseMove;
}
public Point RelativeMousePosition { get { return PointToClient(MousePosition); } }
private void PictureBox1OnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
ResizeTextBox();
}
private void PictureBox1OnMouseUp(object sender, MouseEventArgs mouseEventArgs)
{
EndResizeTextBox();
}
private void PictureBox1OnMouseDown(object sender, MouseEventArgs mouseEventArgs)
{
var tb = CreateTextBox();
StartResizeTextBox(tb);
}
private TextBox CreateTextBox()
{
var tb = new TextBox
{
Location = RelativeMousePosition,
Size = new Size(100, 20),
Multiline = true,
Name = "textBox" + _textBoxCounter++,
};
pictureBox1.Controls.Add(tb);
return tb;
}
private void StartResizeTextBox(TextBox tb)
{
_textBoxCurrentResizing = tb;
}
private void ResizeTextBox()
{
if (_textBoxCurrentResizing == null) return;
var width = Math.Abs(_textBoxCurrentResizing.Left - RelativeMousePosition.X);
var height = Math.Abs(_textBoxCurrentResizing.Top - RelativeMousePosition.Y);
_textBoxCurrentResizing.Size = new Size(width, height);
}
private void EndResizeTextBox()
{
_textBoxCurrentResizing = null;
}
I am creating a WPF application which has a MediaElement that plays video. I didn't like the way the Slider control looked. I therefore I attempted, mostly successfully to use a ProgressBar in its place.
I have implemented functionality for clicking the ProgressBar to set the value of both itself and that of the position of the MediaElement. However when I also implemented a DispatcherTimer to increment the value of the ProgressBar alongside this functionality I am getting an oddity that I cannot work out where it is coming from.
Basically when I click on the ProgressBar, the fill correctly updates to where the cursor is (I am setting it directly in setProgressBarPosition), but then moves back a bit (I am guessing, when the _Tick method call is made of the DispatcherTimer, which is setting the ProgressBar.Value based on the position of meVideo).
The MSPaint arrow shows where I clicked, the red fill shows where it updated to, the gap between the cursor click and the position of the fill gets larger the later in the timeline I click which makes me think I have made a mistake in a calculation somewhere:
I am thinking I have probably done something wrong in the setting of meVideo.Position in pgbVideo_MouseUp but I can't see it. Here is my code:
private void videoTimer_Tick(object sender, EventArgs e)
{
if (!isDragging)
{
pgbVideo.Value = meVideo.Position.TotalSeconds;
}
}
/*
* Handles the clicking of the video timeline.
*/
private void pgbVideo_MouseDown(object sender, MouseButtonEventArgs e)
{
double mousePosition = e.GetPosition(pgbVideo).X;
setProgressBarPosition(mousePosition);
isDragging = true;
}
/*
* Handles the dragging of the mouse over the video timeline.
*/
private void pgbVideo_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
double mousePosition = e.GetPosition(pgbVideo).X;
setProgressBarPosition(mousePosition);
}
}
/*
* Sets the position of the video timeline progressbar.
*/
private void setProgressBarPosition(double mousePosition)
{
if (meVideo.Source != null)
{
double progressBarPosition = mousePosition / pgbVideo.ActualWidth * pgbVideo.Maximum;
pgbVideo.Value = progressBarPosition;
}
}
/*
* Sets the position of the media element.
*/
private void pgbVideo_MouseUp(object sender, MouseButtonEventArgs e)
{
if (meVideo.Source != null)
{
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbTime.Maximum);
meVideo.Position = new TimeSpan((int)videoPositon);
}
isDragging = false;
}
Thanks for reading, any help appreciated!
EDIT: Here is my _MediaOpened method where I set the maximum value of the ProgressBar:
private void meVideo_MediaOpened(object sender, RoutedEventArgs e)
{
pgbVideo.Maximum = meVideo.NaturalDuration.TimeSpan.TotalSeconds;
videoTimer.Start();
isPlaying = true;
}
Annoyingly it was a simple mistake made by me being too quick on the IntelliSense! In pgbVideo_MouseUp the line:
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbTime.Maximum);
Should be:
double videoPositon = meVideo.NaturalDuration.TimeSpan.Ticks * (pgbVideo.Value / pgbVideo.Maximum);
pgbTime refers to another, unrelated ProgressBar which has the Maximum of 100. With the TotalSeconds of the loaded video being used to set the Maximum of pgbVideo and the video length being 92 seconds, the problem presented as an issue with accuracy (to me at least!), whereas if the video was 10 minutes the behaviour would have been way off and maybe led me to looking for a more obvious mistake!
Thanks to everyone that tried to help.
Have a simple form that has a PictureBox in one location. I want to change the cursor to the Cross cursor when entering that control, and change it back when it leaves.
private void Canvas_MouseEnter(object sender, EventArgs e)
{
this.Canvas.Cursor = Cursors.Cross;
}
private void Canvas_MouseLeave(object sender, EventArgs e)
{
this.Canvas.Cursor = Cursors.Default;
}
This doesn't work. If I look closely, I can see it quickly change on MouseEnter, but it flips right back to the default cursor. I have to add "this.Canvas.Cursor = Cursors.Cross;" to the MouseMove event in order for it to work, but then I can constantly see it flickering back to the default cursor.
What gives? This is the only cursor-related code in my whole application, what would be causing it to reset to the default cursor every time I move the mouse?
Thanks.
EDIT: I am an idiot. Person I am collaborating with on this little app had some cursor code tucked away somewhere else that was causing the problem. Thanks guys.
Why don't you set the cursor for the picturebox?
yourPictureBox.Cursor = Cursors.Cross;
I've tried in a new project from scratch (with just the mouseenter/leave handlers and nothing else) and it works.
Might be something else in your application ?
public Form1()
{
InitializeComponent();
pictureBox1.MouseHover += new EventHandler(PictureBox1_MouseHover);
}
void pictureBox1_MouseHover(object sender, EventArgs e)
{
this.PictureBox1.Cursor = Cursors.Cross;
}
You want to use MouseHover event handler.