The execs at my company would like a particular custom control of our ui to make a best-effort at preventing screen capture. I implemented a slick solution using SetWindowDisplayAffinity and DWMEnableComposition at the top Window level of our application to prevent screen capture of the entire app but the previously mentioned execs weren't happy with that. They want only the particular UserControl to prevent screen capture, not the entire app.
The custom control is a .NET 2.0 Windows.Forms.UserControl wrapped in a 4.5 WPF WindowsFormsHost contained by a 4.5 WPF Window control.
Before I tell the execs where to go, I want to be certain there isn't a reasonable way to implement this.
So, my question is: How do I implement screen capture prevention of a .NET 2.0 UserControl?
Thanks
i had an idea:
the user click "PrintScreen", a capture from my program is copy to ClipBoard.
So, all what i need it: to catch cliboard an check if he contains an image.
if yes: i delete the image from clipboard.
the code:
//definition a timer
public static Timer getImageTimer = new Timer();
[STAThread]
static void Main()
{
getImageTimer.Interval = 500;
getImageTimer.Tick += GetImageTimer_Tick;
getImageTimer.Start();
......
}
private static void GetImageTimer_Tick(object sender, EventArgs e)
{
if (Clipboard.ContainsImage())//if clipboard contains an image
Clipboard.Clear();//delete
}
*but it avoid any time program is running. (you need to know if your program is foreground, and then check clipboard);
You might be able to capture the key stroke.
Below is a method I used for that, but it had limited success:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if ((m_parent != null) && m_parent.ScreenCapture(ref msg)) {
return true;
} else {
return base.ProcessCmdKey(ref msg, keyData);
}
}
protected override bool ProcessKeyEventArgs(ref Message msg) {
if ((m_parent != null) && m_parent.ScreenCapture(ref msg)) {
return true;
} else {
return base.ProcessKeyEventArgs(ref msg);
}
}
Returning "True" was supposed to tell the system that the PrintScreen routine was handled, but it often still found the data on the clipboard.
What I did instead, as you can see from the call to m_parent.ScreenCapture, was to save the capture to a file, then display it in my own custom image viewer.
If it helps, here is the wrapper I created for the custom image viewer in m_parent.ScreenCapture:
public bool ScreenCapture(ref Message msg) {
var WParam = (Keys)msg.WParam;
if ((WParam == Keys.PrintScreen) || (WParam == (Keys.PrintScreen & Keys.Alt))) {
ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
msg = new Message(); // erases the data
return true;
}
return false;
}
public void ScreenCapture(string initialDirectory) {
this.Refresh();
var rect = new Rectangle(Location.X, Location.Y, Size.Width, Size.Height);
var imgFile = Global.ScreenCapture(rect);
//string fullName = null;
string filename = null;
string extension = null;
if ((imgFile != null) && imgFile.Exists) {
filename = Global.GetFilenameWithoutExt(imgFile.FullName);
extension = Path.GetExtension(imgFile.FullName);
} else {
using (var worker = new BackgroundWorker()) {
worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
Thread.Sleep(300);
var bmp = new Bitmap(rect.Width, rect.Height);
using (var g = Graphics.FromImage(bmp)) {
g.CopyFromScreen(Location, Point.Empty, rect.Size); // WinForm Only
}
e.Result = bmp;
};
worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
var err = e.Error;
while (err.InnerException != null) {
err = err.InnerException;
}
MessageBox.Show(err.Message, "Screen Capture", MessageBoxButtons.OK, MessageBoxIcon.Stop);
} else if (e.Cancelled) {
} else if (e.Result != null) {
if (e.Result is Bitmap) {
var bitmap = (Bitmap)e.Result;
imgFile = new FileInfo(Global.GetUniqueFilenameWithPath(m_screenShotPath, "Screenshot", ".jpg"));
filename = Global.GetFilenameWithoutExt(imgFile.FullName);
extension = Path.GetExtension(imgFile.FullName);
bitmap.Save(imgFile.FullName, ImageFormat.Jpeg);
}
}
};
worker.RunWorkerAsync();
}
}
if ((imgFile != null) && imgFile.Exists && !String.IsNullOrEmpty(filename) && !String.IsNullOrEmpty(extension)) {
bool ok = false;
using (SaveFileDialog dlg = new SaveFileDialog()) {
dlg.Title = "ACP Image Capture: Image Name, File Format, and Destination";
dlg.FileName = filename;
dlg.InitialDirectory = m_screenShotPath;
dlg.DefaultExt = extension;
dlg.AddExtension = true;
dlg.Filter = "PNG Image|*.png|Jpeg Image (JPG)|*.jpg|GIF Image (GIF)|*.gif|Bitmap (BMP)|*.bmp" +
"|EWM Image|*.emf|TIFF Image|*.tif|Windows Metafile (WMF)|*.wmf|Exchangable image file|*.exif";
dlg.FilterIndex = 0;
if (dlg.ShowDialog(this) == DialogResult.OK) {
imgFile = imgFile.CopyTo(dlg.FileName, true);
m_screenShotPath = imgFile.DirectoryName;
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;
}
ok = true;
}
}
if (ok) {
string command = string.Format(#"{0}", imgFile.FullName);
try { // try default image viewer
var psi = new ProcessStartInfo(command);
Process.Start(psi);
} catch (Exception) {
try { // try IE
ProcessStartInfo psi = new ProcessStartInfo("iexplore.exe", command);
Process.Start(psi);
} catch (Exception) { }
}
}
}
}
I wrote that years ago, and I don't work there now. The source code is still in my Cloud drive so that I can leverage skills I learned once (and forgot).
That code isn't meant to get you completely done, but just to show you a way of doing it. If you need any help with something, let me know.
Not a silver bullet, but could form part of a strategy...
If you were worried about the windows snipping tool or other known snipping tools, you could subscribe to Windows events to capture a notification of when SnippingTool.exe was launched and then hide your application until it was closed:
var applicationStartWatcher = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
applicationStartWatcher.EventArrived += (sender, args) =>
{
if (args.NewEvent.Properties["ProcessName"].Value.ToString().StartsWith("SnippingTool"))
{
Dispatcher.Invoke(() => this.Visibility = Visibility.Hidden);
}
};
applicationStartWatcher.Start();
var applicationCloseWatcher = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
applicationCloseWatcher.EventArrived += (sender, args) =>
{
if (args.NewEvent.Properties["ProcessName"].Value.ToString().StartsWith("SnippingTool"))
{
Dispatcher.Invoke(() =>
{
this.Visibility = Visibility.Visible;
this.Activate();
});
}
};
applicationCloseWatcher.Start();
You will probably need to be running as an administrator for this code to work.
ManagementEventWatcher is in the System.Management assembly.
This coupled with some strategy for handling the print screen key: for example something like this may at least make it difficult to screen capture.
Related
I'm trying to update the GUI, and I have an asynchronous function that uses LoadAsyc(), when I load just one image, it works but when I try to load more than one, the second one doesn't display.
This my code:
public UserFriendlyInterface()
{
InitializeComponent();
locationFileH5 = "";
serverStatus = false;
ipAddress = getLocalIPAddress();
port = 5000;
watcher = new FileSystemWatcher(#"flask_server\cnn\_prepImages_");
watcher.EnableRaisingEvents = true;
watcher.Changed += watcher_Changed;
}
private void watcher_Changed(object sender, FileSystemEventArgs e)
{
updateImages();
}
async Task updateImages()
{
pictureBoxNormalImg.WaitOnLoad = false;
pictureBoxNormalImg.LoadAsync(#"flask_server\cnn\_prepImages_\normal.jpg");
pictureBoxSegmentation.WaitOnLoad = false;
pictureBoxSegmentation.LoadAsync(#"flask_server\cnn\_prepImages_\segmentation.jpg");
}
What you are trying to achieve can be achieved more robustly by querying the Name property of the FileSystemEventArgs object, and updating only the corresponding PictureBox.
private static void Watcher_Changed(object sender, FileSystemEventArgs e)
{
PictureBox pictureBox;
switch (e.Name.ToLowerInvariant())
{
case "normal.jpg": pictureBox = pictureBoxNormalImg; break;
case "segmentation.jpg": pictureBox = pictureBoxSegmentation; break;
default: pictureBox = null; break;
}
if (pictureBox != null)
{
Image image = null;
try
{
using (var temp = new Bitmap(e.FullPath))
{
image = new Bitmap(temp);
}
}
catch { } // Swallow exception
if (image != null)
{
pictureBox.Invoke((MethodInvoker)(delegate ()
{
pictureBox.Image = image;
}));
}
}
}
I would avoid the LoadAsync method because it is intended mainly for loading images from the internet, and because I don't totally trust it.
Update: There were two problems with my initial code:
1) Free file locked by new Bitmap(filePath)
2) FileSystemWatcher Changed event is raised twice
The updated code solves these problems (hopefully), but not in the most robust or efficient way possible.
Update: To make the code more efficient, by avoiding the repeated loading of the images caused by multiple firings of the Changed event, you could use the extension method OnChanged found in this answer. It suffices to replace the line below:
watcher.Changed += Watcher_Changed;
...with this one:
watcher.OnChanged(Watcher_Changed, 100);
I'm trying to figure out how I can send data to my WinForm Component without always having to reply on a message sent by the WinForm. I know one can set an AppService name, etc. in the package.appxmanifest of the UWP App. But, what is the equivaliant of this in a Win32 environment like WinForms.
Is any code needed to assist with getting an answer?
Thank you
Edit
Currently, my UWP App is responding to messages sent from my WinForm Component every couple of milliseconds.
App.xaml.cs
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails)
{
appServiceDeferral = args.TaskInstance.GetDeferral();
args.TaskInstance.Canceled += OnTaskCanceled; // Associate a cancellation handler with the background task.
AppServiceTriggerDetails details = args.TaskInstance.TriggerDetails as AppServiceTriggerDetails;
Connection = details.AppServiceConnection;
Connection.RequestReceived += (new MainPage()).Connection_OnRequestReceived;
}
}
MainPage.xaml.cs
public async void Connection_OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
// write setting to stop timer
localSettings.Values["Win32Working"] = "True";
// read content
if (args.Request.Message.ContainsKey("content"))
{
object message = null;
args.Request.Message.TryGetValue("content", out message);
// if message is an int[]
if (message is int[])
{
// init field vars
int indexInArray = 0;
bool newTest1On = false;
bool newTest2On = false;
bool newTest3On = false;
foreach (int trueorfalse in (int[])message)
{
// set bool state based on index
switch (indexInArray)
{
case 0:
newCapsOn = Convert.ToBoolean(trueorfalse);
localSettings.Values["Test1"] = (Convert.ToBoolean(trueorfalse)).ToString();
break;
case 1:
newNumOn = Convert.ToBoolean(trueorfalse);
localSettings.Values["Test2"] = (Convert.ToBoolean(trueorfalse)).ToString();
break;
case 2:
newScrollOn = Convert.ToBoolean(trueorfalse);
localSettings.Values["Test3"] = (Convert.ToBoolean(trueorfalse)).ToString();
break;
default:
break;
}
indexInArray++;
}
if (newTest1On != Test1On || newTest2On != Test2On || newTest3On != Test3On)
localSettings.Values["updateUI"] = true.ToString();
// update bools
Test1On = newTest1On;
Test2On = newTest2On;
Test3On = neTest3On;
// if exit requested
if (Convert.ToBoolean(localSettings.Values["sendExit"]))
{
// tell WinForm to exit
ValueSet messageExit = new ValueSet();
messageExit.Add("exit", null);
AppServiceResponseStatus responseStatus = await args.Request.SendResponseAsync(messageExit);
localSettings.Values["sendExit"] = false.ToString();
localSettings.Values["Win32Working"] = false.ToString();
}
}
}
else if (args.Request.Message.ContainsKey("request"))
{
if (!Convert.ToBoolean(localSettings.Values["sendExit"]))
{
// send current settings as response
AppServiceResponseStatus responseStatus = await args.Request.SendResponseAsync(UpdateWin32());
}
else
{
// tell WinForm to exit
ValueSet message = new ValueSet();
message.Add("exit", null);
AppServiceResponseStatus responseStatus = await args.Request.SendResponseAsync(message);
localSettings.Values["sendExit"] = false.ToString();
localSettings.Values["Win32Working"] = false.ToString();
}
}
else if (args.Request.Message.ContainsKey("exit"))
{
// exit
Application.Current.Exit();
}
}
My WinForm Code:
private async void threadCommunicationWinFrmAndUWP_Run()
{
bool askForInfo = false;
DoWork:
ValueSet message = new ValueSet();
if (!askForInfo) { message.Add("content", notifyIconsLogic.GetStatuses()); askForInfo = true; }
else { message.Add("request", ""); askForInfo = false; }
#region SendToUWP
// if connection isn't inited
if (connection == null)
{
// init
connection = new AppServiceConnection();
connection.PackageFamilyName = Package.Current.Id.FamilyName;
connection.AppServiceName = "NotifyIconsUWP";
// attempt connection
AppServiceConnectionStatus connectionStatus = await connection.OpenAsync();
// if UWP isn't running
if (connectionStatus == AppServiceConnectionStatus.AppUnavailable) return;
}
AppServiceResponse serviceResponse = await connection.SendMessageAsync(message);
// if UWP isn't running
if (serviceResponse.Status == AppServiceResponseStatus.Failure) return;
// get response
if (serviceResponse.Message.ContainsKey("content"))
{
object newMessage = null;
serviceResponse.Message.TryGetValue("content", out newMessage);
// if message is an int[]
if (newMessage is int[])
{
// init field vars
int indexInArray = 0;
bool showTest1 = false;
bool showTest2 = false;
bool showTest3 = false;
foreach (int trueorfalse in (int[])newMessage)
{
// set bool state based on index
switch (indexInArray)
{
case 0:
showTest1 = Convert.ToBoolean(trueorfalse);
break;
case 1:
showTest2 = Convert.ToBoolean(trueorfalse);
break;
case 2:
showTest3 = Convert.ToBoolean(trueorfalse);
break;
default:
break;
}
indexInArray++;
}
notifyIconsLogic.SetChecker(showTest1, showTest2, showTest3);
}
}
if (serviceResponse.Message.ContainsKey("exit")) Exit();
#endregion
goto DoWork;
}
This ramps up the CPU Usage excessively. The point is, my only way of getting information to the WinForm from the UWP App, currently, is by responding to messages sent in a loop.
Question
How can I send messages from the UWP App to the WinForm without only responding to the messages sent in a loop? Because, I want to eliminate the reliance on the loop (CPU Purposes).
In other words: how do I get the results of the package.appxmanifest to 'work' in WinForms?
package.appxmanifest
<Extensions>
<uap:Extension Category="windows.appService">
<uap:AppService Name="NotifyIconsUWP" />
</uap:Extension>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Win32\NotifyIconsComponent.exe" />
</Extensions>
You should be able to send a request on the established app service connection from UWP app like this:
AppServiceResponse response = await App.Connection.SendMessageAsync(valueSet);
And then receive this message in the Windows Forms app by attaching an event handler:
connection.RequestReceived += Connection_RequestReceived;
Check out this sample which demonstrates two way communication between Console app and UWP app.
I need to find out if my pictureboxes have loaded images.
I create them and give them ImageLocation. Pictures DO load after some time but I need to check if it is loaded. I tried using
if(pb.ImageLocation != null){
Console.WriteLine("Loaded!");
}
But this shows that its loaded even if it actually isn't. Also I have a bunch of dynamic created pictureboxes:
void CreateBrick(int x,int y)
{
bricks[i] = new PictureBox();
bricks[i].Name = "pb_b" + i.ToString();
bricks[i].Location = new Point(y, x);
bricks[i].Size = new Size(60, 60);
bricks[i].ImageLocation = #"Images/brick_wall.jpg";
pb_bg.Controls.Add(bricks[i]);
brick.Add(bricks[i]);
i++;
}
And I have no idea how to check these...
The problem in your code is that you don't call the Load or LoadAsync method.
void CreateBrick(int x,int y)
{
bricks[i] = new PictureBox();
bricks[i].Name = "pb_b" + i.ToString();
bricks[i].Location = new Point(y, x);
bricks[i].Size = new Size(60, 60);
// You can pass the path directly to the Load method
// bricks[i].ImageLocation = #"Images/brick_wall.jpg";
bricks[i].Load(#"Images/brick_wall.jpg");
pb_bg.Controls.Add(bricks[i]);
brick.Add(bricks[i]);
i++;
}
If you use Load method then then the image is loaded after the call, if you use LoadAsync you could add the event handler for the LoadComplete event.
bricks[i].LoadCompleted += onLoadComplete;
bricks[i].LoadAsync(#"Images/brick_wall.jpg");
....
private void onLoadComplete(Object sender, AsyncCompletedEventArgs e)
{
// Don't forget to check if the image has been really loaded,
// this event fires also in case of errors.
if (e.Error == null && !e.Cancelled)
Console.WriteLine("Image loaded");
else if (e.Cancelled)
Console.WriteLine("Load cancelled");
else
Console.WriteLine("Error:" + e.Error.Message);
}
If you want to use the LoadAsync approach you still have to solve the problem how to match the complete load of a particular image to the related picture box. This could be solved using the sender parameter of the LoadAsync. This sender parameter is the PictureBox who has completed the load of the image.
You can use the Tag property and set it to "1" to mark your picturebox as loaded and to the error message in case of problems.
private void onLoadComplete(Object sender, AsyncCompletedEventArgs e)
{
PictureBox pic = sender as PictureBox;
// Don't forget to check if the image has been really loaded,
// this event fires also in case of errors.
if (e.Error == null && !e.Cancelled)
{
pic.Tag = "1";
Console.WriteLine("Image loaded");
}
else
{
pic.Tag = e.Error.Message;
Console.WriteLine("Cancelled:" + e.Error.Message);
}
}
After this the pictureboxes in your bricks arrays have their Tag property marked as "1" for the loaded ones and with an error message for the ones with an error.
Could you try using LoadCompleted event of PictureBox after you assign a ImageLocation as the way it is described here
You can make sure images are loaded asynchronously of course.
pb.WaitOnLoad = false;
Then load the image asynchronously:
pb.LoadAsync("some.gif");
For more from stackoverflow you can have a look here and here
Assign an event handler like the following:
pb.LoadCompleted += PictureBox1_LoadCompleted;
Sample event handler right from msdn:
private void PictureBox1_LoadCompleted(Object sender, AsyncCompletedEventArgs e) {
System.Text.StringBuilder messageBoxCS = new System.Text.StringBuilder();
messageBoxCS.AppendFormat("{0} = {1}", "Cancelled", e.Cancelled );
messageBoxCS.AppendLine();
messageBoxCS.AppendFormat("{0} = {1}", "Error", e.Error );
messageBoxCS.AppendLine();
messageBoxCS.AppendFormat("{0} = {1}", "UserState", e.UserState );
messageBoxCS.AppendLine();
MessageBox.Show(messageBoxCS.ToString(), "LoadCompleted Event" );
}
private stattic bool CheckUplodedImage()
{
bool return = false;
try
{
PictureBox imageControl = new PictureBox();
imageControl.Width = 60;
imageControl.Height = 60;
Bitmap image = new Bitmap("Images/brick_wall.jpg");
imageControl.Image = (Image)image;
Controls.Add(imageControl);
return true;
}
catch(Exception ex)
{
return false;
}
}
can check of its return
bool isUploded = CheckUplodedImage();
if(isUploded)
{
\\ ...uploaded
\\ Perform Operation
}
else
\\ not uploaded
My utility is supposed to resize either .jpg or .png files.
It seems to work fine in one location (at work, where I don't have IrfanView installed). But at home, when I open a *.jpg and then save it (resized), I see:
However, the image still displays fine in either case (whether I select "Yes" or "No" in the dialog.
IOW, I'm able to load and save both jpgs and pngs, and they save as such, and display fine. But IrfanView claims they are messed up.
Actually, I'm not sure how the image is saved; I was assuming it just saved it in the proper format "behind the scenes" based on the extension. Anyway, as this is a rather simple utility, I will just show all the code:
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace FileResizingUtil
{
public partial class FormFileResizer : Form
{
private Image _imgToResize;
String _originalFilename = String.Empty;
public FormFileResizer()
{
InitializeComponent();
}
private void buttonChooseImage_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog
{
InitialDirectory = "c:\\",
Filter = "JPG files (*.jpg)|*.jpg| PNG files (*.png)|*.png", FilterIndex = 2, RestoreDirectory = true
};
if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
_originalFilename = ofd.FileName;
_imgToResize = Image.FromFile(_originalFilename);
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
// If made it to here, it must be good
String preamble = labelImgSelected.Text;
labelImgSelected.Text = String.Format("{0}{1}", preamble, _originalFilename);
textBoxOrigHeight.Text = _imgToResize.Height.ToString();
textBoxOrigWidth.Text = _imgToResize.Width.ToString();
buttonApplyPercentageChange.Enabled = true;
//buttonResizeImage.Enabled = true;
}
private void buttonResizeImage_Click(object sender, EventArgs e)
{
// Really large images take awhile, so show an hourglass
Cursor.Current = Cursors.WaitCursor;
try
{
var size = new Size { Height = Convert.ToInt32(textBoxNewHeight.Text), Width = int.Parse(textBoxNewWidth.Text) };
// Two different ways of getting the int val
Image resizedImg = FileResizeUtils.GetResizedImage(_imgToResize, size);
String fileNameSansExtension = Path.GetFileNameWithoutExtension(_originalFilename);
String fileNameExtension = Path.GetExtension(_originalFilename);
String newFilename = String.Format("{0}{1}_{2}{3}", fileNameSansExtension, size.Height, size.Width, fileNameExtension);
// If used a different extension (jpg where the original was png, or vice versa) would the Save be intelligent enough to actually save in the other format?
resizedImg.Save(newFilename);
MessageBox.Show(String.Format("Done! File saved as {0}", newFilename));
Recycle();
}
finally
{
Cursor.Current = Cursors.Default;
}
}
private void Recycle()
{
buttonResizeImage.Enabled = false;
buttonApplyPercentageChange.Enabled = false;
labelImgSelected.Text = "Image selected: ";
textBoxOrigHeight.Text = String.Empty;
textBoxOrigWidth.Text = String.Empty;
// Retain the percentage vals, as it may be that all in a batch need to be the same pair of vals
}
private void buttonApplyPercentageChange_Click(object sender, EventArgs e)
{
int origHeight = _imgToResize.Height;
int origWidth = _imgToResize.Width;
// Two ways to convert the val
double heightFactor = (double)numericUpDownHeight.Value / 100.0;
double widthFactor = Convert.ToDouble(numericUpDownWidth.Value) / 100.0;
if (heightFactor < 0 || widthFactor < 0)
{
// show an error - no negative values allowed- using updown, so that should not be possible
}
var newHeight = Convert.ToInt32(origHeight * heightFactor);
var newWidth = Convert.ToInt32(origWidth * widthFactor);
textBoxNewHeight.Text = newHeight.ToString();
textBoxNewWidth.Text = newWidth.ToString();
buttonResizeImage.Enabled = true;
}
private void textBoxNewHeight_TextChanged(object sender, EventArgs e)
{
EnableResizeButtonIfValidDimensionsEntered();
}
private void EnableResizeButtonIfValidDimensionsEntered()
{
if (String.IsNullOrWhiteSpace(textBoxOrigHeight.Text)) return;
String candidateHeight = textBoxNewHeight.Text;
String candidateWidth = textBoxNewWidth.Text;
int validHeight;
int validWidth;
buttonResizeImage.Enabled = (int.TryParse(candidateHeight, out validHeight)) &&
(int.TryParse(candidateWidth, out validWidth));
}
private void numericUpDownHeight_ValueChanged(object sender, EventArgs e)
{
if (checkBoxRetainRatio.Checked)
{
numericUpDownWidth.Value = numericUpDownHeight.Value;
}
}
private void numericUpDownWidth_ValueChanged(object sender, EventArgs e)
{
if (checkBoxRetainRatio.Checked)
{
numericUpDownHeight.Value = numericUpDownWidth.Value;
}
}
}
}
..and the GUI (just prior to hitting the "Resize Image" button:
UPDATE
Based on Eugene Sh.'ls comment, I changed my Save method to the following block:
bool success = true;
. . .
if (fileNameExtension != null && fileNameExtension.ToLower().Contains("jpg"))
{
resizedImg.Save(newFilename, ImageFormat.Jpeg);
}
else if (fileNameExtension != null &&
fileNameExtension.ToLower().Contains("png"))
{
resizedImg.Save(newFilename, ImageFormat.Png);
}
else
{
success = false;
}
if (success)
{
MessageBox.Show(String.Format("Done! File saved as {0}", newFilename));
}
else
{
MessageBox.Show("Something went awry. The file was not saved");
}
UPDATE 2
So here is my new code, implementing the suggestion, and supporting several new file types:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
namespace FileResizingUtil
{
public partial class FormFileResizer : Form
{
private Image _imgToResize;
String _originalFilename = String.Empty;
public FormFileResizer()
{
InitializeComponent();
}
private void buttonChooseImage_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog
{
InitialDirectory = "c:\\",
Filter = "JPG files (*.jpg)|*.jpg| PNG files (*.png)|*.png| BMP files (*.bmp)|*.bmp| TIFF files (*.tiff)|*.png| ICO files (*.ico)|*.ico| EMF files (*.emf)|*.emf| WMF files (*.wmf)|*.wmf",
FilterIndex = 1, RestoreDirectory = true
};
if (ofd.ShowDialog() == DialogResult.OK)
{
try
{
_originalFilename = ofd.FileName;
_imgToResize = Image.FromFile(_originalFilename);
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
if (String.IsNullOrWhiteSpace(_originalFilename)) return;
// If made it to here, it must be good
String preamble = labelImgSelected.Text;
labelImgSelected.Text = String.Format("{0}{1}", preamble, _originalFilename);
textBoxOrigHeight.Text = _imgToResize.Height.ToString();
textBoxOrigWidth.Text = _imgToResize.Width.ToString();
buttonApplyPercentageChange.Enabled = true;
}
private void buttonResizeImage_Click(object sender, EventArgs e)
{
bool success = true;
// Really large images take awhile, so show an hourglass
Cursor.Current = Cursors.WaitCursor;
try
{
// Two different ways of getting the int val
var size = new Size { Height = Convert.ToInt32(textBoxNewHeight.Text), Width = int.Parse(textBoxNewWidth.Text) };
Image resizedImg = FileResizeUtils.GetResizedImage(_imgToResize, size);
String fileNameSansExtension = Path.GetFileNameWithoutExtension(_originalFilename);
String fileNameExtension = Path.GetExtension(_originalFilename);
String newFilename = String.Format("{0}{1}_{2}{3}", fileNameSansExtension, size.Height, size.Width, fileNameExtension);
if (fileNameExtension != null && fileNameExtension.ToLower().Contains("jpg"))
{
resizedImg.Save(newFilename, ImageFormat.Jpeg);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("png"))
{
resizedImg.Save(newFilename, ImageFormat.Png);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("bmp"))
{
resizedImg.Save(newFilename, ImageFormat.Bmp);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("ico"))
{
resizedImg.Save(newFilename, ImageFormat.Icon);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("tiff"))
{
resizedImg.Save(newFilename, ImageFormat.Tiff);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("emf"))
{
resizedImg.Save(newFilename, ImageFormat.Emf);
}
else if (fileNameExtension != null && fileNameExtension.ToLower().Contains("wmf"))
{
resizedImg.Save(newFilename, ImageFormat.Wmf);
}
else
{
success = false;
}
MessageBox.Show(success
? String.Format("Done! File saved as {0}", newFilename)
: "Something went awry. The file was not saved");
Recycle();
}
finally
{
Cursor.Current = Cursors.Default;
}
}
private void Recycle()
{
buttonResizeImage.Enabled = false;
buttonApplyPercentageChange.Enabled = false;
labelImgSelected.Text = "Image selected: ";
textBoxOrigHeight.Text = String.Empty;
textBoxOrigWidth.Text = String.Empty;
// Retain the percentage vals, as it may be that all in a batch need to be the same pair of vals
}
private void buttonApplyPercentageChange_Click(object sender, EventArgs e)
{
int origHeight = _imgToResize.Height;
int origWidth = _imgToResize.Width;
// Two ways to convert the val
double heightFactor = (double)numericUpDownHeight.Value / 100.0;
double widthFactor = Convert.ToDouble(numericUpDownWidth.Value) / 100.0;
if (heightFactor < 0 || widthFactor < 0)
{
// show an error - no negative values allowed- using updown, so that should not be possible
}
var newHeight = Convert.ToInt32(origHeight * heightFactor);
var newWidth = Convert.ToInt32(origWidth * widthFactor);
textBoxNewHeight.Text = newHeight.ToString();
textBoxNewWidth.Text = newWidth.ToString();
buttonResizeImage.Enabled = true;
}
private void textBoxNewHeight_TextChanged(object sender, EventArgs e)
{
EnableResizeButtonIfValidDimensionsEntered();
}
private void EnableResizeButtonIfValidDimensionsEntered()
{
if (String.IsNullOrWhiteSpace(textBoxOrigHeight.Text)) return;
String candidateHeight = textBoxNewHeight.Text;
String candidateWidth = textBoxNewWidth.Text;
int validHeight;
int validWidth;
buttonResizeImage.Enabled = (int.TryParse(candidateHeight, out validHeight)) &&
(int.TryParse(candidateWidth, out validWidth));
}
private void numericUpDownHeight_ValueChanged(object sender, EventArgs e)
{
if (checkBoxRetainRatio.Checked)
{
numericUpDownWidth.Value = numericUpDownHeight.Value;
}
}
private void numericUpDownWidth_ValueChanged(object sender, EventArgs e)
{
if (checkBoxRetainRatio.Checked)
{
numericUpDownHeight.Value = numericUpDownWidth.Value;
}
}
}
}
From the Image.Save documentation:
If no encoder exists for the file format of the image, the Portable
Network Graphics (PNG) encoder is used. When you use the Save method
to save a graphic image as a Windows Metafile Format (WMF) or Enhanced
Metafile Format (EMF) file, the resulting file is saved as a Portable
Network Graphics (PNG) file. This behavior occurs because the GDI+
component of the .NET Framework does not have an encoder that you can
use to save files as .wmf or .emf files.
If you want to save in a different format, use the overloaded Save method, taking format as a second parameter:
Save(String, ImageFormat)
Most image viewers don't use the extension of the file to determine the type of the file, but use so called "magic numbers" (http://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files). They basically check the first X bytes of the file wich is often unique to a specific image format.
My guess is, that the library you're using saves the file as PNG as default (edit: see Eugenes answer), not considering what extension you put there. IrfanView notices, that the magic number and the extension don't match but still shows the image by defaulting to the magic number.
Go to a console and print out the file. If it is a PNG file, you will see PNG displayed and it stops.
If is is JPEG, you will get a lot of garbage but should see EXIF or JFIF at the top. The very start is FF D8
Because the JPEG and PNG have different signatures, the application can tell them apart from their contents and invite the appropriate decoder.
Image applications normally identify the type of image from the contents of the stream, not the extension.
I have another WPF Window i've created.. say window2.xaml. I have a button.. and on click i want it to load that window.. i've tried Googling but nothing seems to be working. It just loads a blank page. I know this is really simple, but I really can't find how to do it through my searches.
This is what i have tried:
GameClock temp = new GameClock();
temp.ShowDialog(); //just shows blank window
temp.Show(); //just shows a blank window too
EDIT: I figured out the problem. I took out the initialize component because there an an error. I thought it was something only the main window needed. When I put it back, it works. Thanks, everyone.
try this... u can use like a generic methode
private void button_ItemClick(object sender, ItemClickEventArgs e)
{
try
{
OpenWin("window2", new Uri(#"window2.xaml", UriKind.Relative), "Window2Label");
}
catch (Exception ex)
{
Message.Show(ex);
}
}
public static DocumentPanel OpenWin(string namePainelItem, Uri xamlPath, string caption = "", RoutedEventHandler unloadEvent = null, bool closeOpenWin = false)
{
try
{
if (closeOpenWin)
{
CloseWin(namePainelItem, false);
}
DocumentPanel panel1 = GetWin(namePainelItem);
if (panel1 == null)
{
panel1 = new DocumentPanel();
panel1.Caption = caption;
panel1.Name = namePainelItem;
panel1.Content = xamlPath;
if (unloadEvent != null)
{
panel1.Unloaded += unloadEvent;
}
hdl.dockLayoutManager.DockController.Insert(hdl.documentGroup1, panel1, 1);
hdl.dockLayoutManager.DockController.ActiveItem = panel1;
}
else
{
if (panel1.Visibility != Visibility.Visible)
panel1.Visibility = Visibility.Visible;
if(panel1.IsClosed)
panel1.Closed = false;
hdl.dockLayoutManager.DockController.ActiveItem = panel1;
}
return panel1;
}
catch (Exception ex)
{
Message.Show(ex);
}
return new DocumentPanel();
}
public static void CloseWin(string namePainelItem)
{
try
{
BaseLayoutItem item = hdl.dockLayoutManager.GetItem(namePainelItem);
if (item != null)
{
hdl.documentGroup1.Items.Remove(item);
hdl.dockLayoutManager.DockController.RemovePanel((DocumentPanel)item);
item = null;
}
}
catch (Exception ex)
{
Message.Show(ex);
}
}
You may need to read the XAML file before creating the instance of GameClock. Something like this:
GameClock clock;
FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
clock = (GameClock)XamlReader.Load(fs);
JAB