PrintScreen KeyDown

joe_pool_is

Contributor
Joined
Jan 18, 2004
Messages
507
Location
Longview, TX [USA]
I have created some code to try to capture the Print Screen key, but I never have been able to get it to fire for either my MDI form nor any of the Child forms.

Here's what I have written:
Code:
void MdiForm_KeyDown(object sender, KeyEventArgs e) {
  if ((e.KeyCode == Keys.PrintScreen) || ((e.KeyData & Keys.PrintScreen) == Keys.PrintScreen)) {
    ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
    e.Handled = true;
  }
}
Anyway, other fires needed attention, and I forgot about it. I left a breakpoint on the Screen Capture line.

Today, while inputting some text in one of the Child forms, I had to insert an underscore ("_"). Imagine my shock when I hit that breakpoint!

I did a little debugging, and found that the Key Code contains the following:

e.KeyCode = LButton | MButton | Back | ShiftKey | Space | F17

The Key Data displays this whenever I mouse over it:

e.KeyData = LButton | MButton | Back | ShiftKey | Space | F17 | Shift

The Print Screen identifier contains this:

Keys.PrintScreen = MButton | Back | Space

From System.Windows.Forms.Keys (Using VS2008):
LButton = 1
RButton = 4
Back = 8
ShiftKey = 16
Space = 32
PrintScreen = 44
F17 = 128

I have coded some formatting fixes for lazy people and my program inserts and/or deletes some text, so I initially wasn't surprised by the Space and Back character being in the mix. It turns out, however, that my formatting is not what happens first.

Could someone tell me why I get this strange data instead of a Print Screen keypress?

At the least, whenever I press the Print Screen button, I would expect the MButton, Back, and Space values to all be included in the Key Code or Key Data - and certainly not whenever I call underscore (Shift + Hyphen).
 
I believe that this problem stems from the fact that the Keys enumeration is a combination of both flags and sequential constants. Generally an enum should be one or the other. Otherwise it becomes difficult to sort out what the value means.

The key code for the print screen key is actually 44. The "MButton | Back | Space" is actually Visual Studio trying to decode it as though it were flags. (If you call ToString on the KeyCode it will properly decode it.) What I believe is causing your particular problem as that you are also treating Keys as flags. The underscore key is actually primarily the minus key (OemMinus).

When you test the expression ((e.KeyData & Keys.PrintScreen) == Keys.PrintScreen)), if minus (underscore) is pressed, the comparison done is ((189 & 44) == 44), which is true. Remove this bit test and your problem should go away.
 
Thanks. I understood about the values being OR'ed together.

The easiest implementation was just using your ToString() suggestion. It seems to filter great!

Now I have this segment:

Code:
void MdiForm_KeyDown(object sender, KeyEventArgs e) {
  string val = e.KeyCode.ToString();
  if (val == "PrintScreen") {
  //if ((e.KeyCode == Keys.PrintScreen) || ((e.KeyData & Keys.PrintScreen) == Keys.PrintScreen)) {
    ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
    e.Handled = true;
  }
}

Note, however, that the Print Screen button will still not cause the event to fire. Maybe Microsoft is "protecting us" in the Framework. I hate that!
 
I find it to be extremely annoying that you can't easily access unfiltered key presses, but there is a way to get at them. When the Print Screen key is pressed, the virtual ProcessKeyMessage function is called on the control that contians focus. The tricky part that you have to be able to subclass the control and relay the message, and you may not be able to practically implement this if there is more than a few controls.

As for checking keys in the KeyDown event, you should just be able to directly compare to enumerations values, i.e.

C# Code[HorizontalRule]magic hidden text[/HorizontalRule]void MdiForm_KeyDown(object sender, KeyEventArgs e) {
··string val = e.KeyCode.ToString();
··if (e.KeyCode == Keys.PrintScreen) {
····ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
····e.Handled = true;
··}
}
[HorizontalRule]Why are you quoting me?[/HorizontalRule]
My point about ToString was just that Visual Studio was getting it wrong.
 
Thanks.

