I would like to 'shake' my winforms form to provide user feedback, much like the effect used on a lot of mobile OS's.
I can obviously set the location of the window and go back and forth with Form1.Location.X etc. but the effect from this method is terrible. I'd like something a little more fluent - or alternatively is there a way to shake the entire screen?
I'll only be targeting Windows 7 using .net 4.5.
Update
Using both Hans and Vidstige suggestions I've come up with the following, which also works when the window is maximized - I wish I could pick two answers, I've up-voted your answer though Vidstige and hope others will too. Hans' answer hits all the salient points though.
Two forms MainForm and ShakeForm
MainForm Code
Private Sub shakeScreenFeedback()
Dim f As New Shakefrm
Dim b As New Bitmap(Me.Width, Me.Height, PixelFormat.Format32bppArgb)
Me.DrawToBitmap(b, Me.DisplayRectangle)
f.FormBorderStyle = Windows.Forms.FormBorderStyle.None
f.Width = Me.Width
f.Height = Me.Height
f.ShowInTaskbar = False
f.BackgroundImage = b
f.BackgroundImageLayout = ImageLayout.Center
f.Show(Me)
f.Location = New Drawing.Point(Me.Location.X, Me.Location.Y)
'I found putting the shake code in the formLoad event didn't work
f.shake()
f.Close()
b.Dispose()
End Sub
ShakeForm Code
Public Sub shake()
Dim original = Location
Dim rnd = New Random(1337)
Const shake_amplitude As Integer = 10
For i As Integer = 0 To 9
Location = New Point(original.X + rnd.[Next](-shake_amplitude, shake_amplitude), original.Y + rnd.[Next](-shake_amplitude, shake_amplitude))
System.Threading.Thread.Sleep(20)
Next
Location = original
End Sub
Have you tried something like this?
private void shakeButton_Click(object sender, EventArgs e)
{
Shake(this);
}
private static void Shake(Form form)
{
var original = form.Location;
var rnd = new Random(1337);
const int shake_amplitude = 10;
for (int i = 0; i < 10; i++)
{
form.Location = new Point(original.X + rnd.Next(-shake_amplitude, shake_amplitude), original.Y + rnd.Next(-shake_amplitude, shake_amplitude));
System.Threading.Thread.Sleep(20);
}
form.Location = original;
}
The typical problem is having way too many controls on the form, making the painting too slow. So just fake it, create a borderless window that displays a bitmap of the form and shake that one. Create the bitmap with the form's DrawToBitmap() method. Use 32bppPArgb for the pixel format, it draws ten times faster than all the other ones.
You can use the Aero Shake feature of Windows 7 to achieve this.
Better you can have a look in the below link for more details:
http://www.codeproject.com/Articles/36294/Aero-Shake
Here is a small work around, you may try it.
private void button1_Click(object sender, EventArgs e)
{
this.Width = this.Width - 10;
this.Height = this.Height - 10;
Thread.Sleep(15);
this.Width = this.Width + 10;
this.Height = this.Height + 10;
}
Related
Good afternoon,
I am new to object-oriented programming, .NET and C#. I am studying these previous mentioned topics and presently am doing a relatively simple programming assignment which turned out not to be so simple after all, at least ... for me still.
I want to create a Windows Form Application which contains one form that is filled with country flags (.png, 128px x 128px). In the Form_OnLoad() the files are read and the flags are stored array of PictureBox objects and set several object attributes. Then the form is filled neatly in rows of 8 flags. So far so good.
Problem:
I would like to add a MouseOver event-handler attached to each PictureBox that generates a ToolTip with the country name of the specific flags. In the event-handler I don't know what code to put after what I have managed to do myself already. Specifically, I would like to address the array with flags from the MouseOver event-handler method, but it's not visible from there. I am just stuck here, although my intuition tells me that I am not far from my goal, at this moment my mind decided to give up on me a few meters away from the finish line. Would someone be so kind to help me out with this please?
Here's what I got already:
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace WorldFlags
{
public partial class FormWorldFlags :Form {
public FormWorldFlags() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
int col = 0, row = 0;
string imageDirectory = #"D:\Documents\Visual Studio 2017\Projects\ITvitae\WorldFlags\flags\";
string[] imageFileList = Directory.GetFiles(imageDirectory);
PictureBox[] countryFlag = new PictureBox[imageFileList.Length];
for (int i = 0; i < imageFileList.Length; i++) {
countryFlag[i] = new PictureBox();
countryFlag[i].Name = Path.GetFileNameWithoutExtension(imageFileList[i]);
countryFlag[i].Image = Image.FromFile(imageFileList[i]);
countryFlag[i].Location = new Point(col * 128 + 1, row * 128 + 1);
countryFlag[i].Size = new Size(128, 128);
countryFlag[i].MouseHover += FormWorldFlags_MouseHover;
if (col + 1 == 8) {
row++;
col = 0;
} else
col++;
Controls.Add(countryFlag[i]);
}
}
private void FormWorldFlags_MouseHover(object sender, EventArgs e) {
ToolTip countryName = new ToolTip();
countryName.SetToolTip(?????)
}
}
};
Thanks so much in advance.
Joeri van der Heijden
change your handler to the following:
private void FormWorldFlags_MouseHover(object sender, EventArgs e) {
ToolTip countryName = new ToolTip();
countryName.SetToolTip(sender, "country name");
}
If you want to access the array within the handler you can can just move it out of the form load function. Alternatively you can make use of the Tag property when loading the images and access the value in the handler.
I have a problem that I couldn't be able to fix since one week, I hope someone could have ever experienced this.
I'm using SharpDX with a windows form project, basically there is a form with a picturebox and some panel over it.
The typical Swapchain.Present(1,PresentFlags.None) works good when I zoom or translate the image. However I get this strange situation where when I minimize my screen and reopen it back, all the swapchain content is hidden like if it has been erased. However, I know it hasn't and this is probably a race condition between GDI refresh and SharpDX.
So, I tried to overrides WndProc(Message m) in every control and handle it's paint and eraseBackground event. This is not working. When I debug, windows message are always different from time to time so it is quite hard to figure out what could be wrong.
If anyone has ever experience this behavior while mixing SharpDX(or directX) on Windows form, I would really like an answer.
Here is how I handle the resize event of my SharpDX DeviceContext
Public Overrides Sub Resize(Width As Integer, Height As Integer)
If m_SwapChain IsNot Nothing Then
If m_BackBuffer IsNot Nothing Then
m_BackBuffer.Dispose()
End If
If m_2DDeviceContext IsNot Nothing Then
m_2DDeviceContext.Dispose()
End If
If m_2DTarget IsNot Nothing Then
m_2DTarget.Dispose()
End If
m_SwapChain.ResizeBuffers(2, Width, Height, Format.B8G8R8A8_UNorm, SwapChainFlags.GdiCompatible)
m_BackBuffer = m_SwapChain.GetBackBuffer(Of Surface)(0)
m_2DDeviceContext = New SharpDX.Direct2D1.DeviceContext(m_2DDevice, SharpDX.Direct2D1.DeviceContextOptions.EnableMultithreadedOptimizations)
m_2DTarget = New SharpDX.Direct2D1.Bitmap(m_2DDeviceContext, m_BackBuffer, m_Properties)
m_2DDeviceContext.AntialiasMode = AntialiasMode.PerPrimitive
m_2DDeviceContext.Target = m_2DTarget
CType(m_Context, GPUDrawingContext).DeviceContext = m_2DDeviceContext
End If
End Sub
EDIT :
After trying many stuff, I realize it was happening right when I call base.Wndproc(m) where m = WM_PAINT.
I can't ignore the paint message because if I do, my control is still invalidated and it will add a new WM_PAINT to the event queue and send me into an infinite loop. I really cannot do base.Wndproc(m) because this is where my swapchain gets hide.
Is their any way to handle(ignore) this event message without having it back in the loop because SharpDX doesn't require this function since it's a paint layer over the control.
When you just want to resize the swapchain you don't have to recreate the DeviceContext. Maybe here is the first cause of your problem, because the swapchain was created with a different DeviceContext than the backbuffer.
For me this is working in C#:
first add eventhandlers to the resize events of the control. No need to override the WM_PAINT:
form.ResizeBegin += (o, e) => {
formHeight = ((Form)o).Height;
formWidth = ((Form)o).Width;
};
form.ResizeBegin += (o, e) => {
isResizing = true;
};
form.ResizeEnd += (o, e) => {
isResizing = false;
HandleResize(o, e);
};
form.SizeChanged += HandleResize;
with this I can save the old size of the control for comparison. And I'm just resizing the swapchain after the resize is finished and not for every event (like when resizing a window). Also I will only resize if the control is not minimized:
private void HandleResize(object sender, System.EventArgs e) {
Form f = (Form)sender;
if ((f.ClientSize.Width != formWidth || f.ClientSize.Height != formHeight)
&& !isResizing
&& !(f.WindowState == FormWindowState.Minimized)) {
formWidth = f.ClientSize.Width;
formHeight = f.ClientSize.Height;
DoResize(formWidth, formHeight);
}
}
Finally DoResize is following (renderTarget is my custom class):
private void DoResize(int width, int height) {
renderTarget.Dispose();
swapChain.ResizeBuffers(1, width, height, Format.R8G8B8A8_UNorm, SwapChainFlags.AllowModeSwitch);
using (var resource = Resource.FromSwapChain<Texture2D>(swapChain, 0)) {
//recreate the rendertarget with the new backbuffer
renderTarget.Resize(width, height, resource);
}
}
This is simply an annoyance.
But the system seems to stack child windows properly for a while, but then at some point, before you expect it to, it begins re-stacking them over the top of the ones you've already laid.
Are there any configurations I can set? I mean for the size of the MDI setup that I'm missing. It seems to stop before the center of the screen. I will post a picture of a test project, but it is a little similar to what happens in the real project. I realize that I can do a 'Cascade' and other functions, to windows after they already in place. But that is not desirable. I also realize that MDI forms fell out of fashion a while back. Well... I'm not getting rid of them yet.
I think this is what Hans was saying:
In the test project I put this, and it works...
Note: This is in the load of the MDIChild.
private void Form2_Load(object sender, EventArgs e)
{
int t = 1, l = 1;
int d = this.MdiParent.MdiChildren.GetUpperBound(0) - 1;
if (d >= 0)
{
Form f = this.MdiParent.MdiChildren[d];
t = f.Top + 20;
l = f.Left + 20;
}
this.Top = t;
this.Left = l;
}
While searching for code to fade a winform, I came across this page on the MSDN forum.
for (double i = 0; i < 1; i+=0.01)
{
this.Opacity = i;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
The for loop has a non-integer increment and, from a previous question I asked, that's not a good programming technique (due to inexact representation of most decimals).
I came up with this alternative.
for (double i = 0; i < 100; ++i)
{
this.Opacity = i/100;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
Which of these is more efficient?
If there's a better algorithm for fading a form, I'll be very glad if it is included.
Thanks.
Forget timers (pun intended).
With Visual Studio 4.5 or higher, you can just await a task that is delayed. An advantage of this method is that it's asynchronous, unlike a thread Sleep or DoEvents loop, which blocks the application during the fade (and the other aforementioned DoEvents problems).
private async void FadeIn(Form o, int interval = 80)
{
//Object is not fully invisible. Fade it in
while (o.Opacity < 1.0)
{
await Task.Delay(interval);
o.Opacity += 0.05;
}
o.Opacity = 1; //make fully visible
}
private async void FadeOut(Form o, int interval = 80)
{
//Object is fully visible. Fade it out
while (o.Opacity > 0.0)
{
await Task.Delay(interval);
o.Opacity -= 0.05;
}
o.Opacity = 0; //make fully invisible
}
Usage:
private void button1_Click(object sender, EventArgs e)
{
FadeOut(this, 100);
}
You should check if the object is disposed before you apply any transparency to it. I used a form as the object, but you can pass any object that supports transparency as long as it's cast properly.
So, first off, application.DoEvents should be avoided unless you really know what you're doing and are sure that this is both an appropriate use of it, and that you are using it correctly. I'm fairly certain that neither is the case here.
Next, how are you controlling the speed of the fading? You're basically just letting the computer fade as quickly as it can and relying on the the inherent overhead of the operations (and background processes) to make it take longer. That's really not very good design. You're better off specifying how long the fade should take from the start so that it will be consistent between machines. You can use a Timer to execute code at the appropriate set intervals and ensure that the UI thread is not blocked for the duration of the fade (without using DoEvents).
Just modify the duration below to change how long the fade takes, and modify the steps to determine how "choppy" it is. I have it set to 100 because that's effectively what your code was doing before. In reality, you probably don't need that many and you can just lower to just before it starts getting choppy. (The lower the steps the better it will perform.)
Additionally, you shouldn't be so worried about performance for something like this. The fade is something that is going to need to be measured on the scale of about a second or not much less (for a human to be able to perceive it) and for any computer these days it can do so, so much more than this in a second it's not even funny. This will consume virtually no CPU in terms of computation over the course of a second, so trying to optimize it is most certainly micro-optimizing.
private void button1_Click(object sender, EventArgs e)
{
int duration = 1000;//in milliseconds
int steps = 100;
Timer timer = new Timer();
timer.Interval = duration / steps;
int currentStep = 0;
timer.Tick += (arg1, arg2) =>
{
Opacity = ((double)currentStep) / steps;
currentStep++;
if (currentStep >= steps)
{
timer.Stop();
timer.Dispose();
}
};
timer.Start();
}
I wrote a class specifically for fading forms in and out. It even supports ShowDialog and DialogResults.
I've expanded on it as I've needed new features, and am open to suggestions. You can take a look here:
https://gist.github.com/nathan-fiscaletti/3c0514862fe88b5664b10444e1098778
Example Usage
private void Form1_Shown(object sender, EventArgs e)
{
Fader.FadeIn(this, Fader.FadeSpeed.Slower);
}
for (double i = 0; i < 1; i+=0.01)
{
this.Opacity = i;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
is more efficient as the number of floating point divisions are more machine-expensive than compared to floating point additions(which do not affect vm-flags). That said, you could reduce the number of iterations by 1/2(that is change step to i+=0.02). 1% opacity reduction is NOT noticeable by the human brain and will be less expensive too, speeding it up almost 100% more.
EDIT:
for(int i = 0; i < 50; i++){
this.Opacity = i * 0.02;
Application.DoEvents();
System.Threading.Thread.Sleep(0);
}
I applied the approach of Victor Stoddard to a splashScreen. I used it in the Form_Load event to fadeIn and FormClosing event to fadeOut.
NOTE: I had to set the form's opacity to 0 before call the fadeIn method.
Here you can see the order of events rised by a winform (lifecycle):
https://msdn.microsoft.com/en-us/library/86faxx0d(v=vs.110).aspx
private void Splash_Load(object sender, EventArgs e)
{
this.Opacity = 0.0;
FadeIn(this, 70);
}
private void Splash_FormClosing(object sender, FormClosingEventArgs e)
{
FadeOut(this, 30);
}
private async void FadeIn(Form o, int interval = 80)
{
//Object is not fully invisible. Fade it in
while (o.Opacity < 1.0)
{
await Task.Delay(interval);
o.Opacity += 0.05;
}
o.Opacity = 1; //make fully visible
}
private async void FadeOut(Form o, int interval = 80)
{
//Object is fully visible. Fade it out
while (o.Opacity > 0.0)
{
await Task.Delay(interval);
o.Opacity -= 0.05;
}
o.Opacity = 0; //make fully invisible
}
In the past, I've used AnimateWindow to fade in/out a generated form that blanks over my entire application in SystemColor.WindowColor.
This neat little trick gives the effect of hiding/swapping/showing screens in a wizard like interface. I've not done this sort of thing for a while, but I used P/Invoke in VB and ran the API in a thread of its own.
I know your question is in C#, but it's roughly the same. Here's some lovely VB I've dug out and haven't looked at since 2006! Obviously it would be easy to adapt this to fade your own form in and out.
<DllImport("user32.dll")> _
Public Shared Function AnimateWindow(ByVal hwnd As IntPtr, ByVal dwTime As Integer, ByVal dwFlags As AnimateStyles) As Boolean
End Function
Public Enum AnimateStyles As Integer
Slide = 262144
Activate = 131072
Blend = 524288
Hide = 65536
Center = 16
HOR_Positive = 1
HOR_Negative = 2
VER_Positive = 4
VER_Negative = 8
End Enum
Private m_CoverUp As Form
Private Sub StartFade()
m_CoverUp = New Form()
With m_CoverUp
.Location = Me.PointToScreen(Me.pnlMain.Location)
.Size = Me.pnlMain.Size
.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None
.BackColor = Drawing.SystemColors.Control
.Visible = False
.ShowInTaskbar = False
.StartPosition = System.Windows.Forms.FormStartPosition.Manual
End With
AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend) 'Blocks
Invoke(New MethodInvoker(AddressOf ShowPage))
End Sub
Private Sub EndFade()
AnimateWindow(m_CoverUp.Handle, 100, AnimateStyles.Blend Or AnimateStyles.Hide)
m_CoverUp.Close()
m_CoverUp = Nothing
End Sub
Victor Stoddard is close, but I found that the fade was more pleasing if it started the opacity increase fast and slowed as it approached full opacity of 1. Here's a slight modification to his code:
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class EaseInForm : Form
{
public EaseInForm()
{
InitializeComponent();
this.Opacity = 0;
}
private async void Form_Load(object sender, EventArgs e)
{
while (this.Opacity < 1.0)
{
var percent = (int)(this.Opacity * 100);
await Task.Delay(percent);
this.Opacity += 0.04;
}
}
}
This question already has answers here:
Showing a Windows form on a secondary monitor?
(9 answers)
Closed 9 years ago.
I have an application in which there is a form which I want to show on second screen.
Mean If application is running on screen A and when I click on menu to show Form it should display on Screen B
and same with if application is running on screen B and when I click on menu to show Form it should display on Screen A.
You need to use the Screen class to find a screen that the original form is not on, then set the second form's Location property based on that screen's Bounds.
For example:
var myScreen = Screen.FromControl(originalForm);
var otherScreen = Screen.AllScreens.FirstOrDefault(s => !s.Equals(myScreen))
?? myScreen;
otherForm.Left = otherScreen.WorkingArea.Left + 120;
otherForm.Top = otherScreen.WorkingArea.Top + 120;
This will work for any number of screens.
Note that it is possible that the video card is configured so that Windows sees one large screen instead of two smaller ones, in which case this becomes much more difficult.
Below is a function allowing you to display a form on any monitor. For your current scenario you can call this showOnMonitor(1);.
Essentially you have to get screen information from Screen.AllScreens and then get the dimensions of each, then place your form where you need it
function void showOnMonitor(int showOnMonitor)
{
Screen[] sc;
sc = Screen.AllScreens;
Form2 f = new Form2();
f.FormBorderStyle = FormBorderStyle.None;
f.Left = sc[showOnMonitor].Bounds.Left;
f.Top = sc[showOnMonitor].Bounds.Top;
f.StartPosition = FormStartPosition.Manual;
f.Show();
}
Note don't forget to do validation to ensure you actually have two screens etc else an exception will be thrown for accessing sc[showOnMonitor]
On the OnLoad method change the Location of the window.
protected void Form1_OnLoad(...) {
showOnMonitor(1);
}
private void showOnMonitor(int showOnMonitor)
{
Screen[] sc;
sc = Screen.AllScreens;
if (showOnMonitor >= sc.Length) {
showOnMonitor = 0;
}
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(sc[showOnMonitor].Bounds.Left, sc[showOnMonitor].Bounds.Top);
// If you intend the form to be maximized, change it to normal then maximized.
this.WindowState = FormWindowState.Normal;
this.WindowState = FormWindowState.Maximized;
}
I used this for an XNA 4 Dual Screen Application (Full Screen XNA Game Window + WinForm)
In the Form_Load() method, place the following code:
var primaryDisplay = Screen.AllScreens.ElementAtOrDefault(0);
var extendedDisplay = Screen.AllScreens.FirstOrDefault(s => s != primaryDisplay) ?? primaryDisplay;
this.Left = extendedDisplay.WorkingArea.Left + (extendedDisplay.Bounds.Size.Width / 2) - (this.Size.Width / 2);
this.Top = extendedDisplay.WorkingArea.Top + (extendedDisplay.Bounds.Size.Height / 2) - (this.Size.Height / 2);