Internship (WPF) has been more than two weeks of time, stepping on some pits, but also accumulated some small things, ready to slowly take out to share. ('? ') )
This time, we'll talk about a simple implementation of the electronic signature board.
First on the picture (PS: The word is more ugly, do not care too much ha):
1. Mission Objectives
Basic requirements: 1. Signature function 2. Clear Signature 3. Save the signature (let the user select a folder, the signature is saved as a PNG format picture)
Try extra features: 1. Ctrl + Z Implementation undo function 2.Ctrl + y implement Redo function 3. Open the file location and select the file after saving the signature
2. Doing things
1) UI aspects
, Overall, a InkCanvas plus two buttons solves the problem.
A. InkCanvas
<InkCanvasGrid.column= "1"Grid.Row= "1"Background= "White"Height= "+"Name= "Ink"> <inkcanvas.defaultdrawingattributes> <DrawingAttributesColor= "#FF000000"Stylustip= "Ellipse"Height= "6"Width= "6"ignorepressure= "False"Fittocurve= "False"> <!--Adjust Brush shape - <Drawingattributes.stylustiptransform> <!--Https://msdn.microsoft.com/library/system.windows.media.matrix (v=vs.110). aspx - <MatrixM11= "1"M12= "0"M21= "0"M22= "1"OffsetX= "0"OffsetY= "0"/> </Drawingattributes.stylustiptransform> </DrawingAttributes> </inkcanvas.defaultdrawingattributes></InkCanvas>
The part about adjusting the shape of the brush (yes, that matrix) is not very well known to me personally, so I do not explain it, and interested children's shoes can access the corresponding Microsoft official documents to view the relevant information.
B. Button
<Buttonx:name= "Btnclearsign"HorizontalAlignment= "Left"VerticalAlignment= "Top"Width= "+"Padding= "0"Margin= "12,6,0,0"Click= "Btnclearsign_click"> <button.template> <ControlTemplate> <Grid> <LabelCursor= "Hand"Foreground= "Red"FontFamily= "Microsoft yahei UI"FontSize= " the"> <Underline> <RunText= "Clear Signature"></Run> </Underline> </Label> </Grid> </ControlTemplate> </button.template></Button>
The two buttons in the diagram are the same routine, so just show the code for one button. (PS: To make the button appear not too vulgar, we have a hyperlink-like style for the button)
2) Logic Code
Signature function We don't have to worry about it, InkCanvas will take care of it.
A. Clear signatures
Ink. Strokes.clear ();
This line of code is sufficient. Note that the ink here is the InkCanvas that we wrote in the UI section.
B. Saving a signature as a PNG image
//determine if there is content in the signature boardif(Ink. Strokes.any ()) {//let users choose Folders to save themselves//you need to add a reference to the System.Windows.Forms in your project//References = Add Reference = Check System.Windows.Forms item = OK varFolderpicker =NewFolderBrowserDialog (); varres =Folderpicker.showdialog (); //determine if the user has a folder selected if(res = = System.Windows.Forms.DialogResult.Cancel)return; //File Save path varFolderPath =Folderpicker.selectedpath; varFileName = DateTime.Now.ToString ("YYYYMMDDHHMMSS"); varFileuri = FolderPath +"\\"+ FileName +". PNG"; //The default DPI appears to be 96 under the Windows system, but the current native test considers the DPI setting to be 72 more appropriate//the size of the DPI will directly affect the integrity of the signature preservation results, about the DPI of the knowledge on the Internet or more, please do not know for yourselves//The third parameter of the next line of code determines the horizontal dpi of the bitmap, and the fourth parameter is the vertical dpi varRenderbitmap =NewRenderTargetBitmap ((int) Ink. ActualWidth, (int) Ink. ActualHeight, 72d, 72d, pixelformats.pbgra32); Renderbitmap.render (Ink); using(varstream =NewFileStream (Fileuri, FileMode.Create)) { varEncoder =NewPngbitmapencoder (); Encoder. Frames.add (Bitmapframe.create (Renderbitmap)); Encoder. Save (stream); } undolist.clear (); //Where to open the signature fileFileutil.locatefile (Fileuri);}Else{System.Windows.MessageBox.Show ("no signature has been performed and the save operation cannot be executed! ");}
Note: Undolist.clear () and Fileutil.locatefile (Fileuri) in the code are not to be heeded, I will explain later.
The picture below explains how to add a reference to System.Windows.Forms.
C. Implementing Undo and redo functions
As the InkCanvas itself does not seem to be such a method, so we have to move hands. The method is still relatively simple: first we need to understand that inkcanvas each stroke with a stroke class object in a collection inside (InkCanvas strokes property, StrokeCollection type). So, the undo/Redo function becomes a collection action, Undo removes the top element (of course we need to save the removed element for subsequent redo), and redo will add an item to the top of the collection. Here's a look at the code:
New Stack<stroke> ();
Declares a global variable (a stack of stroke) that is used to store the stroke that is removed when the undo operation is performed, and also to provide resources when the redo function is performed.
Private voidMainwindow_loaded (Objectsender, RoutedEventArgs e) { This. KeyDown + = (s, args) = { //Undo = detect Ctrl + Z if((Keyboard.modifiers & modifierkeys.control) = = Modifierkeys.control && args. Key = =key.z) {if(Ink. Strokes.any ()) {Undolist.push (ink). Strokes[ink. Strokes.count-1]); Ink. Strokes.removeat (Ink. Strokes.count-1); } } //Redo = detect Ctrl + Y if((Keyboard.modifiers & modifierkeys.control) = = Modifierkeys.control && args. Key = =key.y) {if(Undolist.any ()) {ink. Strokes.add (Undolist.pop ()); } } };}
In the loaded event of window, add the detection of CTRL + Z and CTRL + Y, as shown in the code above.
D. Open the Signature location
First pull humorous digression, this place I use the time P/invoke way, call C + + method to implement. Because I know very little about the cross-language call, I can't explain much, but after a bit of luck, I have reached my goal. If you feel more about this piece later, write a separate blog to explain it.
Back to the point, the first code:
Public Static classfileutil{/// <summary> ///Open the file location and select it based on the given file path/// </summary> /// <param name= "path" >file Full path</param> Public Static voidLocateFile (stringpath) { /*//This method causes each new File Explorer window to be opened, not like * string domain = "; * var psi = new ProcessStartInfo ("Explorer.exe"); * PSI. Arguments = "/c,/select," + path; * domain = PSI. Domain; * var p = process.start (PSI); */IntPtr ppidl=IntPtr.Zero; UINTpsfgaoout; Filemanager.shparsedisplayname (Path, IntPtr.Zero, outPpidl,0, outpsfgaoout); varres = Filemanager.openfolderandselectitems (Ppidl,0, IntPtr.Zero,0); } classFileManager {[DllImport ("Shell32.dll", EntryPoint ="Shopenfolderandselectitems")] Public Static extern LongOpenfolderandselectitems (IntPtr pidlfolder, UInt32 cidl, IntPtr apidl, UInt32 dwFlags); [DllImport ("Shell32.dll", EntryPoint ="Shparsedisplayname")] Public Static extern voidShparsedisplayname ([MarshalAs (UNMANAGEDTYPE.LPWSTR)]stringName, IntPtr BindingContext, [out ()] outIntPtr Pidl,UINTSfgaoin, [out ()] out UINTpsfgaoout); }}
This guy's going to start off with the beep again, please ignore it:
As the code says, the part of the comment can also be implemented to some extent, but it has some problems. So I was determined to find another solution, and finally I found out that the Shopenfolderandselectitems method in Shell32.dll (located in the Windows\System32 directory) could meet my needs. After a period of search related materials, and looked at the elder brother's experience sharing, I finally in C # way to godless the Shopenfolderandselectitems method into the above code in the shape. But I was sad to find that only the Openfolderandselectitems method seems to still not work (there is no correct location to the corresponding File/folder), after some data review [MSDN, Pinvoke.net], finally made a usable version.
3.Demo
Http://files.cnblogs.com/files/lary/Demo.rar
WPF Learning Note (i)--make a simple electronic signature board