C#:
void Form1(object sender, EventArgs e) {
  Console.WriteLine("P.S. What do you use for your C# code formatter?");
  Console.WriteLine("This ('CS') works unless code contains brackets (i.e. arrays).");
}
 
I threw together a Q&D winforms app that picks out keywords, operators, and brackets and throws vB tags around them.
 
Just for completeness and to aid others (and myself if I ever have to revisit this), I was able to streamline the process somewhat using the guidance shown above into two small overrides that require "no wiring" to the form:
Code:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
  if (((Keys)(int)msg.WParam) == Keys.PrintScreen) {
    ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
  }
  return base.ProcessCmdKey(ref msg, keyData);
}

protected override bool ProcessKeyEventArgs(ref Message msg) {
  if (((Keys)(int)msg.WParam) == Keys.PrintScreen) {
    ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
  }
  return base.ProcessKeyEventArgs(ref msg);
}
Notice that the base process is not blocked! If I want to continue on to MS Paint and manually paste my screen capture, I can still do that, too.

Further, for those interested in how I handle the Screen Capture, here is my very generic ScreenCapture routine (open source for anyone to use) which drops the screen shot on the desktop using a simple BackgroundWorker that tries to display the screen capture after it has completed:
Code:
public void ScreenCapture(string initialDirectory) {
  using (BackgroundWorker worker = new BackgroundWorker()) {
    Thread.Sleep(0);
    this.Refresh();
    worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
      BackgroundWorker wkr = sender as BackgroundWorker;
      Rectangle bounds = new Rectangle(Location, Size);
      Thread.Sleep(300);
      Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
      using (Graphics g = Graphics.FromImage(bitmap)) {
        g.CopyFromScreen(Location, Point.Empty, bounds.Size);
      }
      e.Result = bitmap;
    };
    worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
      if (e.Error != null) {
        Exception err = e.Error;
        while (err.InnerException != null) {
          err = err.InnerException;
        }
        MessageBox.Show(err.Message, "Screen Capture", MessageBoxButtons.OK, MessageBoxIcon.Stop);
      } else if (e.Cancelled == true) {
      } else if (e.Result != null) {
        if (e.Result is Bitmap) {
          Bitmap bitmap = (Bitmap)e.Result;
          using (SaveFileDialog dlg = new SaveFileDialog()) {
            dlg.Title = "Image Capture: Image Name, File Format, and Destination";
            dlg.FileName = "Screenshot";
            dlg.InitialDirectory = initialDirectory;
            dlg.DefaultExt = "jpg";
            dlg.AddExtension = true;
            dlg.Filter = "Jpeg Image (JPG)|*.jpg|PNG Image|*.png|GIF Image (GIF)|*.gif|Bitmap (BMP)|*.bmp" +
              "|EWM Image|*.emf|TIFF Image|*.tiff|Windows Metafile (WMF)|*.wmf|Exchangable image file|*.exif";
            dlg.FilterIndex = 1;
            if (dlg.ShowDialog(this) == DialogResult.OK) {
              ImageFormat fmtStyle;
              switch (dlg.FilterIndex) {
                case 2: fmtStyle = ImageFormat.Jpeg; break;
                case 3: fmtStyle = ImageFormat.Gif; break;
                case 4: fmtStyle = ImageFormat.Bmp; break;
                case 5: fmtStyle = ImageFormat.Emf; break;
                case 6: fmtStyle = ImageFormat.Tiff; break;
                case 7: fmtStyle = ImageFormat.Wmf; break;
                case 8: fmtStyle = ImageFormat.Exif; break;
                default: fmtStyle = ImageFormat.Png; break;
              }
              bitmap.Save(dlg.FileName, fmtStyle);
              try {
                Process.Start(dlg.FileName);
              } catch (Exception) {
                try { // try IE
                  Process.Start("iexplore.exe", dlg.FileName);
                } catch (Exception) { }
              }
            }
          }
        }
      }
    };
    worker.RunWorkerAsync();
  }
}
Happy Trails!

The End
 
Back
Top