|
¼ò½é È¥Ä꣬ÔÚŦԼÊÐÎ÷´åµÄÒ»¼ÒС¿§·È¹Ý£¬ÓÐÈËÏòÎÒ½éÉÜÁËÒ»¿îǦ±ÊÌîÊýÓÎÏ·£¬ÎÒµÄÅóÓÑ Luke ÍêÈ«±»Õâ¿îÓÎÏ·ÃÔסÁË¡£µ±Ê±ÓëÆäËûÈ˵ÄÓä¿ì̸»°Ê¹ÎÒÃâÓÚ²½Æäºó³¾£¬¿ÉÊÇȴûÏëµ½£¬¶ãµÃ¹ý³õÒ»¶ã²»¹ýÊ®Îå¡£¼¸¸öÔÂÖ®ºó£¬Ò²¾ÍÊÇ 2005 Äê 6 Ô£¬ÎÒÈ¥Â×¶Ø¿´ÍûÐֵܺʹóѧÊÒÓÑʱÓÖÅöµ½ÁË Sudoku ÓÎÏ·¡£ÏÔ¼û Sudoku ÒѾ·çÃÒһʱ£¬ÎÞ¸¥Ô¶½ü¡£ÎÒ´ÓºÃÆæ¡¢ÊÔ̽£¬×îºóÒ²³ÁÃÔÓÚÆäÖС£¶Ô¿ª·¢ÈËÔ±À´ËµÍùÍù»áÊÇÕâÑù£¬½ñÌìµÄ¼¤Ç鼫ÓпÉÄÜת»¯ÎªÃ÷ÌìµÄ¿ª·¢ÏîÄ¿¡£Õâ´ÎÒ²ºÁ²»ÀýÍ⣬ÎÒ·É»ØÃÀ¹úºóºÜ¿ì°²×°Á˵ÚÒ»¸ö°æ±¾µÄ C# Sudoku ÓÎÏ·£¬²¢ÂíÉÏ¿ªÊ¼ÔËÐС£ ¶øÎÒ¶ÔËüÈ´²»Ì«ÂúÒ⣬×ܸоõȱµãʲô¡£ÓñÊÔÚÖ½ÉϼÆËã²¢µÃ³öÕýÈ·Êý×ÖÊÇÒ»¼þÈÃÈËÐÄÇéÓäÔõÄÊ¡£¶øÎÊÌâÔÚÓÚÓÎϷת»¯Îª Windows ±í¸ñÐÎʽºó£¬ÕâÖÖÀÖȤ²»¼ûÁË¡£Õâ¸öÎÊÌâÉîÉîµØÀ§ÈÅ×ÅÎÒ¡£Sudoku ÊÇÒ»Öַdz£ÊʺÏÔÚ Tablet PC ÉÏʵÏÖµÄÓ¦ÓóÌÐò¡£µÚ¶þÌìÎÒÏÂÔØÁË Tablet PC SDK£¬ºÜ¿ì¾Í½«Õâ¿îÓɼüÅ̺ÍÊó±ê²Ù×÷µÄÓÎÏ·¸ÄΪ±Ê¿Ø²Ù×÷¡££¨Çë×¢ÒâʾÀý³ÌÐòÎļþÖеijÌÐòÔ±×¢ÊÍʹÓõÄÊÇÓ¢ÎÄ£¬±¾ÎÄÖн«ÆäÒëΪÖÐÎÄÊÇΪÁ˱ãÓڲο¼¡£±¾ÎÄÕ»¹°üº¬Ö¸ÏòÓ¢ÎÄÍøÒ³µÄÁ´½Ó¡££© 
ͼ 1£ºMicrosoft Sudoku ±¾ÎľÍÈçºÎ»ùÓÚ Tablet PC ʵÏÖ Sudoku ½øÐÐÁËÉîÈë̽ÌÖ¡£ÓëÔÀ´ÔÚ Touch Pack ÉϵÄʵÏÖ·½·¨Ïàͬ£¬ultra-mobile PC (UMPC) ÉÏԤװÁËÈí¼þ°ü¡£³ýÁË Sudoku ÓÎÏ·µÄÌØ¶¨Ï¸½ÚÍ⣬±¾ÎÄ»¹ÏêÊöÁËÓÎϷʵÏÖµÄËã·¨ÎÊÌ⣬ÓÐÖúÓÚÄúÔÚ Tablet PC ÉÏʵÏÖÆäËûµÄÓ¦ÓóÌÐòÉè¼Æ¡£ ʲôÊÇ Sudoku£¿ Sudoku ÊÇÒ»ÖÖÌîÊýÓÎÏ·£¬·çÃÒÓÚÈÕ±¾ºÍÅ·ÖÞ¡£Ôں̵ܶÄʱ¼äÄÚËüÓÖѸËÙÕ¼ÁìÁËÃÀ¹úÊг¡ £ ÊéµêÀï°ÚÂúÁË Sudoku ²úÆ·£¬±¨Ö½Ò²Á¬ÆªÀÛë¹µØ¶ÔÆä½øÐб¨µÀ¡£ÓÎÏ·µÄÔÐÍÊÇÒ»¸ö 9x9 µÄÍø¸ñ£¬·Ö³É¾Å¸ö 3x3 µÄС¿ò£¬Ã¿Ò»Ð¡¿ò°üº¬ 9 ¸öµ¥Ôª¸ñ¡£Èçͼ 2 ËùʾΪ°üº¬ 81 ¸öµ¥Ôª¸ñµÄÍêÕûͼÐΡ£

ͼ 2£º¿ÕµÄ Sudoku Íø¸ñ Õâ¿îÓÎÏ·µÄÍæ·¨ÊÇ£ºÔÚÿ¸öµ¥Ôª¸ñÖÐÌîÈëÒ»¸öÊý×Ö£¨1-9£©£¬×îºóÿÐС¢Ã¿ÁкÍÿһС¿òµÄ¾Å¸öµ¥Ôª¸ñÖж¼°üº¬ÓÐÈ«²¿µÄ 9 ¸öÊý×Ö£¨³¬¼¶ Sudoku ÄѶȸü¸ß£¬ËüʹÓà 16x16 Íø¸ñ£¬ÌîÈëÊý×Ö 1-9 ºÍ×Öĸ A-G£©¡£ Èçͼ 3 ÏÔʾΪһ¸ö Sudoku ÓÐЧ½â·¨¡£ 
ͼ 3£ºSudoku ÓÐЧ½â·¨ ͨ¹ý¼ÆË㣬9x9 Sudoku ¹²ÓÐ 1021 ÖÖÒÔÉϵÄÓÐЧ½â·¨£¨ÇëµÇ¼ http.//www.afjarvis.staff.shef.ac.uk/sudoku/ Á˽âÓйØÕâ·½Ãæ¼ÆËãµÄÏêϸÐÅÏ¢£©¡£¿ª¾Öʱ£¬Íø¸ñÖÐÒѾÌîÁËһЩÊý×Ö£¬¶øÆä÷ÈÁ¦¾ÍÔÚÓÚ£¬Ã¿¾ÖÓÎÏ·Ö»ÓÐÒ»ÖÖÕýÈ·½â·¨¡£Äú±ØÐëͨ¹ýÂß¼ÍÆµ¼ÌîÈëËùÓÐȱʧµÄÊý×Ö¡£ ÒÔͼ 4 ËùʾµÄÓÎϷΪÀý£¨Îª±ãÓÚ½²½â£¬ÎÒÃÇÒÔ»ÆÉ«ºÍºìɫͻ³öÏÔʾÁ˼¸¸öµ¥Ôª¸ñ£©¡£ÈçÉÏËùÊö£¬Ã¿Ò»Ð¡¿ò¶¼Òª°üº¬È«²¿µÄ¾Å¸öÊý×Ö¡£ÏÖÔÚ£¬×óÉϽǵĿòÖÐûÓÐ 2£¬Ö»Ðè¼òµ¥ÍƵ¼Ò»Ï¾ÍÄÜÖªµÀ 2 Ó¦¸ÃÌîÈëÄĸöµ¥Ôª¸ñ¡£µÚÒ»Ðв»ÐУ¬ÒòΪµÚÒ»ÐÐÒѾÓÐ 2 ÁË£¨ÔÚÓÒÉϽǣ¬ÒѾÓûÆÉ«Í»³öÏÔʾ£©¡£ÖмäÒ»ÐÐÒ²²»ÐУ¬ÕâÒ»ÐÐÒ²ÓÐ 2 £¨ÔÚµÚ¶þÐС¢µÚËÄÁУ©¡£ºÜÃ÷ÏÔÒ²²»ÄÜÊǵÚÈýÐеڶþÁУ¬¸Ãµ¥Ôª¸ñÒѾÓÐÒ»¸ö 7 £¨ÒÔ»ÆÉ«Í»³öÏÔʾ£©£»Ò²²»¿ÉÄÜÊÇ 7 ÓÒ²àµÄµ¥Ôª¸ñ£¬ÕâÒ»ÁÐÒ²ÓÐ 2£¨µÚ°ËÐУ¬ÒÔ»ÆÉ«Í»³öÏÔʾ£©¡£ËùÒÔ£¬2 Ò»¶¨ÊÇÌîÔÚµÚÈýÐеÚÒ»ÁУ¬¼´ÒÔºìɫͻ³öÏÔʾµÄµ¥Ôª¸ñ¡£È·¶¨Á˸ÿòÄÚ 2 µÄλÖúó£¬ÎÒÃÇÀëÍê³ÉÓÎÏ·¸ü½üÁËÒ»²½¡£ 
ͼ 4£º¿ªÊ¼ Sudoku ÓÎÏ· ÏÖÔÚÄúÒѾ¶ÔÓÎÏ·ÓÐÁËÒ»¶¨Á˽⣬±¾ÎĵįäÓàÄÚÈÝÖ÷ÒªÊǹØÓÚÈçºÎÔÚ Tablet PC ÉÏʵÏÖ Sudoku ÓÎÏ·µÄ¡£Ê×ÏÈ£¬ÈÃÎÒÃÇÀ´¿´Ò»Ï Tablet PC Ó¦ÓóÌÐòµÄ»ù±¾ÒªÇ󣬲¢ËµÃ÷ÈçºÎÔÚ Sudoku ÖÐÂú×ãÕâЩҪÇó£¬ÒÔ¼°ÄúÔÚ±àд×Ô¼ºµÄÓ¦ÓóÌÐòʱӦ¸ÃÔõô×ö¡£½ÓÏÂÀ´£¬ÎÒ»á¾ßÌå½²½â Sudoku ÓÎϷʵÏֵľßÌåÎÊÌ⣬Ö÷ÒªÊÇÈçºÎͨ¹ýÊʵ±Ëã·¨Íê³É Sudoku µÄÏà¹ØÈÎÎñ£¬ÈçÉú³ÉÐÂÓÎÏ·ºÍÕÒ³öÓÎÏ·½â·¨¡£È»ºóÔÙ½²Ò»ÏÂÓÐ¹Ø Tablet PC µÄ¾ßÌåϸ½ÚÎÊÌ⣬Ö÷ÒªÊÇÈçºÎʹÓà Tablet PC API À´Ö§³Ö±Êʽ½»»¥¡£×îºó£¬ÎÒ½«»á¸ø³öһЩÓÐȤµÄ¸ÅÄîºÍʾÀý£¬Äú¿ÉÒÔÓÃ×Ô¼ºÏ²»¶µÄ·½·¨ÍæÓÎÏ·¡£ Ó¦ÓóÌÐò»ù´¡¼Ü¹¹ ±¾²¿·ÖÉæ¼°µÄ¸ÅÄîÊÊÓÃÓÚÒ»°ãµÄ Tablet PC Ó¦ÓóÌÐò£¬²¢·Ç½öÏÞÓÚ Sudoku ÓÎÏ·¡£ ƽ̨¼ì²â ¶àÊýΪ Tablet PC ±àдµÄ³ÌÐòÖ»ÓÐÔÚ Tablet PC »ò²ÉÓÃÏàÓ¦ÅäÖõĿª·¢¼ÆËã»úÉϲÅÄÜ´ïµ½×î¼ÑÔËÐÐ״̬¡£¿ª·¢¼ÆËã»úÐèҪͬʱ°²×° Microsoft Windows XP Tablet PC Edition Software Development Kit 1.7 ºÍ Microsoft Windows XP Tablet PC Edition 2005 Recognizer Pack£¨¿É´Ó Mobile PC Developer Center£¨Ó¢ÎÄ£©»ñµÃÕâÁ½¸öÈí¼þ£©¡£Ôì³ÉÕâÖÖÏÖÏóµÄ²¿·ÖÔÒòÊÇ¶Ô Microsoft.Ink.dll ³ÌÐò¼¯µÄÒÀÀµ£¬¸Ã³ÌÐò¼¯Óë Windows XP Tablet PC Edition 2005 һͬ·¢²¼¡£Òò¶ø£¬ÔÚ³¢ÊÔÖ´ÐÐÒÀÀµÓÚÕâЩ³ÌÐò¼¯´æÔڵĴúÂë֮ǰ£¬ÓбØÒª¼ì²é Tablet PC Ó¦ÓóÌÐòÊÇ·ñ¿ÉÒÔʹÓñØÐèµÄ¿âºÍ¹¦ÄÜ¡£ÓÉÓÚ±ØÐëʹÓÃÆ½Ì¨¼ì²âÀ´Ö´ÐдæÔÚÐÔ¼ì²é£¬Òò´Ë£¬¼´Ê¹Ó¦ÓóÌÐò¶¨ÒåΪ½öÔÚ·¢ÏÖ¿âµÄʱºò²Å¶Ô¿â½øÐмÓÔØºÍʹÓã¬ÒàÐèÖ´ÐÐÈç´Ë²Ù×÷£¬ ×¢ÒâÖØÐ·¢²¼ tpcman17.msm ºÍ mstpcrt.msmneed ʱ²¢²»ÐèÒª¼ì²é Tablet PC ¶þ½øÖÆÎļþ£¬µ«ÄúÐèÒª±£Ö¤Ö»ÔÚÔËÐÐ Windows XP Tablet PC Edition 2005 µÄ¼ÆËã»úÉϲ¿ÊðÓ¦ÓóÌÐò¡£Èç¹ûÄúÎÞ·¨ÖØÐ·¢²¼ Tablet PC ¶þ½øÖÆÎļþ£¬²¢ÇÒÊÇÔÚûÓÐ Tablet PC ¶þ½øÖƵļÆËã»úÉϲ¿ÊðÓ¦ÓóÌÐò£¬ÔòÐèÒªÖ´ÐÐÕâЩ¼ì²éÒÔÈ·±£Ó¦ÓóÌÐòµÄÕý³£ÔËÐС£ÔÚijЩÇéÐÎÏÂÐèÒªÌØ±ð×¢ÒâÕâÒ»µã£¬ÀýÈçÔÚÓ¦ÓóÌÐò°üº¬ÊÖд¿Ø¼þÇÒ²¿ÊðÓÚÍøÕ¾Ê±¡£ ÔÚËæ¸½µÄʾÀý´úÂëÖУ¬ËùÓÐÆ½Ì¨¼ì²â´úÂë¾ù°üº¬ÔÚ PlatformDetection.cs ÎļþµÄ PlatformDetection ÀàÖУ¬¸ÃÎļþλÓÚ Utilities Îļþ¼ÐÏ¡£¸ÃÀà°üº¬µÄ´úÂë¿ÉÓÃÀ´Ö´Ðжà¸ö²»Í¬µÄ¼ì²é£¬»¹¿ÉÒÔ½«²éѯ½á¹ûÌṩ¸øÓ¦ÓóÌÐòµÄÆäÓಿ·Ö¡£ Ê×ÏÈ£¬ÔÚÆô¶¯ºó²»¾Ã£¬ÄúµÄÓ¦ÓóÌÐò»á¼ì²éÊÇ·ñÓпÉÓÃµÄ Microsoft.Ink.dll ³ÌÐò¼¯¡£Ê×ÏȱàÒëÀ´×Ô Microsoft.Ink.dll ³ÌÐò¼¯µÄ´úÂëÒýÓÃÀàʱ£¬½«ÒþʽִÐÐÕâÏî¼ì²é£¬µ«ÊÇÄú²¢²»»áÏ£ÍûÕâÑù£¬ÒòΪÔÚÄúûÓÐÔ¤¼Æ»á·¢ÉúÒ쳣ʱ£¬¿ÉÄÜ»áºÜÄÑ´Ó JIT ±àÒëÆ÷Òý·¢µÄÒì³£ÖнøÐÐÕýÈ·»Ö¸´¡£ÁíÍâÒ»ÖÖ·½·¨ÊÇ£¬Äú¿ÉÒÔÑ¡ÔñʹÓà Assembly.Load ºÍ Assembly.LoadWithPartialName ·½·¨À´½øÐÐÏÔʽËÑË÷£¬ÒÔÈ·¶¨ÊÇ·ñÄÜÕÒµ½ÓÐЧµÄ Microsoft.Ink.dll ³ÌÐò¼¯¡£ public static bool InkAssemblyAvailable { get { return _inkAssemblyAvailable; } } private static bool _inkAssemblyAvailable = (LoadInkAssembly() != null); private static Assembly LoadInkAssembly() { try { Assembly a = Assembly.Load( "Microsoft.Ink, Version=1.7.2600.2180, " + "Culture=neutral, PublicKeyToken=31bf3856ad364e35"); if (a == null) { a = Assembly.LoadWithPartialName( "Microsoft.Ink, PublicKeyToken=31bf3856ad364e35"); if (a != null && a.GetName().Version < new Version("1.7.2600.2180")) a = null; } return a; } catch(IOException){} catch(SecurityException){} catch(BadImageFormatException){} return null; } ¹«¹²µÄ¾²Ì¬ InkAssemblyAvailable ÊôÐÔ·µ»ØµÄÖµ´æ´¢ÔÚÒ»¸ö˽Óо²Ì¬²¼¶ûÖµÖУ¬¸Ã²¼¶ûÖµÒÔµ÷Óà LoadInkAssembly µÄ·µ»ØÖµ½øÐгõʼ»¯¡£LoadInkAssembly ʹÓóÌÐò¼¯µÄÈ«Ãû£¨°üÀ¨°æ±¾ºÅ¡¢ÇøÓòÐԺ͹«Ô¿±ê¼Ç£©£¬Í¨¹ý·´Éä API À´¼ÓÔØ Microsoft.Ink ³ÌÐò¼¯¡£Èç¹û¼ÓÔØ³ÌÐòÎÞ·¨ÕÒµ½°üº¬ÕâЩÐÅÏ¢µÄ³ÌÐò¼¯£¬Ôò LoadInkAssembly »á³¢ÊÔʹÓò¿·ÖÃû³ÆÀ´¼ÓÔØ Microsoft.Ink ³ÌÐò¼¯¡£Ò²¾ÍÊÇ˵£¬Ö´ÐÐËÑË÷ʱ»áÊ¡ÂÔһЩ³ÌÐò¼¯È«ÃûÐÅÏ¢¡£ÔÚ´ËʾÀýÖУ¬ÎÒÊ¡ÂÔÁ˰汾ºÅ¡£ÕâÖÖ˼·ÊÇ£¬Sudoku ʵÏÖ³ÌÐò¿ÉÄÜ»áÓë¸üа汾µÄ Microsoft.Ink.dll Ò»ÆðʹÓã¨ÀýÈ磬Èç¹ûÓÎÏ·ÔÚ Windows Vista ÏÂÔËÐиð汾µÄ Tablet PC API£©£¬Ôòͨ¹ýÊ¡ÂÔ°æ±¾ºÅ£¬ÎÒÔÊÐí¼ÓÔØ³ÌÐò²éÕÒËùÓа汾µÄ³ÌÐò¼¯¡£µ±È»£¬¼ÙÉèÕÒµ½µÄÿһ°æ±¾¶¼ÄÜ·ûºÏÒªÇóδÃâÓÐЩ²»ÇкÏʵ¼Ê£»ÓÎÏ·ÓпÉÄÜÔÚȱÉÙijЩ¹¦ÄÜÖ§³ÖµÄ½Ï¾É°æ±¾ÉÏÔËÐУ¬¶øÈç¹ûÔÚ´Ëʱ JIT ±àÒëÆ÷³¢ÊÔ¶Ôȱʧ²¿·ÖµÄʹÓýøÐбàÒëµÄ»°£¬½«»áµ¼Ö²Ù×÷ʧ°Ü¡£Îª±ÜÃâÕâÖÖÇé¿öµÄ·¢Éú£¬ÎÒ×öÁËÒ»¸ö¸üΪ°²È«µÄ¼ÙÉ裨Ҳ²»ÊÇ 100% µÄ°²È«£©£¬¼´Ö»½ÓÊܽÏеİ汾¡£Òò´Ë£¬ÔÚ°´²¿·ÖÃû³Æ¼ÓÔØ³ÌÐò¼¯Ö®ºó£¬ÎÒ¼ì²éÆä°æ±¾ºÅÒÔÈ·±£ÊǾ¹ýÎÒ±àÒëµÄ°æ±¾»ò½Ïа汾¡£ ¾¹ýÑéÖ¤»¹ÓÐÒ»ÖÖ¸üºÃµÄÑ¡Ôñ£¨ÎÒҲʵ¼ÊʹÓùý£©£¬¾ÍÊÇÈà CLR ÒÔÆä±ê×¼Âß¼À´³¢ÊÔÕÒµ½²¢¼ÓÔØÊʵ±µÄ Microsoft.Ink.dll ³ÌÐò¼¯£¬È»ºó¶Ô³ÌÐò¼¯²»¿ÉÓûòÎÞ·¨¼ÓÔØµÄÇé¿ö½øÐд¦Àí¡£ÎÒÔÚÇ°ÃæÒѾÌáµ½£¬ÔÚÄúûÓÐÔ¤¼Æ»á·¢ÉúÒ쳣ʱ£¬Í¨³£ºÜÄÑ´Ó³ÌÐò¼¯ºÍÀàÐͼÓÔØÒì³£Öлָ´¡£µ«Êǵ±ÄúÒâʶµ½¿ÉÄܻᷢÉúÒì³££¬²¢ÔÚÊܿػ·¾³Ï³ä·ÖÀûÓÃËüÃÇʱ£¬Æ¾½è CLR µÄ¹¦ÄÜͨ³£ÊÇ×îºÃµÄ·½·¨¡£ÎÒ¿ÉÒÔ°´ÕÕÏÂÁз½·¨ÖØÐ±àд LoadInkAssembly À´¸ü¸ÄÎÒµÄʵÏÖ³ÌÐò¡£ private static Assembly LoadInkAssembly() { try { return LoadInkAssemblyInternal(); } catch(TypeLoadException) {} catch(IOException){} catch(SecurityException){} catch(BadImageFormatException){} return null; } [MethodImpl(MethodImplOptions.NoInlining)] private static Assembly LoadInkAssemblyInternal() { return typeof(InkOverlay).Assembly; } »Øµ½³ÌÐòµÄ Main ·½·¨£¨Î»ÓÚ Program.cs ÎļþÖУ©£¬ÎÒʹÓà InkAssemblyAvailable ÊôÐÔÀ´È·¶¨ÊÇ·ñÓ¦¸Ã¼ÌÐøÖ´ÐС£Main Ö®ºó»áÏÔʾһ¸öÏûÏ¢¿ò£¬Èç²»ÔÊÐí¼ÌÐøÖ´ÐÐÔò»áÍ˳ö¡£ bool validSystem = PlatformDetection.InkAssemblyAvailable && PlatformDetection.RecognizerInstalled; if (!validSystem) { MessageBox.Show(ResourceHelper.NotRunningOnTablet, ResourceHelper.DisplayTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return 1; } Çë×¢Ò⣬ÎÒ²»½öÒªÇóÁËÊÖд³ÌÐò¼¯£¬»¹ÒªÇó°²×°Ê¶±ð³ÌÐò¡£ÔÚµäÐ굀 Tablet PC °²×°ÖУ¬ÊÖд³ÌÐò¼¯ºÍʶ±ð³ÌÐò×ÜÊdzɶԳöÏÖ¡£µ«ÊÇ£¬Ò²ÓпÉÄܳöÏÖʶ±ð³ÌÐò±»É¾³ýµÄÇé¿ö¡£ÔÚ¿ª·¢»·¾³ÏÂÒ²ÓпÉÄÜÔÚûÓÐʶ±ð³ÌÐòµÄÇé¿öϰ²×°ÁË Tablet PC API¡£Òò´Ë£¬ÎÒʹÓÃÏÂÁдúÂëÀ´¼ì²é¾ßÌåµÄÇé¿ö¡£ public static bool RecognizerInstalled { get { if (!InkAssemblyAvailable) return false; return GetDefaultRecognizer() != null; } } RecognizerInstalled ÊôÐÔÊ×Ïȼì²éÊÖд³ÌÐò¼¯ÊÇ·ñ¿ÉÓá£ÕâÒ»µã·Ç³£ÖØÒª£¬ÒòΪ RecognizerInstalled ÊôÐÔÓÃÀ´²é¿´Ê¶±ð³ÌÐòÊÇ·ñ¿ÉÓÃµÄ GetDefaultRecognizer ʵÏÖ³ÌÐò£¬ÒÀÀµÓÚÀ´×Ô Microsoft.Ink.dll µÄÀàÐÍ¡£ [MethodImpl(MethodImplOptions.NoInlining)] public static Recognizer GetDefaultRecognizer() { Recognizer recognizer = null; try { Recognizers recognizers = new Recognizers(); if (recognizers.Count > 1) { try { recognizer = recognizers.GetDefaultRecognizer(); } catch {} if (recognizer == null) { try { recognizer = recognizers.GetDefaultRecognizer(1033); } catch {} } } } catch {} return recognizer; } GetDefaultRecognizer ʵÀý»¯ Microsoft.Ink.Recognizers£¨Ó¢ÎÄ£©¼¯ºÏ£¬²¢È·±£ÆäÖÐÖÁÉÙÓÐÒ»¸öʶ±ð³ÌÐò¿ÉÓá£È»ºóËü»áʹÓøü¯ºÏÀ´¼ìË÷ĬÈϵÄʶ±ð³ÌÐò¡£Èç¹û³öÓÚijÖÖÔÒò¶øÎÞ·¨Èç´Ë²Ù×÷£¬½«»á³¢ÊÔ¼ìË÷ LCID 1033£¨ÃÀ¹úÓ¢Óï»·¾³£©µÄĬÈÏʶ±ð³ÌÐò¡££¨ÒòΪΨһÓÉ Sudoku Ö´ÐеÄʶ±ðÊÇÕë¶ÔÕûÊýµÄ£¬Õâ¾ÍÒѾ×ã¹»ÁË¡££© Çë×¢Ò⣬ÎÒÔÚ GetDefaultRecognizer ÉÏʹÓÃÁË System.Runtime.CompilerServices.MethodImplAttribute£¨Ó¢ÎÄ£©£¬½«Æä±ê¼ÇΪ±£³ÖÔÑù¶ø²»½øÐÐÄÚÁª¡£ÄÚÁªÊÇÖ¸±àÒëÆ÷Ñ¡Ôñ½«Ä¿±êº¯ÊýÄÚÈݸ´ÖƵ½Ò»¸öµ÷Óó¡Ëù£¬¶ø·ÇÕæÕý½øÐз½·¨µ÷ÓõĹý³Ì¡£¶ÔÓÚ½ÏСµÄ·½·¨£¬Ö´Ðз½·¨µÄ³É±¾Ö÷Ҫȡ¾öÓÚ½øÐз½·¨µ÷ÓõĿªÏú£¬¶øÄÚÁªÔò¿ÉÒÔ´ó·ùÌáÉýÐÔÄÜ¡£Êµ¼ÊÉÏ£¬ÒòΪ GetDefaultRecognizer ·½·¨ÖдúÂëÊýÁ¿µÄÎÊÌ⣬JIT ±àÒëÆ÷¼¸ºõûÓÐʲô»ú»áÀ´Ö´ÐÐÄÚÁª¹ý³Ì¡£ ²»¹ÜÔõÑù£¬ÎÒ¶¼²»Ï£Íû GetDefaultRecognizer ½øÐÐÄÚÁª¡£Çë¼Çס£¬µ± JIT ±àÒëÒ»¸ö·½·¨Ê±£¬JIT ±àÒëÆ÷»á³¢ÊÔ¼ÓÔØËùÓаüº¬·½·¨ËùÓÃÀàÐ͵ijÌÐò¼¯¡£¾ÙÀýÀ´Ëµ£¬µ± JIT ±àÒë GetDefaultRecognizer ʱ£¬JIT ±àÒëÆ÷½«»á³¢ÊÔ¼ÓÔØ Microsoft.Ink.dll¡£Èç¹û GetDefaultRecognizer ÒѾÄÚÁªÖÁ RecognizerInstalled£¬Ôò JIT ±àÒë RecognizerInstalled ʱ£¬»áÔÚ RecognizerInstalled Óлú»á¼ì²éÊÇ·ñ´æÔÚÊÖд³ÌÐò¼¯Ö®Ç°½øÐмÓÔØ³¢ÊÔ¡£Òò´Ë£¬ÎÒÔÚ GetDefaultRecognizer ÉÏʹÓà MethodImplAttribute À´È·±£Ëü²»»á½øÐÐÄÚÁª¡£ÕâʹµÃ RecognizerInstalled Ê×ÏÈÖ´ÐгÌÐò¼¯µÄ´æÔÚÐÔ¼ì²é¡£ ¸Ã Sudoku ʵÏÖ³ÌÐòÖ§³ÖʹÓòÁ³ýÊÖÊÆÀ´Çå³ýÍæ¼Ò֮ǰÊäÈëµÄÊý×Ö¡£ÔÚ´ó¶àÊýʾÀý´úÂëÖУ¬Èç¹ûûÓа²×°ÓÐЧµÄÊÖÊÆÊ¶±ð³ÌÐò£¬³¢ÊÔÖ§³ÖÊÖÊÆ¹¦Äܽ«»áµ¼ÖÂÒì³£ÏÖÏóµÄ·¢Éú£¬µ«ºÜÒź¶µÄÊÇ£¬ÕâЩÄú¸ù±¾¿´²»µ½¡£Òò´Ë£¬ÎÒ»¹Ê¹ÓÃÁË PlatformDetection À´¼ì²éÊÇ·ñ°²×°ÓÐÊÖÊÆÊ¶±ð³ÌÐò¡£ public static bool GestureRecognizerInstalled { get { return InkAssemblyAvailable && GestureRecognizerInstalledInternal; } } private static bool GestureRecognizerInstalledInternal { [MethodImpl(MethodImplOptions.NoInlining)] get { try { Recognizers recognizers = new Recognizers(); if (recognizers.Count > 0) { using(new GestureRecognizer()) return true; } } catch {} return false; } } Èç RecognizerInstalled Ò»Ñù£¬GestureRecognizerInstalled ¹«¹²¾²Ì¬ÊôÐÔ²»»áÏÔʽµØÖ´ÐÐÕâÏî¼ì²é¡£ÓÉÓÚÆä¶Ô Microsoft.Ink ³ÌÐò¼¯µÄÒÀÀµ¹ØÏµ£¬ËüͬÑù»áÒÀÀµÓÚ±ê¼ÇΪ NoInlining µÄ°ïÖúÕß³ÉÔ±¡£GestureRecognizerInstalledInternal ÊôÐÔ¼ì²éÊÇ·ñ´æÔÚʶ±ð³ÌÐò¡£È»ºóËüÏÔʽʵÀý»¯ Microsoft.StylusInput.GestureRecognizer£¨Ó¢ÎÄ£©£¬·µ»ØÊÇ·ñÄܹ»³É¹¦´´½¨Ò»¸ö Microsoft.StylusInput.GestureRecognizer µÄÖµ¡£Èç¹ûÎÞ·¨³É¹¦ÊµÀý»¯£¬Ôò»áÒý·¢ InvalidOperationException£¨Òò´Ë£¬try-catch ΧסʵÀý»¯²¿·Ö£©£¬±íÃ÷¡°The requested recognizer is not available with the current setup or configuration¡±£¨ÇëÇóµÄʶ±ð³ÌÐòÔÚµ±Ç°ÉèÖûòÅäÖÃϲ»¿ÉÓã©¡£ PlatformDetection ÌṩµÄ×îºóÒ»ÏÄÜ£¬ÊÇÈ·¶¨Óû§µÄ×óÓÒÊÖʹÓÃϰ¹ß¡£Tablet PC ²Ù×÷ϵͳÔÊÐíÓû§¸ù¾Ý×óÊÖ²Ù×÷»òÓÒÊÖ²Ù×÷À´ÅäÖÃϵͳ¡£Ó¦ÓóÌÐò¿É²éѯ´ËÉèÖò¢ÐÞ¸ÄÓû§½çÃæ£¬ÒÔ±ã×îºÃµØÊÊÓ¦Óû§µÄ×óÓÒÊÖʹÓÃϰ¹ß¡£ public static bool UserIsRightHanded { get { if (IsRunningOnTablet) { bool rightHanded = true; if (NativeMethods.SystemParametersInfo( NativeMethods.SPI_GETMENUDROPALIGNMENT, 0, ref rightHanded, 0)) { return rightHanded; } } return true; } } private static bool IsRunningOnTablet { get { return NativeMethods.GetSystemMetrics( NativeMethods.SM_TABLETPC) != 0; } } ´ËÊôÐÔÊ×Ïȼì²éµ±Ç°ÏµÍ³ÊÇ·ñΪ Tablet PC¡£ÕâÀïûÓÐʹÓà InkAssemblyAvailable£¬ÒòΪÊÖд³ÌÐò¼¯µÄ´æÔÚ²¢²»´ú±íϵͳ¾ÍÊÇ Tablet PC£¬Òò¶øÒ²²»´ú±í´ËÉèÖÿɹ©²éѯ¡£Ïà·´£¬ÆäÒÀÀµÓÚ user32.dll ÌṩµÄ GetSystemMetrics º¯Êý¡£ [DllImport("user32.dll", CharSet=CharSet.Auto)] internal static extern int GetSystemMetrics(int nIndex); ÌṩÁË SM_TABLETPC Öµ (86) Ö®ºó£¬Èç¹ûµ±Ç°²Ù×÷ϵͳΪ Microsoft Windows XP Tablet PC Edition£¬Ôò´Ëº¯Êý·µ»ØÒ»¸ö·ÇÁãÖµ£¬·ñÔò·µ»ØÒ»¸öÁãÖµ¡£ÔÚÖªµÀÓ¦ÓóÌÐò½«ÔÚ Tablet PC ÉÏÔËÐкó£¬ÎÒ¿ÉÒÔʹÓà user32.dll ÌṩµÄ SystemParametersInfo º¯ÊýÀ´²éѯ×óÓÒÊÖʹÓÃϰ¹ßÉèÖá£Óû§Ê×Ñ¡Ïî¿Éͨ¹ý SPI_GETMENUDROPALIGNMENT ÉèÖõÃÒÔÌåÏÖ£¬¶ÔÓÚÏàÓ¦µÄ²Ëµ¥À¸ÏîÄ¿£¬¸ÃÉèÖÃÈ·¶¨ÊÇÒÔ×ó¶ÔÆë»¹ÊÇÓÒ¶ÔÆëµÄ·½Ê½µ¯³ö²Ëµ¥¡£ Ϊ Ultra-Mobile PC Éè¼ÆÓ¦ÓóÌÐò
×î½ü Microsoft ºÍÐí¶à OEM ÍÆ³öÁË ultra-mobile PC (UMPC) ÕâÒ»Ìå»ýСÇɵļÆËã»ú£¬¶ÔÓÚÎÒÃÇÕâЩÊìϤ Tablet PC ¿ª·¢µÄÈËÀ´ËµÊÇÒ»¸ö¾øºÃµÄ»ú»á¡£ÓÉÓÚÔÚ UMPC ÉÏÔËÐÐ Windows XP Tablet PC Edition£¬ËùÒÔÎÒÃÇ¿ÉÒÔ³ä·ÖÀûÓÃÔÚ Tablet PC ·½ÃæµÄ¿ª·¢¾Ñé¡£Õâ¾ÍÒâζ×Åÿһ UMPC Öж¼º¬ÓÐ API£¬±ØÈ»ÐèÒª¿ª·¢³ÉÊìµÄÊÖдÊäÈëÓ¦ÓóÌÐò¡£Êµ¼ÊÉÏ£¬ÄúΪ Tablet PC ±àдµÄÓ¦ÓóÌÐòÓ¦¸Ã¿ÉÒÔºÁÎÞÕϰµØÔÚ UMPC ÉÏÔËÐС£µ«ÊÇ£¬ÔÚΪ UMPC É豸±àдÐÂÈí¼þ»òµ÷ÕûÏÖÓÐÈí¼þÒÔÆÚ¸ü¼ÑµÄ UMPC ÌåÑéʱ£¬ÐèÒªÌØ±ð×¢ÒâÈçϼ¸µã¡£ Ê×ÏÈÊÇ UMPC µÄÆÁÄ»½ÏС£¬Í¨³£Îª 800x480¡£Õâ¾ÍÐèÒªÄúÇå³þÁ˽âÓ¦ÓóÌÐòÒªÇóʲôÀàÐÍµÄÆÁÄ»¡£´ËÍ⣬Èç¹ûÄúµÄÓ¦ÓóÌÐòʹÓöÔÏó³ýÁË UMPC Ö®Í⻹Óбê×¼µÄ Tablet PC£¬ÔòÐèÒª¶ÔÓ¦ÓóÌÐò½øÐо«ÐÄÉè¼Æ£¬Ê¹Ö®ÔÚ²»Í¬µÄÆÁÄ»³ß´çÉ϶¼ÄÜ´ïµ½×î¼ÑЧ¹û¡£ÎÒ½«ÔÚ Windows ´°ÌåºÍÓ²¼þ½»»¥ÖÐÌÖÂÛÈçºÎÔÚ Sudoku Öд¦ÀíÕâÖÖÇé¿ö¡£ ½ñÌì´ó¶àÊý Tablet PC ÓµÓеÄÊǵç´Å¸ÐÓ¦ÆÁÄ»£¬¿ÉÓëרÓÃÊÖд±Ê½øÐи´Ôӵļì²âÓë½»»¥£¬¶ø UMPC Å䱸µÄÊÇ´¥ÃôʽÆÁÄ»¡£ÕâÑùµÄ»°É豸ʹÓÃÆðÀ´¾Í¸üΪ¼ò±ã£¬Óû§ÓÃÊÖÖ¸»òÆäËûÖ¸µãÉ豸¾Í¿ÉÒÔÇáËɵزÙ×÷ UMPC¡£µ«ÊÇ£¬ÓÉÓÚ´ó¶àÊýµÄ´¥ÃþÆÁÉÏȱÉÙÊÖÕÆÎó´¥·À»¤£¬Óû§ÌåÑéÆðÀ´ÓëÅ䱸µç´Å¸ÐÓ¦Êý×Ö°åµÄ Tablet PC »áÓв»Í¬¡£ËùÒÔÔÚÉè¼ÆÓ¦ÓóÌÐòÓû§½çÃæÊ±Ò»¶¨Òª¿¼Âǵ½ÕâÒ»µã¡£ÎÒµÄ Sudoku Ö§³Ö¶àÖÖ½»»¥·½Ê½¡£Ê¹ÓüüÅÌ»òÊÖд±Ê¿ÉÒÔÇáËɵØÔÚÓÎÏ·ÖÐÊäÈëÊý×Ö£¬¶øÇÒ»¹ÌṩÁ˽ϴóµÄÊý×Ö°´Å¥£¬ÕâÑù²Ù×÷ÆðÀ´¾Í¸üΪ¼òµ¥£¬Ö»ÐèÓÃÊÖÖ¸µã»÷Ò»¸öÊý×Ö£¬È»ºóÇá´¥ÔÚ Sudoku Íø¸ñÄڵĵ¥Ôª¸ñ£¬µ¥Ôª¸ñÖоͻá³öÏÖ¸ÃÊý×Ö¡£ ÁíÍâÒ»¸öÐèÒªÃÜÇÐ×¢ÒâµÄµØ·½ÊÇ UMPC ÖеÄÊÖд±ÊÖ»ÊÇÒ»¸ö¼òµ¥µÄÖ¸µãÉ豸£¬¶ø²»Êǵç´Å¸ÐÓ¦É豸¡£Ò²¾ÍÊÇ˵ÆäÓ¦ÓóÌÐò²»ÄÜÏñÅ䱸µç´Å¸ÐÓ¦Êý×Ö°åµÄ Tablet PC ÄÇÑù£¬Í¨¹ýÊÖд±ÊµÄ½»»¥½ÓÊÕµ½Í¬Ñù¶àµÄÐÅÏ¢¡£Òò´ËΪ±£Ö¤¸÷ÏÄܵÄÕý³£ÔËÐУ¬ÇëÈ·±£×¨Îª UMPC Éè¼ÆµÄÓ¦ÓóÌÐò²»ÒªÒÀÀµÓÚÆäËûÐÅÏ¢£¬Èç¿ÕÖÐÊý¾Ý°ü»òÊý×Ö°æÑ¹Á¦¡£ ×îÖØÒªµÄÒ»µãÊÇ£¬ÄúÍêÈ«¿ÉÒÔ½è¼øÔÚ Tablet PC ·½ÃæµÄ¿ª·¢ÖªÊ¶¡£ÄúÔÚÉè¼ÆºÍ¿ª·¢ UMPC Ó¦ÓóÌÐòʱÈç¹ûÄܹ»¿¼Âǵ½ÕâЩ·½Ã棬¾Í¿ÉÒÔΪÓû§´øÀ´¸üºÃµÄÌåÑé¡£ÕýÈçËù¼û£¬ÎÒ¿ª·¢µÄ Sudoku ÔÚ±ê×¼ Tablet PC ºÍ UMPC É豸É϶¼ÄÜÕý³£ÔËÐС£ ´úÂë·ÃÎʰ²È«ÐÔ Õ§¿´Ö®Ï£¬¿ÉÄÜ»áÓÐÈËÈÏΪÏñ Sudoku ÕâÑùµÄÓÎϷʵÏÖÎÞÐ迼ÂÇʲô°²È«ÎÊÌ⣬µ«ÊµÇéÈ´²¢·ÇÈç´Ë¡£×÷ΪһÃû¿ª·¢ÈËÔ±£¬Ó¦¸ÃʼÖÕ¹Ø×¢°²È«ÎÊÌ⣬¹Ø×¢Ó¦¸ÃΪ¿Í»§´øÀ´Ê²Ã´ÑùµÄ°²È«Ïà¹ØÒªÇó¡£ ÀýÈç Sudoku ÖÐÒªÓõ½¼¸¸ö Win32 API¡£·ÃÎÊÕâЩ API ÒªÇóÓзÇÍйܴúÂëȨÏÞ¡£Èç¹û Sudoku ÊÇ´Ó±¾µØÇý¶¯Æ÷ÔËÐС¢Ä¬ÈϵĴúÂë·ÃÎʰ²È«ÐÔ (CAS) ÉèÖñ»ÍêÕû±£Áô£¬ÄÇô¾ÍÍòÎÞһʧÁË¡£µ«ÊÇ£¬Èç¹ûÓÐÈËÊÔͼÒÔÍøÂç¹²ÏíµÄ·½Ê½ÔËÐÐ Sudoku ½«»áÈçºÎ£¿Ä¬ÈÏÇé¿öÏ£¬²»»áÏò Intranet Ó¦ÓóÌÐòÊÚÓè·ÇÍйܴúÂëȨÏÞ¡£Òò´Ë£¬µ± Sudoku ÊÔͼ·ÃÎÊÕâЩ·ÇÍÐ¹Ü API ʱ½«»áÒý·¢ÏµÍ³µÄ°²È«Òì³£¡£ ÓëÆäÔÚ²¿·ÖÐÅÈεĻ·¾³ÏÂÔËÐÐ Sudoku£¬»¹²»ÈçÏñ´ó¶àÊýÍйÜÓ¦ÓóÌÐòÒ»Ñù£¬Èà Sudoku ÔÚÍêÈ«ÐÅÈεĻ·¾³ÏÂÔËÐС£¶øÇÒ£¬Èç¹ûÔÚ²¿·ÖÐÅÈεĻ·¾³ÏÂÔËÐÐ Sudoku£¬ÄÇôÓû§ÌåÑéÒ²»á¼«ÆäÔã¸â£¬Ó¦ÓóÌÐò½«ÔÚÍæ¼ÒÃæÇ°·¢Éú±¯¾çÐÔµÄÔÖÄÑ¡£ËùÒÔ£¬Sudoku ÔÚÆô¶¯Ö®ºó»áÃ÷È·²é¿´ÊÇ·ñΪÍêÈ«ÐÅÈεĻ·¾³£¬Èç¹ûÓ뵱ǰ»·¾³²»¼æÈÝÔòÏòÓû§·¢³ö¾¯¸æ¡£ÒÔÏÂÊÇÆô¶¯ Sudoku Ö÷·½·¨µÄ´úÂë¡£ [STAThread] public static int Main() { Application.EnableVisualStyles(); Application.DoEvents(); if (!HasFullTrust) { MessageBox.Show(ResourceHelper.LacksMinimumPermissionsToRun, ResourceHelper.DisplayTitle, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); return 1; } Sudoku »á¼ì²éÊÇ·ñÒѾÊÚÓèÓ¦ÓóÌÐòÍêÈ«ÐÅÈμ¶±ð¡£Èç¹ûûÓУ¬Sudoku »áÏòÓû§ÏÔʾһ¸öÓѺõĴíÎó¶Ô»°¿ò£¬È»ºóÍ˳ö¡£HasFullTrust ÊôÐÔ¿Éͨ¹ýÈçÏ´úÂëʵÏÖ¡£ private static bool HasFullTrust { get { try { new PermissionSet(PermissionState.Unrestricted).Demand(); return true; } catch (SecurityException) { return false; } } } ¸ÃÊôÐÔ½« System.Security.PermissionSet ʵÀý»¯Îª²»ÊÜÏÞÖÆ¡¢ÍêÈ«ÐÅÈεķÃÎÊ£¬²¢ÇëÇó¸ÃȨÏÞ¡£Èç¹ûËùÇëÇóµÄȨÏÞ²»¿ÉÓã¬Ôò Demand ·½·¨»áÅ׳ö SecurityException¡£Æä½á¹ûÊÇ£¬´Ë´úÂë»á±»°ü×°½ø try ´úÂë¿éÖ®ÖС£Èç¹û Demand ·½·¨³É¹¦£¬HasFullTrust Ôò·µ»Ø true¡£Èç¹û³öÏÖÒì³£¶øÊ§°Ü£¬HasFullTrust Ôò·µ»Ø false¡£ »¹ÓÐÒ»¼þÓÐȤµÄʼþÐèҪעÒ⣬Èç¹û¼ì²éÍêÈ«ÐÅÈÎʧ°Ü£¬ÔòÓ¦ÓóÌÐò½«»áÊÔͼÏÔʾһ¸öÏûÏ¢¿ò¡£Êµ¼ÊÉÏ»¹ÓÐÒ»¸ö¿ØÖÆÊÇ·ñ¿ÉÒÔÏÔʾÏûÏ¢¿òµÄȨÏÞ¡£Èç¹ûûÓÐÉèÖà SafeSubWindows µÄ UIPermission£¬ÔòÔÚÓ¦ÓóÌÐòÊÔͼÏÔʾÏûÏ¢¿òʱ»áÊÕµ½°²È«Òì³£¡£Óм¸ÖÖ·½·¨¿ÉÒÔ½â¾öÕâ¸öÎÊÌâ¡£Ò»ÖÖÊDz¶»ñ²úÉúµÄÒì³£²¢Í˳ö³ÌÐò£¬µ«ÊÇÕâÑùµÄ»°Óû§²»ÖªµÀºÎ´¦³öÏÖ´íÎó¡£ËûÃÇ»áÔÚ Microsoft Windows ×ÊÔ´¹ÜÀíÆ÷ÖÐË«»÷¸ÃÓ¦ÓóÌÐò£¬µ«²»»áÓÐÈκÎÏìÓ¦£¨»òÕ߸üÈ·Çеؽ²ÊÇ¿´²»µ½ÈκÎÏìÓ¦¡£Windows »áÔÚºǫ́Æô¶¯¸ÃÓ¦ÓóÌÐò£¬È»ºóÔÚÉÐδÏÔʾÈκοɼûÏßË÷֮ǰÍ˳ö¡££©Òò´Ë£¬ÎÒ½«³ÌÐò¼¯¶Ô´Ë SafeSubWindows ȨÏÞµÄÒªÇó±ê¼ÇΪ×îµÍÏÞ¶È¡£ [assembly:UIPermission(SecurityAction.RequestMinimum, Window=UIPermissionWindow.SafeSubWindows)] ÕâÑù£¬Èç¹û³ÌÐò¼¯Î´±»ÊÚÓè´Ë×îµÍȨÏÞ£¬CLR ½«»áÏÔʾÆä×ÔÓжԻ°¿ò£¬Í¨ÖªÓû§ÒòȱÉÙ±ØÒªµÄȨÏÞ¶øÎÞ·¨¼ÓÔØÓ¦ÓóÌÐò¡£Èç¹ûÊÚÓèÁË´Ë×îµÍȨÏÞ£¬Ôò»á¼ÓÔØÓ¦ÓóÌÐò£¬Í¬Ê±ÎÒµÄÂß¼»á¼ì²é Main ·½·¨ËùÈ¡µÃµÄÍêÈ«ÐÅÈΡ£ ´¦ÀíÑÏÖØÒì³£´íÎó
Main ·½·¨Ê×ÏȲ鿴ÍêÈ«ÐÅÈΣ¬È»ºóÑéÖ¤ Microsoft.Ink ³ÌÐò¼¯¿ÉÓò¢ÇÒʶ±ð³ÌÐòÒѰ²×°ºó£¬»áµ÷ÓÃÁíÍâÒ»¸ö·½·¨ MainInternal£¬¸Ã·½·¨ÓÃÓÚÍê³ÉÖ÷´°ÌåµÄÉèÖÃºÍÆô¶¯µÈºËÐŤ×÷¡£MainInternal ¼¤»îÓ¦ÓóÌÐòµÄÖ÷ÏûϢѻ·£»Òò´Ë£¬ÓÎÏ·ÖÐËùÓÐÀ´×ÔÓû§½çÃæ½»»¥µÄδ´¦ÀíÒì³£¾ù´Ó MainInternal ÏòÍâ´«²¥¡£ÎªÈ·±£ÕýÈ·µØ¼Ç¼ÕâЩÒì³££¨ÓÃÓÚÕï¶ÏºÍµ÷ÊÔ£©£¬Main ·½·¨»á²¶»ñËùÓÐÕâЩÒì³££¬½«Æä¼Ç¼ȻºóÍ˳ö¡£ try { return MainInternal() ? 0 : 1; } catch(Exception exc) { ShutdownOnException(new UnhandledExceptionEventArgs(exc, false)); return 1; } catch { ShutdownOnException(new UnhandledExceptionEventArgs(null, false)); return 1; } ÎÒʹÓà ShutdownOnException ·½·¨À´¼Ç¼ÑÏÖØ´íÎóÌõ¼þ£¬ÒÔÈ·±£Ó¦ÓóÌÐòÄܹ»¾¡¿ì¹Ø±Õ¡£ internal static void ShutdownOnException(UnhandledExceptionEventArgs e) { try { string message = (e.ExceptionObject != null) ? e.ExceptionObject.ToString() : ResourceHelper.ShutdownOnError; EventLog.WriteEntry(ResourceHelper.DisplayTitle, message, EventLogEntryType.Error); } catch {} try { MessageBox.Show(ResourceHelper.ShutdownOnError, ResourceHelper.DisplayTitle, MessageBoxButtons.OK, MessageBoxIcon.Hand, MessageBoxDefaultButton.Button1); } catch {} if (!e.IsTerminating) Environment.Exit(1); } ÔÚʹÓà .NET Framework 2.0 ÖÐÌṩµÄ System.Environment.FailFast ·½·¨Ö®ºó£¬¿ÉÒԺܺõÄÍê³É´ó¶àÊý²Ù×÷£¬µ«ÊÇ´ËʵÏÖÊÇ»ùÓÚ .NET Framework 1.1 µÄ¡£ ShutdownOnException ·½·¨½ÓÊÜÃèÊöºÎ´¦³öÏÖ´íÎóµÄ System.UnhandledExceptionEventArgs ²ÎÊý¡£¸Ã·½·¨Ê×Ïȳ¢ÊԼǼÓйØÒì³£µÄÐÅÏ¢£¨°üÀ¨¶ÑÕ»¸ú×Ù£©£¬Ê¹Óà System.Diagnostics.EventLog.WriteEntry ¾²Ì¬·½·¨½«¸ÃÐÅϢдÈëÓ¦ÓóÌÐòʼþÈÕÖ¾ÖС£È»ºó»áÏÔʾÏûÏ¢¿ò£¬Í¨ÖªÓû§·¢ÉúÁËÒâÍâ¡£×îºó£¬Èç¹ûÓ¦ÓóÌÐòÈÔδ¹Ø±Õ£¬Environment.Exit ·½·¨»áÖÕÖ¹ÔËÐС£ ×ÛÉÏËùÊö£¬½öÔÚÖ÷ UI Ï̳߳öÏÖδ´¦ÀíÒ쳣ʱµ÷Óà ShutdownOnException¡£ÆäËûÏ̳߳öÏÖÑÏÖØÒ쳣ʱÔõô°ì£¿Îª½â¾öÕâÒ»ÎÊÌ⣬MainInternal ·½·¨ËùÒª×öµÄµÚÒ»¼þÊ£¬¾ÍÊǽ«Ê¼þ´¦ÀíÆ÷Óë AppDomain µÄµ±Ç° UnhandledException ʼþÒÔ¼° Windows Form µÄ Application.ThreadException ʼþÁªÏµÆðÀ´¡£ AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( CurrentDomain_UnhandledException); Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException); ÕâЩʼþµÄʼþ´¦ÀíÆ÷ίÍиø ShutdownOnException¡£ private static void Application_ThreadException( object sender, ThreadExceptionEventArgs e) { ShutdownOnException(new UnhandledExceptionEventArgs( e.Exception, false)); } private static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e) { ShutdownOnException(e); } ÀûÓÃÕâЩ´úÂ룬Óû§ÔÚÓ¦ÓóÌÐòʹÓùý³ÌÖÐÓöµ½µÄËùÓйÊÕÏ»òÑÏÖØÒì³£¶¼»á±»¼Ç¼ÏÂÀ´£¬²úÆ·¹ÊÕϵĵ÷ÊÔ¹¤×÷½«»á¸ü¼ÓÇáËÉ¡£Çë×¢Ò⣬ÔÚ .NET Framework 2.0 ֮ϣ¬ÕâЩ¹¤×÷´ó¶àÊý¶¼ÊÇ×Ô¶¯Íê³ÉµÄ¡£ÖðÒ»½â¾öÔËÐйý³ÌÖÐËùÓÐδ´¦ÀíÒì³£Ö®ºó£¬Dr. Watson ¿ªÊ¼×ª´¢Ó¦ÓóÌÐòÒÔ±ãÓÚÈÕºóµÄ·ÖÎöºÍµ÷ÊÔ¡£ µ¥ÊµÀýÓ¦ÓóÌÐò
Ðí¶àÓ¦ÓóÌÐò£¬ÓÈÆäÊÇ Tablet PC Ó¦ÓóÌÐò£¬´ó¶¼Êǵ¥ÊµÀýÓ¦ÓóÌÐò¡£µ¥ÊµÀýÓ¦ÓóÌÐòÊÇÖ¸£¬Ê¼ÖÕÖ»ÓÐÒ»¸öÖ´ÐиÃÓ¦ÓóÌÐòµÄ½ø³ÌÔÚÔËÐеÄÓ¦ÓóÌÐò£¬ÎÞÂÛÊÇÔÚÌØ¶¨×ÀÃæÓ¦Óû¹ÊÇÕû¸ö¼ÆËã»úÖоùÊÇÈç´Ë£¬È¡¾öÓÚÄú¶Ô¡°µ¥ÊµÀý¡±µÄ¶¨ÒåÒÔ¼°¶ÔÓ¦ÓóÌÐòµÄÐèÇó¡£¶ÔÓÚ Sudoku£¬ÎÒÏëÒª¼ÆËã»úÉϵÄÿ¸öÓû§Ã¿´ÎÖ»ÄÜÔËÐÐ Sudoku µÄÒ»¸öʵÀý¡£Èç¹ûʹÓà Microsoft Visual Studio 2005 ºÍ .NET Framework 2.0 À´ÊµÏÖ Sudoku£¬Ôòµ¥ÊµÀýÖ§³ÖÒѾ¿ÉÓᣵ«ÎÒʹÓõÄÊÇ .NET Framework 1.1£¬ËùÒÔ»¹ÐèÆäËû¹¤×÷²ÅÄÜʵÏÖµ¥ÊµÀýÖ§³Ö¡£Óйص¥ÊµÀýÖ§³ÖµÄÏêϸÐÅÏ¢£¬Çë²ÎÔÄ .NET Matters column in MSDN Magazine's September, 2005 issue£¨Ó¢ÎÄ£©¡£ Îҵĵ¥ÊµÀýÖ§³ÖÄÚÖÃÓÚ SingleInstance ÀàÖУ¬Î»ÓÚ Utilities Îļþ¼ÐµÄ SingleInstance.cs ÎļþÖС£MainInternal °´ÕÕÏÂÁз½Ê½Ê¹Óà SingleInstance¡£ using(SingleInstance si = new SingleInstance()) { if (si.IsFirst) { //´´½¨Ö÷´°Ìå²¢ÔËÐÐÓ¦ÓóÌÐò ... //³É¹¦Íê³ÉÓÎÏ· return true; } else { //²»ÊǵÚÒ»¸ö Sudoku ʵÀý... ÏÔʾÁíÍâÒ»¸ö si.BringFirstToFront(); return false; } } ÎÒÊ×ÏÈ´´½¨Ò»¸öеÄÀàʵÀý²¢²éѯÆä IsFirst ÊôÐÔ£¬ÒÔÈ·¶¨¸ÃÓ¦ÓóÌÐòµ±Ç°ÊÇ·ñÓÐÆäËûʵÀýÕýÔÚÔËÐС£Èç¹ûÕâÊǵÚÒ»¸öʵÀý£¬ÔòÓ¦ÓóÌÐò¼ÌÐøÕý³£ÔËÐУ¬´´½¨²¢ÏÔʾӦÓóÌÐòµÄÖ÷´°Ìå¡£Èç¹ûÕâ²»ÊǵÚÒ»¸öʵÀý£¬BringFirstToFront ·½·¨»á°Ñ Sudoku Ó¦ÓóÌÐòµÚÒ»¸öʵÀýµÄ´°¿ÚתÖÁǰ̨£¬È»ºóÍ˳öµÚ¶þ¸öʵÀý¡£ µ¥ÊµÀý¹¦ÄÜÐèҪijÖÖ½ø³Ì¼äͨÐÅ (IPC) µÄ·½Ê½£¬ÕâÊÇÓÉÓÚÒ»¸öʵÀýÐèҪ֪ͨÆäËûʵÀýÒѾÓÐʵÀý´æÔÚ£¬²»Ó¦¼ÌÐø¼ÓÔØµÚ¶þ¸öʵÀý¡£SingleInstance Ààʵ¼ÊÉÏÒÀÀµÁ½¸ö²»Í¬µÄ IPC »úÖÆ£º½ø³Ì¼äͬ²½ÔÓïºÍÄÚ´æÓ³ÉäÎļþ¡£ ´´½¨µ¥´¿µÄµ¥ÊµÀý¹¦ÄÜÊ®·Ö¼òµ¥£¬Ö»Ð輸ÐдúÂë¼´¿ÉÍê³É¡£ static void Main() { string mutexName = "2ea7167d-99da-4820-a931-7570660c6a30"; bool grantedOwnership; using(Mutex singleInstanceMutex = new Mutex(true, mutexName, out grantedOwnership) { if (grantedOwnership) { ... //´Ë´¦ÎªºËÐÄ´úÂë } } } µ±Ë«Ï̻߳ò¸ü¶àÏß³Ìͬʱ·ÃÎʹ²Ïí×ÊԴʱ£¬ÄúÐèÒªÓÐͬ²½»úÖÆÀ´È·±£ÔÚijһʱ¼äµãÖ»ÓÐÒ»¸öÏ߳̿ÉÒÔ·ÃÎʸÃ×ÊÔ´¡£¶ÔÓÚÎÒÃǶøÑÔ£¬¡°×ÊÔ´¡±µÄÂß¼º¬ÒåÒª±ÈÆäÎïÀíÉϵĺ¬Òå¹ãÀ«µÄ¶à¡£ËüÊDZ£Ö¤ÕýÔÚÔËÐеÄÓ¦ÓóÌÐòÖ»ÓÐÒ»¸öʵÀýµÄÄÜÁ¦¡£Ëùν mutex£¬ÈçÔÚ System.Threading.Mutex ÀàÖÐËùʵÏֵģ¬ÊÇÔÚijһʱ¼äµã£¬½öÏòÒ»¸öÏß³ÌÊÚÓè¶ÀÕ¼·ÃÎÊȨÏÞµÄͬ²½ÔÓï¡£Èç¹ûÒ»¸öÏ̻߳ñµÃ Mutex£¬ÔòÔÝÍ£ÆäËûÇëÇóÏàͬ Mutex µÄỊ̈߳¬Ö±µ½µÚÒ»¸öÏß³ÌÊÍ·Å Mutex Ϊֹ¡£»òÕߣ¬ÔÚijһÏ̵߳ȴý×ÊԴʱ²»½«ÆäÔÝÍ££¬¶øÊÇÔÚÆä³¢ÊÔ»ñµÃ Mutex ʱ±»¸æÖª¸Ã Mutex Òѱ»ÆäËûÏß³ÌÕ¼Óᣠ´Ë´¦£¬ÒÔ GUID Ãû³Æ´´½¨ Mutex£¨ËùÓÐʹÓôËÀà´úÂëµÄ²»Í¬Ó¦ÓóÌÐò½«»áÑ¡ÔñÆäΨһµÄÃû³Æ£©¡£ÔÚÄúʹÓÃijһÃû³Æ´´½¨ Mutex Ö®ºó£¬¾Í¿ÉÒÔÓÃÀ´Í¬²½´æÔÚÓÚ²»Í¬½ø³ÌÖ®¼äµÄỊ̈߳¬¶øÃ¿Ò»½ø³Ì¶¼¿ÉÒÔ¸ù¾ÝÆäÔ¤ÉèºÍÒÑÖªµÄÃû³ÆÀ´ÒýÓà Mutex¡£Mutex µÄÒ»¸ö¹¹Ô캯Êý²»½ö½ÓÊÜÃû³Æ£¬»¹½ÓÊÜ out ²¼¶û²ÎÊý£¬¸Ã²ÎÊýÓÃÓÚָʾ Mutex ÊÇ·ñʵ¼Ê¿ÉµÃ¡£ÕâÑù¾Í¿ÉÒÔÇáËÉ´´½¨µ¥ÊµÀý¹¦ÄÜ¡£¸Ã´úÂë»á´´½¨ Mutex ²¢¼ì²éÆäÊÇ·ñ¿ÉÓá£Èç¹û Mutex ¿ÉÓã¬Ôò¸ÃÓ¦ÓóÌÐòµÄʵÀý¾ÍÊǵÚÒ»¸öʵÀý£¬¿ÉÒÔ¼ÌÐø½øÐС£Èç¹û Mutex ²»¿ÉÓã¬ÄÇôËü¾ÍÊǵڶþ¸öʵÀý£¬Ó¦¸ÃÍ˳ö¡£ ÔÚÄÚ²¿£¬SingleInstance Î§ÈÆÏàͬµÄÔÔò¹¹½¨£¬µ«ÊÇÉÔÓв»Í¬¡£Ê×ÏÈ£¬SingleInstance ²»Ê¹ÓÃÓ²±àÂë GUID£¬¶øÊÇ»ùÓÚµ±Ç°Ó¦ÓóÌÐòÖÐÊäÈë³ÌÐò¼¯µÄÉí·ÝÉú³É ID¡£ private static string ProgramID { get { Assembly asm = Assembly.GetEntryAssembly(); Guid asmId = Marshal.GetTypeLibGuidForAssembly(asm); return asmId.ToString("N") + asm.GetName().Version.ToString(2); } } ID ÊdzÌÐò¼¯ÀàÐÍ¿â GUID ºÍ³ÌÐò¼¯°æ±¾µÄ×éºÏ¡£°´ÕÕÏÂÁз½Ê½Ê¹Óà ID À´¹¹Ôì Mutex Ãû³Æ¡£ string mutexName = "Local\\" + ProgramID + "_Lock"; ÈçͬÉÏÒ»¸ö¼òµ¥µÄµ¥ÊµÀýʾÀý£¬ÎÒµÄ SingleInstance À๹Ô캯Êý¶Ô Mutex ½øÐÐʵÀý»¯£¬¾¡¹ÜÊǽ«Æä´æ´¢ÔÚ³ÉÔ±±äÁ¿¶ø·Ç±¾µØ±äÁ¿ÖС£ _singleInstanceLock = new Mutex(true, mutexName, out _isFirst); ¹¹Ô캯ÊýµÄµÚÈý¸ö²ÎÊýÊDz¼¶û±äÁ¿£¬¸Ã±äÁ¿ÓÃÓÚ´æ´¢´Ë Mutex ʵÀýÄÜ·ñ»ñµÃËø¶¨£¬È»ºó¿ÉÒÔͨ¹ý IsFirst ÊôÐÔ·ÃÎʸò¼¶ûÖµ¡£ public bool IsFirst { get { return _isFirst; } } SingleInstance ʵÏÖµÄ IDisposable.Dispose »á¹Ø±Õ Mutex¡£ Èç¹ûÕâÕýÊÇËùÒªÇóµÄ¹¦ÄÜ£¬ÄÇôÎҵŤ×÷¾Í»ù±¾ÉÏÍê³ÉÁË¡£µ«ÊÇ£¬ÔÚÊÔͼÆô¶¯µÚ¶þ¸öʵÀýʱ£¬Ðí¶àµ¥ÊµÀýÓ¦ÓóÌÐò»¹»á°ÑÏÖÓÐʵÀýתÖÁǰ̨¡£ÕâÒ»¹¦ÄÜÒªÇó¿ÉÔÚ½ø³Ì¼ä´«µÝ¸½¼ÓÐÅÏ¢¡£ÓÈÆäÊÇ£¬³ýÁ˼ì²âÊÇ·ñÒѾ´æÔÚʵÀýÍ⣬µÚ¶þ¸öʵÀý±ØÐëÄܹ»È·¶¨Ó¦½«Äĸö½ø³ÌתÖÁǰ̨¡£ÎÒ×¢Òâµ½£¬Ò»Ð©Ó¦ÓóÌÐòÊÇͨ¹ýËÑË÷×ÀÃæÉϾßÓÐÌØ¶¨Ãû³ÆµÄËùÓж¥²ã´°¿ÚÀ´Íê³ÉÕâÒ»ÈÎÎñ£¬µ«ÕâÖÖ·½·¨²»Ì«¿É¿¿¡£ÎÒ²ÉÓõÄÊÇÁíÍâÒ»ÖÖ·½Ê½£¬ÄÚ´æÓ³ÉäÎļþ¡£ ¶ÔÓÚÎÒÃǵÄÇé¿ö£¬¿ÉÒÔ½«ÄÚ´æÓ³ÉäÎļþ¿´×÷Ò»ÖÖÄÚ´æ¹²Ïí·½Ê½¡£ÕâÔÊÐíÒ»¸ö½ø³Ì¿ÉÒÔÏòÄÚ´æÖÐдÈëÐÅÏ¢£¬¹©ÆäËû½ø³Ì¶ÁÈ¡¡£ÌرðÊÇ£¬Ó¦ÓóÌÐòµÄµÚÒ»¸öʵÀý¿ÉÏòÄÚ´æÓ³ÉäÎļþÖÐдÈëÆä½ø³Ì ID£¬È»ºóµÚ¶þ¸öʵÀý¿ÉÒÔ¶ÁÈ¡¸Ã½ø³Ì ID ²¢Ê¹Óøà ID ½«Ö÷ʵÀýתÖÁǰ̨¡£ private void WriteIDToMemoryMappedFile(uint id) { _memMappedFile = new HandleRef(this, NativeMethods.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, PAGE_READWRITE, 0, IntPtr.Size, _memMapName)); if (_memMappedFile.Handle != IntPtr.Zero && _memMappedFile.Handle != new IntPtr(-1)) { IntPtr mappedView = NativeMethods.MapViewOfFile( _memMappedFile.Handle, FILE_MAP_WRITE, 0, 0, 0); try { if (mappedView != IntPtr.Zero) Marshal.WriteInt32(mappedView, (int)id); } finally { if (mappedView != IntPtr.Zero) NativeMethods.UnmapViewOfFile(mappedView); } } } private uint ReadIDFromMemoryMappedFile() { IntPtr fileMapping = NativeMethods.OpenFileMapping( FILE_MAP_READ, false, _memMapName); if (fileMapping != IntPtr.Zero && fileMapping != new IntPtr(-1)) { try { IntPtr mappedView = NativeMethods.MapViewOfFile( fileMapping, FILE_MAP_READ, 0, 0, 0); try { if (mappedView != IntPtr.Zero) return (uint)Marshal.ReadInt32(mappedView); } finally { if (mappedView != IntPtr.Zero) NativeMethods.UnmapViewOfFile(mappedView); } } finally { NativeMethods.CloseHandle(fileMapping); } } return 0; } ÀàËÆÓÚ½ø³Ì¼äµÄ»¥³âËø£¬Ò²¿ÉÒÔÃüÃûÄÚ´æÓ³ÉäÎļþ£¬ÒÔ±ã¶à¸ö½ø³Ìͨ¹ý¸ÃÃû³Æ·ÃÎÊ¡£¿ÉʹÓà kernel32.dll ÌṩµÄ CreateFileMapping º¯Êý´´½¨ÄÚ´æÓ³ÉäÎļþ¡£È»ºóʹÓÃͬÑùÓÉ kernel32.dll ÌṩµÄ MapViewOfFile º¯Êý½«ÄÚ´æÓ³ÉäÎļþÓ³Éäµ½µ±Ç°µØÖ·¿Õ¼ä¡£MapViewOfFile ΪÄÚ´æÌṩÆðʼµØÖ·£¬È»ºóÓëÀ´×Ô System.Runtime.InteropServices.Marshal ÀàµÄ·½·¨Ò»ÆðʹÓ㬶ÁÈ¡ÄÚ´æÖеÄÊý¾ÝÒÔ¼°ÏòÄÚ´æÖÐдÈëÊý¾Ý¡£Èç¹ûÒѾ´´½¨ÁËÄÚ´æÓ³ÉäÎļþ£¬Äú¿ÉÒÔʹÓÃÀ´×Ô kernel32.dll µÄ OpenFileMapping º¯Êý´ò¿ª¸ÃÎļþ¡£ [DllImport("Kernel32", CharSet=CharSet.Auto, SetLastError=true)] internal static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpAttributes, int flProtect, int dwMaxSizeHi, int dwMaxSizeLow, string lpName); [DllImport("Kernel32", CharSet=CharSet.Auto, SetLastError=true)] internal static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32", CharSet=CharSet.Auto, SetLastError=true)] internal static extern IntPtr MapViewOfFile(IntPtr hFileMapping, int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow, int dwNumberOfBytesToMap); [DllImport("Kernel32", CharSet=CharSet.Auto, SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] internal static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); [DllImport("kernel32.dll", SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle(IntPtr handle); SingleInstance ¹¹Ô캯Êý´´½¨ Mutex ²¢ÑéÖ¤ÆäÊÇ·ñΪµÚÒ»¸öʵÀý¡£Èç¹ûÊǵÚÒ»¸öʵÀý£¬SingleInstance ¹¹Ô캯ÊýʹÓà WriteIDToMemoryMappedFile ·½·¨À´´æ´¢×Ô¼ºµÄ½ø³Ì ID£¬ÒÔ¹©µÚ¶þ¸öʵÀýËæºó¼ìË÷Ö®Óᣵ±µÚ¶þ¸öʵÀý¿ªÊ¼ÔËÐв¢µ÷Óà BringFirstToFront ·½·¨Ê±£¬µÚ¶þ¸öʵÀýÊ×ÏÈʹÓà ReadIDFromMemoryMappedFile À´·ÃÎÊÖ÷ʵÀýµÄ½ø³Ì ID¡£¿É½«¸Ã½ø³Ì ID Óë user32.dll ÌṩµÄ ShowWindowAsync ºÍ SetForegroundWindow º¯Êý½áºÏʹÓ㬽«Ö÷ʵÀýתÖÁǰ̨¡£ public void BringFirstToFront() { if (!_isFirst) { uint processID = ReadIDFromMemoryMappedFile(); if (processID != 0) { const int SW_SHOWNORMAL = 0x1; const int SW_SHOW = 0x5; IntPtr hwnd = new ProcessInfo(processID).MainWindowHandle; if (hwnd != IntPtr.Zero) { int swMode = NativeMethods.IsIconic(new HandleRef( this, hwnd)) ?SW_SHOWNORMAL :SW_SHOW; NativeMethods.ShowWindowAsync(hwnd, swMode); NativeMethods.SetForegroundWindow(new HandleRef( this, hwnd)); } } } } Sudoku Ëã·¨
±¾²¿·Ö½«¶Ô Sudoku ÓÎÏ·ÌØÓеÄÓ¦ÓóÌÐòÌØÕ÷·½Ãæ½øÐÐÏêϸ²ûÊö¡£ ά»¤ÓÎϷ״̬ ±àдÕâÑùµÄÓ¦ÓóÌÐòʱ£¬ÎÒ»áÊ×ÏÈÈ·¶¨¾¡¿ÉÄÜÉÙµÄÒ»×鹦ÄÜ£¬½«Æä×÷ΪÒÔºó¹¤×÷µÄ»ù´¡¡£¾ßÌåµ½ Sudoku£¬¾ÍÊÇÊ×ÏÈ´´½¨±ØÒªµÄÊý¾Ý½á¹¹£¬ÒÔ±£´æÓÎϷ״̬¡£ ¿ÉÒÔ¼òµ¥µØ½«ÌîÊýÓÎÏ·±íʾΪһ¸ö¶þάÊý×顣ÿ¸öµ¥Ôª¸ñ±ØÐëÄÜ´æ´¢Ò»¸öÊý×Ö£¬»òÕßΪ¿Õ¡£.NET Framework 2.0 Äܹ»Ìṩ¿ÉÒÔΪ¿ÕµÄÀàÐÍÕâÖÖÐÎʽ£¬´Ó¶ø¸øÎÒÃÇ´øÀ´ÁËÒ»ÖÖÁ¼ºÃµÄ½â¾ö·½°¸¡£.NET Framework 2.0 ÖС°¿ÉÒÔΪ¿ÕµÄÀàÐÍ¡±ÔÊÐíÖµÀàÐÍҲΪ¿ÕÖµ£¬Õâ´ú±í×ÅËüʹÓÃÁËÒ»ÖÖеķºÐͽṹ£ºNullable<T>¡£Nullable<T> ¿ÉÒÔÔÚÄÚ²¿±£´æÁ½¸öÖµ£¬Ò»¸öÊÇÀàÐÍ T£¨ÔÚÕâÀT ÊÇÒ»¸ö·ºÐÍÀàÐͲÎÊý£¬¿ÉÒÔΪÈÎÒâµÄÖµÀàÐÍ£¬µ«²»ÄÜΪÁíÒ»¸ö¿ÉÒÔΪ¿ÕµÄÀàÐÍ£©£¬´æ´¢µÄÊÇʵÀýµÄÖµ£»ÁíÒ»¸öÊDz¼¶ûÐÍ£¬±íÃ÷¸ÃʵÀýÊÇ·ñÓÐÖµ¡£.NET Framework 1.1 Öв»ÄÜʹÓ÷ºÐÍ£¬µ«ÎÒ»¹ÓÐÆäËû°ì·¨¡£¶ÔÓÚ¼¸¸öÏëÒªÖ§³Ö¿ÕÖµµÄÀàÐÍ£¬ÎÒ¿ÉÒÔ´´½¨×Ô¼ºµÄÌæ´úÀàÐÍ£¬Èç NullableByte ºÍ NullableInt¡£Ã¿ÖÖÌæ´úÀàÐͶ¼ÊÇÒ»ÖÖ°üº¬Ïà¹ØÀàÐ͵ÄÖµ£¨Èç byte ÊÊÓÃÓÚ NullableByte£¬int ÊÊÓÃÓÚ NullableInt£©ºÍÒ»¸ö²¼¶ûÖµµÄ½á¹¹£¬²¼¶ûÖµ±íʾÕûÊýÖµÖÐÊÇ·ñº¬ÓпÉÓÃÖµ¡£ÕâËäȻûÓÐ .NET Framework 2.0 µÄ·ºÐͽâ¾ö·½°¸ÄÇô¸ßÃ÷£¬´Ó C# 2.0 Ìṩ¶Ô Nullable<T> µÄÓï·¨Ö§³ÖÖ®ºó¸üÊÇÈç´Ë£¬µ«¾ÍʵÏÖÎÒµÄÄ¿±ê¶øÑÔ£¬ÒѾ×ã¹»ÁË¡£ÓÎÏ·Íø¸ñÖеÄÿ¸öµ¥Ôª¸ñ»òÕßΪ¿Õ£¬»òÕßÊÇÒ»¸ö½ÏСµÄÊýÖµ£¬ËùÒÔÎÒ¿ÉÒÔ°´ÕÕÒ»¸ö NullableBytes Êý×éÀ´±£´æÍø¸ñ¡£ [Serializable] public sealed class PuzzleState :ICloneable, IEnumerable { private NullableByte[,] _grid; private readonly byte _boxSize; private readonly byte _gridSize; ... } PuzzleState Àà°üº¬¼¸ÌõÐÅÏ¢£¬°üÀ¨Íø¸ñµÄ´óСºÍÄÚÈÝ¡£Ä¿Ç°µÄʵÏÖ³ÌÐò½öÖ§³Ö 9x9 Sudoku Íø¸ñ£¬µ«ÊǾÍêÉÆºó£¬¸ÃÓ¦ÓóÌÐò¿ÉÒÔ»ñµÃÀ©Õ¹£¬Äܹ»Ö§³Ö¸ü´óµÄÍø¸ñ£¬ÉõÖÁÊÇ·ÇÕý·½ÐÎÍø¸ñ¡£ PuzzleState ×îÖØÒªµÄÌØÕ÷Ö®Ò»ÊÇ ICloneable½Ó¿ÚµÄʵÏÖ¡£¸Ã½Ó¿ÚʵÏÖºó£¬Äú¿ÉÒÔÇáËɵؽøÐÐ PuzzleState ʵÀýµÄÉ±´£¬½«ÆäÉîÈëÓ¦Óõ½½â¾ö·½°¸µÄÆäËû¸÷¸ö·½Ã棬ÈçÓÎÏ·½â·¨¡¢ÓÎÏ·Éú³É£¬ÉõÖÁ»¹¿ÉÒÔÓÃÔÚÓëÓÎÏ·Ïà¹ØµÄ¹¦ÄÜÖУ¬Èç³·ÏûÖ§³Ö¡£ object ICloneable.Clone() { return this.Clone(); } public PuzzleState Clone() { return new PuzzleState(this); } private PuzzleState(PuzzleState state) { _boxSize = state._boxSize; _gridSize = state._gridSize; _grid = (NullableByte[,])state._grid.Clone(); ... } ·ÖÎöºÍÍê³ÉÌîÊýÓÎÏ·
Íê³É Sudoku ÌîÊýÓÎÏ·µÄ¹ý³Ìʵ¼ÊÉÏÊÇÒ»¸öÌØÊâµÄËÑË÷¹ý³Ì£¬Òò´Ë£¬ÎÒ¿ÉÒÔ½«±ê×¼µÄËÑË÷Ëã·¨ÔËÓõ½ÓÎÏ·µ±ÖС£Ò»ÖÖ³£¼ûµÄËÑË÷Ëã·¨ÊÇÉî¶ÈÓÅÏÈËÑË÷ (DFS)£¬ÔÚ´óѧµÄÊý¾Ý½á¹¹ºÍËã·¨¿Î³ÌÖУ¬Õâͨ³£ÊÇÊ×ÏÈҪѧµÄËã·¨Ö®Ò»¡£ËÑË÷ʱ£¬ÓеÄʵÀýÐèҪ̽Ѱ¶àÌõ·¾¶£¬ÔÚ DFS ÖУ¬Òª¶ÔÆäÖÐÒ»Ìõ·¾¶½øÐÐÕûÌå̽Ѱ£¬°üÀ¨Óɸ÷¾¶Òý³öµÄËùÓз¾¶£¬È»ºóÔÙÒÀ´Î¼ì²éÆäËû·¾¶¡£ËäÈ»¿ÉÒÔʹÓÃÏÔʽ¶ÑÕ»µÄ·½Ê½Ö´ÐÐ DFS£¬µ«ÎÒÃÇͨ³£Ê¹Óõݹ鷨¡£¼ÙÉèÔÚÒ»¸öÊ÷½ÚµãÊý¾Ý½á¹¹ÖУ¬³ýÒ»ÁÐ×Ó½ÚµãÍ⣬»¹´æ´¢ÓÐÒ»¸öÖµ¡£¿ÉÒÔͨ¹ýÈçÏ·½Ê½¶ÔÊ÷ÄÚÒ»¸ö¾ßÌåÖµÖ´ÐÐ DFS ¹ý³Ì£º public class TreeNode<T> where T :IComparable { public TreeNode(T value) { Value = value; } public T Value; public List<TreeNode<T>> Children = new List<TreeNode<T>>(); public TreeNode<T> DfsSearch(T v) { if (Value.CompareTo(v) == 0) return this; foreach (TreeNode<T> child in Children) { TreeNode<T> rv = child.DfsSearch(v); if (rv != null) return rv; } return null; } } ÔÚ TreeNode<T> Éϵ÷Óà DfsSearch ÒÔ¼ì²é½ÚµãÄÚ´æ´¢µÄÖµÊÇ·ñÓëÒªËÑË÷µÄÖµÏàͬ¡£Èç¹ûÏàͬ£¬¼´·µ»Øµ±Ç°µÄ½Úµã¡£Èç¹û²»Í¬£¬Ôòµ÷ÓÃÿ¸ö×Ó½ÚµãµÄ DfsSearch ·½·¨£¬¼ì²éÒªËÑË÷µÄÖµÊÇ·ñ´æÔÚÓÚ¸Ã×Ó½ÚµãµÄ×ÓÊ÷ÉÏ¡£Í¨¹ýÕâÖÖ·½Ê½£¬ÎÒ¿ÉÒÔËÑË÷Õû¸öÊ÷½á¹¹¡£ Äú¿ÉÒÔÓÃÏàͬµÄ·½Ê½ËÑË÷ Sudoku µÄ½â·¨¡£½«ÓÎÏ·Å̵ÄÒ»¸ö¾ßÌå״̬µ±×÷Ê÷ÄÚµÄÒ»¸ö½Úµã¡£ÒÔ¸Ã״̬ΪÆðµã£¬ÎÒ¿ÉÒÔÔÚÍø¸ñÖеĿյ¥Ôª¸ñÄÚÊäÈëÒ»¸öÊý×Ö£¬×ªµ½ÆäËû¡°×Ó¡±×´Ì¬¡£Í¨¹ýÕâÖÖ·½Ê½£¬ÎÒ¿ÉÒÔ´Óÿ¸ö״̬´ïµ½Ò»¶¨ÊýÁ¿µÄ×Ó״̬£¨Ã¿ÔÚÒ»¸ö¿Õµ¥Ôª¸ñÖÐÊäÈëÒ»¸öÖµ£¬¼´µÃµ½Ò»¸ö״̬£©¡£¶ÔÓÚ 9x9 µÄÍø¸ñ£¬×î¶àÓÐ 729 ¸ö×Ó״̬£¬ÕâÊÇÒ»¸öÈ·¶¨µÄÊýÖµ¡£Òò´Ë£¬ÎÒ¿ÉÒÔʹÓúÜÉÙµÄ DFS ÀàËÆ´úÂë¶Ô Sudoku ÓÎÏ·µÄ½â·¨Ö´ÐÐËÑË÷¡£ static PuzzleState DfsSolve(PuzzleState state) { switch (CheckSolution(state)) { case PuzzleStatus.Solved:return state; case PuzzleStatus.CannotBeSolved:return null; default: NullablePoint cell = FindFirstEmptyCell(state); if (cell == null) return null; for (byte n = 0; n < state.GridSize; n++) { state[cell.Value] = n; PuzzleState result = DfsSolve(state); if (result != null) return result; } state[cell.Value] = null; return null; } } ÕâÖÖ·½·¨Óë Sudoku ʵÏÖ³ÌÐòµÄ²¿Êð·½·¨ÀàËÆ£¬¼ì²é PuzzleState ²¢È·¶¨ÊÇ·ñÒÑÍê³É£»µ±Ç°ÊÇ·ñ´¦ÓÚÎÞЧ״̬£¨Ö¸´æÔÚ³åÍ»£©²¢ÇÒÒÑÈ·¶¨ÎÞ·¨Íê³É£»»òÕßÊÇ·ñÈÔÓÐÒ»¸ö´¦ÓÚ½ø³ÌÖеÄδÍê³ÉÓÎÏ·¡£Ö´ÐÐÕâÒ»¼ì²éÓë TreeNode<T> µÄ DfsSearch ¼ì²éÀàËÆ£¬Ö÷ÒªÊÇ¿´ÒÑÕÒµ½µÄ½ÚµãÖÐÊÇ·ñ°üº¬ÕýÈ·Öµ£¬ÒÔ¼°¼ì²éÊÇ·ñ´æÔÚ½âµÄλÖá£Èç¹ûÕÒµ½Ò»¸ö½â£¬¼´½«Æä·µ»Ø¡£Èç¹ûÕÒµ½µÄÊÇÒ»¸öÎÞЧ״̬£¬Ôò˵Ã÷ÔÚij´¦³ö´í£¬ÐèÒªÔ··µ»Ø¡£Èç¹ûÓÎÏ·ÈÔÔÚ¼ÌÐø£¬ÐèÒªÔÚÒ»¸ö¿Õµ¥Ôª¸ñÖÐÊäÈëÒ»¸öÊý×Ö¡£ÕÒ³öÊ׸öΪ¿ÕµÄµ¥Ôª¸ñ£¬ÒÀ´ÎÊäÈëÿ¸ö¿ÉÄÜÖµ¡£Èç¹û¸ÃÓÎÏ·Óн⣬ÆäÖÐij¸öÖµÒ»¶¨ÊÇÕýÈ·µÄ¡£ÎÒÔÚÿ¸öµ¥Ôª¸ñÖÐÊäÈëÒ»¸öÊý×Ö£¬È»ºóµÝ¹éµ÷Óà DfsSolve¡£Ö»ÒªÓÎÏ·Óн⣬×îºó¾ÍÒ»¶¨Äܹ»ÕÒµ½¡£ Òź¶µÄÊÇ£¬ÕâÖÖ·½·¨ÓÐÒ»¸öÖÂÃüµÄȱµã£ºÍê³ÉÓÎÏ·ÐèÒªÏ൱³¤µÄʱ¼ä¡£¶ÔÓÚ 9x9 µÄÓÎÏ·ÅÌ£¬ÐèҪΪÿ¸öµ¥Ôª¸ñÊÔÌîµÄÖµ×î¸ßΪ¾Å¸ö¡£¶ÔÓÚÐèÒªÌî³ä 81 ¸öµ¥Ôª¸ñµÄÓÎÏ·ÅÌ£¬ÐèÒªÊÔÌîµÄÅÌÊý¸ß´ï 981¡£¼´Ê¹ËٶȴﵽÿʮÒÚ·ÖÖ®Ò»Ãë´¦ÀíÒ»¸öµ¥Ôª¸ñÖеÄÒ»¸öÊý×Ö£¬½á¹ûÒ²ºÜ¿Ö²À£¬ÎҺ͸÷λ¶ÁÕß¶¼²»¿ÉÄÜÔÚÓÐÉúÖ®Äê¿´µ½ÓÎÏ·½áÊø£¡ÊÂʵÉÏ£¬ÕÒµ½½â֮ǰ£¬Ì«Ñô¶¼¿ÉÄÜÒѾ²»¸´´æÔÚÁË¡£ÎÊÌâµÄÖ¢½áÔÚÓÚ£¬ÕâÖÖ·½·¨ÐèÒªËÑË÷Õû¸öËÑË÷¿Õ¼ä£¬¼´Ê¹¿ÉÒÔͨ¹ýÐí¶à°ì·¨¶ÔËÑË÷Ê÷½øÐС°ÐÞÖ¦¼ôÒ¶¡±£¬ÀýÈçÏÞÖÆËÑË÷±ØÐëÖ´ÐеÄѰÕÒÊýÁ¿£¬µ«¶ÔÓÚËõ¶Ìʱ¼äÀ´Ëµ£¬ÕâЩ¶¼ÊDZˮ³µÐ½¡£ÎÒÃÇÍæ Sudoku ÓÎϷʱ¶¼ÖªµÀÓÐÐí¶à²½ÖèÊDz»±Ø½øÐеģ¬ÄÇΪʲô»¹ÒªÈüÆËã»úÈ¥ÊÔÄØ£¿ËùÒÔ£¬¶ªµôËüÃÇ£¡ ÎÒÃÇ¿ÉÒÔͨ¹ýÐí¶à¼¼ÇÉÈüÆËã»úʡȥһЩ²½Ö裬ÆäÖÐ×î¼òµ¥µÄÒ»ÖÖÊÇ£¬Í¨Öª¼ÆËã»úij¸ö¾ßÌåµÄµ¥Ôª¸ñÖÐÒ»¶¨²»»áº¬ÓÐÄÄЩÊý×Ö¡£ÕâÑù£¬¼ÆËã»ú½øÐÐÇ¿Á¦ËÑË÷ʱֻÐèÒª¿¼ÂÇÊ£ÓàµÄ¿ÉÄÜÊý×Ö¡£ ¶ÔÍø¸ñÄÚµÄÿ¸öµ¥Ôª¸ñ£¬ÎһᱣÁôÒ»¸öλÊý×é¡£ÕâÀïÓÐÒ»ÖÖ˼·£¬¼´Í¨¹ý¼ì²éÆäËû×Ô¶¯ÉèÖÃÊý×Ö£¬»òÕßµ¥Ôª¸ñµÄÁС¢ÐкͿòÖÐÒÑ¡°¸ø³ö¡±µÄÊý×Ö£¬ÎªÃ¿¸öµ¥Ôª¸ñÅųýÒ»¶¨Á¿µÄ¿ÉÄÜÊý×Ö£»ÒѳöÏÖµÄÈκÎÊý×Ö¶¼²»¿ÉÄÜÊǸõ¥Ôª¸ñÖÐÓ¦ÓеÄÊý×Ö¡£ÔÔòÉÏ£¬Î»Êý×éÊÇÒ»×鿪¹Ø£¬Í¨¹ý´ò¿ª»ò¹Ø±ÕÀ´Î¬»¤Ò»×éÏîÄ¿µÄ²¼¶ûÖµ¡£ÔÚ.NET Framework ÖбíʾλÊý×éµÄ×î¼ò±ã·½·¨ÊÇʹÓà System.Collections.BitArray Àà¡£µ«ÊÇ£¬ÎÒÔÚʵÏÖ³ÌÐòÖÐʹÓøÃÀಢ½øÐÐÁËһЩÅäÖÃÎļþ²âÊÔÖ®ºó£¬ÎÒ·¢ÏÖ´´½¨ÊʺϾßÌåÒªÇóµÄеÄʵÏÖ³ÌÐò¸üΪ·½±ã¡£ËùÒÔ£¬ÎÒ´´½¨ÁË×Ô¼ºµÄʵÏÖ³ÌÐò FastBitArray£¬ËüλÓÚ Collections Ŀ¼Ï嵀 FastBitArray.cs ÎļþÖС££¨Èç¹ûÄú±àд¹ýλͰ£¬¾Í»á¶Ô¸ÃʵÏÖ³ÌÐò·Ç³£ÊìϤ¡££©ÒòΪ Sudoku ʵÏÖ³ÌÐò²»ÐèÒª´¦Àí´óµÄÖµ£¨¼Ù¶¨Æä½öÖ§³Ö 9x9 ÓÎÏ·ÅÌ£©£¬FastBitArray ¾Í¿ÉÒÔ¶Ôµ¥Ò»ÎÞ·ûºÅÕûÊý½øÐÐÏÞÖÆ£¬Ö»ÔÊÐí×î¶à±£Áô 32 ¸ö²¼¶ûÖµ¡£Í¨¹ý¼òµ¥µÄλ²Ù×÷¼´¿ÉʵÏÖ¶Áд²Ù×÷¡£ public bool Get(int index) { return (_bits & (1u << index)) != 0; } public void Set(int index, bool value) { bool curVal = Get(index); if (value && !curVal) { _bits |= (1u << index); _countSet++; } else if (!value && curVal) { _bits &= ~(1u << index); _countSet--; } } Ö®ºó£¬ÎһᱣÁôÒ»¸ö FastBitArray ʵÀýµÄ¶þάÊý×飬Óë°üº¬Íø¸ñÖи÷µ¥Ôª¸ñµÄÖµµÄ¶þάÊý×éÏà¶ÔÓ¦¡£ Á˽ⵥԪ¸ñÖеĿÉÄÜÖµ¿ÉÒÔʹ DfsSolve ·½·¨¸ü¿ìµØ·¢»Ó×÷Óá£ÎÒ¿ÉÒÔ½«ËÑË÷ÏÞÖÆÔÚ¸÷µ¥Ôª¸ñµÄ±¸Ñ¡ÖµÖУ¬¶ø²»±ØÏñÒÔǰÄÇÑù£¬ÔÚÿ¸öµ¥Ôª¸ñÖÐÊÔÌîËùÓÐÖµ¡£´ËÍ⣬ͨ¹ý DfsSolve ʵÏÖ³ÌÐò²éÕÒ²¢Ìî³äÕÒµ½µÄµÚÒ»¸ö¿Õµ¥Ôª¸ñ£¬ÕâÆäʵÊÇÒ»¸öÎ󵼡£ÔÚ¾ßÌåµ¥Ôª¸ñÄÚÊÔÌîµÄÖµÖÐÖ»ÓÐÒ»¸öÊÇÕýÈ·µÄ£¬Õâ¾Í¸øÁËÎÒÃÇÒ»ÖÖÆôʾ£ºÈç¹û´ÓÎÒÃÇÖªµÀµÄ±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñ¿ªÊ¼£¬½«Äܹ»Ê¡È¥´óÁ¿µÄËÑË÷¹¤×÷¡£Òò´Ë£¬Òª½øÒ»²½ÍêÉÆ DfsSolve£¬¿ÉÒÔÔÚËùÓпյ¥Ôª¸ñÖÐÕÒ³ö±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñ¡£ÔÚ DfsSolve ÖÐÿÉ趨һ¸öµ¥Ôª¸ñ£¬Ëæ¼´¸üб¸Ñ¡Êý×ÖµÄÊý×飬ɾ³ýÒѾΪµ¥Ôª¸ñÉèÖõı¸Ñ¡Êý×Ö¡£ ´´½¨ DfsSolve ÊÇΪÁË˵Ã÷Óë½âÊÍ£¬Êµ¼ÊÉÏ£¬ÎÒÓÃÔÚ Sudoku ÖеÄʵÏÖ³ÌÐòÓëÖ®·Ç³£ÏàËÆ¡£ÔÚ Solver.cs ÎļþÖУ¬ÎÒµÄ Solver ÀàÖÐÓÐÕâÁ½¸öºËÐÄ·½·¨µÄ¼òÂÔ°æ±¾¡£ÇëÁôÒâËüÃÇÓë DfsSolve µÄÏàËÆÖ®´¦£¬ÒÔ¼°ÎÒÌáµ½µÄ²¹³ä½¨Òé¡£ private static SolverResults SolveInternal( PuzzleState state, SolverOptions options) { state = state.Clone(); FastBitArray [][] possibleNumbers = FillCellsWithSolePossibleNumber(state, options.EliminationTechniques); switch (state.Status) { case PuzzleStatus.Solved: case PuzzleStatus.CannotBeSolved: return new SolverResults(state.Status, state, 0); default: if (options.AllowBruteForce) { SolverResults results = BruteForceSolve( state, options, possibleNumbers); return results; } else return new SolverResults( PuzzleStatus.CannotBeSolved, state, 0, null); } } private static SolverResults BruteForceSolve(PuzzleState state, SolverOptions options, FastBitArray [][] possibleNumbers) { //ÕÒµ½±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñ ArrayList bestGuessCells = new ArrayList(); int bestNumberOfPossibilities = state.GridSize + 1; for (int i = 0; i < state.GridSize; i++) { for (int j = 0; j < state.GridSize; j++) { int count = possibleNumbers[i][j].CountSet; if (!state[i, j].HasValue) { if (count < bestNumberOfPossibilities) { bestNumberOfPossibilities = count; bestGuessCells.Clear(); bestGuessCells.Add(new Point(i, j)); } else if (count == bestNumberOfPossibilities) { bestGuessCells.Add(new Point(i, j)); } } } } //Ñ¡ÔñÒ»¸ö SolverResults results = null; if (bestGuessCells.Count > 0) { Point bestGuessCell = (Point)bestGuessCells[ RandomHelper.GetRandomNumber(bestGuessCells.Count)]; FastBitArray possibleNumbersForBestCell = possibleNumbers[ bestGuessCell.X][bestGuessCell.Y]; for(byte p=0; p<possibleNumbersForBestCell.Length; p++) { if (possibleNumbersForBestCell[p]) { PuzzleState newState = state; newState[bestGuessCell] = p; SolverOptions tempOptions = options.Clone(); if (results != null) { tempOptions.MaximumSolutionsToFind = (uint)( tempOptions.MaximumSolutionsToFind.Value a€¡° results.Puzzles.Count); } //ÖØ¸´ SolverResults tempResults = SolveInternal(newState, tempOptions); if (tempResults.Status == PuzzleStatus.Solved) { if (results != null && results.Puzzles.Count > 0) { results.Puzzles.AddRange(tempResults.Puzzles); } else { results = tempResults; results.NumberOfDecisionPoints++; } if (options.MaximumSolutionsToFind.HasValue && results.Puzzles.Count >= options.MaximumSolutionsToFind.Value) return results; } newState[bestGuessCell] = null; } } } return results != null ? results :new SolverResults(PuzzleStatus.CannotBeSolved, state, 0, null); } ÕâÀïÒªÍê³ÉÐí¶à²Ù×÷¡£SolveInternal ·½·¨´Ó¸´ÖÆ PuzzleState ¿ªÊ¼£¬È»ºóÔÚ¸±±¾¶ø·ÇÔ¼þÉϽøÐвÙ×÷¡£ÕâÑù£¬µ÷ÓóÌÐòÌṩµÄ PuzzleState ¾Í²»»áÊܵ½ Solver Æô¶¯²Ù×÷µÄÓ°Ï졣ȻºóʹÓà FillCellsWithSolePossibleNumber ·ÖÎöµ±Ç°µÄÓÎÏ·ÅÌ£¬ÕÒ³öÖ»ÓÐÒ»¸ö±¸Ñ¡Êý×ֵĵ¥Ôª¸ñ£¬È»ºóÌîÈëÊý×Ö£¨ºóÃæÎһὲ½âÕâÒ»·½·¨£©¡£Õâʱ£¬Èç¹ûÕÒµ½ÁËÓÎÏ·Å̵Ľⷨ»òÕß±»È·¶¨ÎªÎ޽⣬¸Ã·½·¨¼´»á·µ»Ø¡£·ñÔò£¬Ê¹ÓÃÇ¿Á¦ËÑË÷À´Ìî³äÒ»¸öµ¥Ôª¸ñ¡£±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñ½«»á³ÉΪÏÂÒ»¸öÒªÌîÈëÊý×ֵĵ¥Ôª¸ñ£¨Èç¹û±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñ²»Ö¹Ò»¸ö£¬ÔòËæ»úÑ¡ÔñÒ»¸ö£©¡£ÌîÈëÊý×Öºó£¬ÏµÍ³½«µ÷Óà SolveInternal ÖØ¸´ÕâÒ»¹ý³Ì£¬½ø³ÌÖØÐ¿ªÊ¼£¬ÒÑÌîÈë¶à¸öµ¥Ôª¸ñµÄÇé¿ö³ýÍâ¡£ Äú»á×¢Òâµ½£¬SolverOptions ÀàµÄʵÀýÔÚÇó½â²Ù×÷ÖУ¬»áÍÆ¶¯Ä³Ð©¾ö¶¨µÄ²úÉú¡£ÒÔÏ´úÂë¿ÉÏÔʾ SolverOptions ¹«¹²½Ó¿Ú£º public sealed class SolverOptions :ICloneable { public SolverOptions(); public NullableUint MaximumSolutionsToFind { get; set; } public TechniqueCollection EliminationTechniques { get; } public bool AllowBruteForce { get; set; } public SolverOptions Clone(); } MaximumSolutionsToFind ¿ÉÒÔʹ Solver µÄ¿Í»§¶ËÈ·¶¨ÐèÒª³¢ÊÔ½âµÄÊýÄ¿£»Ö»ÓÐÕâЩ½âÈ«²¿ÕÒµ½ºó£¬»òÇó½âÆ÷È·¶¨ÇëÇóµÄÊý×ÖÎÞ·¨ÕÒµ½Ê±£¬Çó½âÆ÷²Å»áÖÕÖ¹¡£Çë×¢Ò⣬MaximumSolutionsToFind ÊÇÒ»ÖÖ×Ô¶¨Òå NullableUint ÀàÐÍ£»Èç¹ûÊÇ null£¬Çó½âÆ÷»á²éÕÒËùÓпÉÄܵÄÎÞ½ç½â¡£µ«¶Ô¸ÃÖµµÄ×îÆÕ±éÓ÷¨ÊÇ£¬½«ÆäÉèÖÃΪ 1 »ò 2¡£Í¨¹ý¶ÔÒ»¸ö½âµÄËÑË÷£¬ÎÒ¿ÉÒÔѸËÙÈ·¶¨¸ÃÓÎÏ·ÊÇ·ñ¿É½â¡£Í¨¹ýËÑË÷Á½¸ö½â£¬ÎÒ¿ÉÒÔÈ·¶¨ÓÎÏ·ÓÐÒ»¸ö½â»¹ÊÇÁ½¸ö½â£¨Èç¹ûÇó½âÆ÷ÕÒµ½Á½¸ö½â£¬¼´Ê¹»¹ÄÜÕÒµ½¸ü¶àµÄ½â£¬µ«ÖÁÉÙÎÒÒѾȷ¶¨£¬¸ÃÓÎÏ·²»Ö¹ÓÐÒ»¸ö½â£©¡£AllowBruteForce ÊôÐÔ¾ö¶¨ÁËÇó½âÆ÷ÊÇ·ñÔÊÐíÇ¿Á¦ËÑË÷£¬»¹ÊÇÖ»ÄÜʹÓà FillCellsWithSolePossibleNumber ·½·¨Ìî³äÊý×Ö¡£FillCellsWithSolePossibleNumber ʹÓà EliminationTechniques ÊôÐÔΪÿ¸öµ¥Ôª¸ñÖ¸¶¨¿ÉÄܵÄÊý×Ö¡££¨ÎÒ»áÔÚÉú³ÉÓÎÏ·Öн²½â AllowBruteForce ºÍ FillCellsWithSolePossibleNumber¡££© ÌÖÂÛÔÐÍ DfsSolve ʱ£¬ÎÒ»á˳±ã½âÊÍһϠCheckSolution¡£ÔÚʵ¼ÊµÄ Sudoku ʵÏÖ³ÌÐòÖУ¬CheckSolution ÓÉ PuzzleState.Status ´úÌæ£¬ºóÕߵŦÄÜÊÇ·ÖÎöµ±Ç°µÄÓÎϷ״̬£¬²¢·µ»ØÐÅÏ¢,˵Ã÷ÓÎÏ·ÊÇÒÑÍê³É¡¢Î´Íê³É»¹ÊÇ´¦ÓÚijÖÖÖмä״̬¡£ public PuzzleStatus Status { get { if (_status == PuzzleStatus.Unknown) _status = AnalyzeSolutionStatus(); return _status; } } private PuzzleStatus AnalyzeSolutionStatus() { FastBitArray numbersUsed = new FastBitArray(_gridSize); //È·¶¨¸÷ÁÐÖÐÎÞÖØ¸´Êý×Ö for (int i = 0; i < _gridSize; i++) { numbersUsed.SetAll(false); for (int j = 0; j < _gridSize; j++) { if (_grid[i, j].HasValue) { int value = _grid[i, j].Value; if (numbersUsed[value]) return PuzzleStatus.CannotBeSolved; numbersUsed[value] = true; } } } //È·±£¸÷ÐÐÖÐÎÞÖØ¸´Êý×Ö for (int j = 0; j < _gridSize; j++) { numbersUsed.SetAll(false); for (int i = 0; i < _gridSize; i++) { if (_grid[i, j].HasValue) { int value = _grid[i, j].Value; if (numbersUsed[value]) return PuzzleStatus.CannotBeSolved; numbersUsed[value] = true; } } } //È·±£¸÷¿òÖÐÎÞÖØ¸´Êý×Ö for (int boxNumber = 0; boxNumber < _gridSize; boxNumber++) { numbersUsed.SetAll(false); int boxStartX = (boxNumber / _boxSize) * _boxSize; for (int x = boxStartX; x < boxStartX + _boxSize; x++) { int boxStartY = (boxNumber % _boxSize) * _boxSize; for (int y = boxStartY; y < boxStartY + _boxSize; y++) { if (_grid[x, y].HasValue) { int value = _grid[x, y].Value; if (numbersUsed[value]) return PuzzleStatus.CannotBeSolved; numbersUsed[value] = true; } } } } //ÏÖÔÚÖ¸³öÕâÊÇÒÑÍê³ÉµÄÌîÊýÓÎÏ· //»¹ÊÇÈÔÔÚ½øÐÐÖУ¨¸ù¾ÝÊÇ·ñ´æÔÚ©¶´£© for (int i = 0; i < _gridSize; i++) { for (int j = 0; j < _gridSize; j++) { if (!_grid[i, j].HasValue) return PuzzleStatus.InProgress; } } //Èç¹ûÄÜÔËÐе½ÕâÒ»²½£¬Ôò±íÃ÷¸Ã״̬Óн⣡ return PuzzleStatus.Solved; } PuzzleState.Status ͨ¹ý¼¸¸ö²½Öè·ÖÎöµ±Ç°µÄ½â¡£Ëü¼ì²é¸Ã״̬µÄ PuzzleStatus ÊÇ·ñÒѾ¹ý¼ÆËã²¢±»»º´æ¡£Èç¹ûÊÇ£¬PuzzleStatus »á·µ»Ø»º´æÖµ£¨¶ÔÓÎÏ·µÄÈκθü¸Ä¶¼»áʹ»º´æÎÞЧ£©¡£¼Ù¶¨¸Ã״̬µÄ PuzzleStatus ȷʵÒѾ¹ý¼ÆË㣬Status ¾Í»áʹÓà AnalyzeSolutionStatus ·½·¨²é¿´ËùÓÐÁС¢Ðлò¿òÖе±Ç°ÊÇ·ñ´æÔÚÖØ¸´Êý×Ö¡£Èç¹û´æÔÚÖØ¸´£¬ÄÇôºÜÏÔÈ»£¬ÓÎÏ·ÎÞ·¨Íê³É¡£È»ºó£¬ÏµÍ³»á²é¿´Íø¸ñÖÐÊÇ·ñ»¹´æÔÚ¿Õµ¥Ôª¸ñ¡£Èç¹ûÓУ¬ÓÎÏ·¼ÌÐø½øÐС£Èç¹ûûÓУ¬ÓÎÏ·¼´¸æÍê³É¡£Çë×¢Ò⣬AnalyzeSolutionStatus ²»ÄÜÖ¸³öÕýÔÚ½øÐеÄÓÎÏ·ÊÇ·ñ¿ÉÒÔ±»Íê³É£¬Ö»ÄÜÖ¸³öÓÎÏ·µ±Ç°ÊÇ·ñÒÑÍê³É¡¢È·¶¨²»¿É½â£¬»òÕß¿ÉÄÜÓн⡣Äú¿ÉÒÔ¿´µ½£¬Solver Äܹ»Ö´ÐÐËÑË÷£¬È·¶¨ÓÎÏ·µÄÕæÊµ¿É½âÐÔ¡£ ÕâÒ²ÊÇ Solver µÄ»ù±¾¹¦ÄÜ¡£¹«¹² Solve ·½·¨°üÀ¨ SolveInternal ·½·¨£¬ÆäËûһЩ²Ù×÷ÔòÓÃÀ´×·×Ùͳ¼ÆÊý¾Ý - ÈçºÎÍê³ÉÓÎÏ·£¬ÓÎÏ·Éú³ÉÖÐÄÄÌõÐÅÏ¢ÊÇÓÐÓÃÐÅÏ¢¡£ÎÞÐèÌ«¶à´úÂë¼´¿ÉʵÏÖÒ»¸ö·Ç³£ÓÐÓöøÇÒÇ¿´óµÄÀà¡£ Éú³ÉÓÎÏ·
¿´ÆðÀ´£¬Óдý½â¾öµÄ×î´óÄÑÌâÓ¦¸ÃÊÇÈçºÎÉú³ÉÖ»ÓÐÒ»¸öÓÐЧ½â·¨µÄËæ»úÌîÊýÓÎÏ·£»¶øÊÂʵÉÏ£¬ÓÐÒѱàºÃµÄËùÓÐÆäËû´úÂë×ö»ù´¡£¬±àдÕⲿ·Ö´úÂë»áÏ൱ÈÝÒס£ÎÒÏàÐÅ£¬ÍøÉÏÓÐ×ã¹»¶àµÄËã·¨¿ÉÒÔÓÃÓÚÉú³ÉÓÎÏ·¡£µ«ÊÇÎÒÇéԸѡÔñ×Ô¼ºµÄËã·¨£¬ÔÒòÈçÏ£ºËüºÜÓÅÐ㣬ʹÓÃÒÑΪ´ËʵÏÖµÄÀàºÍ·½·¨¿ÉÒÔÇáËɱàд´úÂ룻ËÙ¶ÈÒ²Ï൱¿ì£»×îÖØÒªµÄÒ»µãÊÇ£¬ËüµÄЧ¹û²»´í¡£µ±È»£¬Èç¹ûÄúÔ¸Ò⣬ÍêÈ«¿ÉÒÔ´Ó¸½Ëæ´úÂëÖÐɾ³ý¸ÃÉú³ÉÆ÷£¬´úÖ®ÒÔ×Ô¼ºµÄËã·¨¡£ Ö§³ÖÕâÒ»Ëã·¨µÄ˼··Ç³£¼òµ¥£¬ÓÉÁ½²¿·Ö×é³É¡£Ê×ÏÈ£¬ÐèÒª´´½¨Ò»¸öÍêÕûºÍËæ»úµÄ Sudoku ½â·¨£¬Ò²¾ÍÊÇ˵£¬Í¨¹ýÕâÒ»Ëæ»ú½â·¨£¬Íø¸ñÖÐËùÓÐ 81 ¸öµ¥Ôª¸ñ¾ùÌîÓÐÊý×Ö£¬Ã¿ÐС¢Ã¿ÁкÍÿ¸ö¿òÖоùº¬Óв»Öظ´µÄ 1 ÖÁ 9 ¾Å¸öÊý×Ö¡£Õ§¿´ÉÏÈ¥£¬ÕâÊÇÒ»¸öÎÞ½âµÄÄÑÌ⣬µ«ÊÇÎÒÒѾͨ¹ýÒ»¶¨µÄ´úÂë½â¾öÁËÕâÒ»ÎÊÌ⣺ Solver.Solve¡£Çë²»Òª¶Ô´ËÌ«¹ý¾ªÑÈ£¡Äú¿ÉÒԻعËÒ»ÏÂÎÒ¹ØÓÚÍê³ÉÌîÊýÓÎÏ·ºÍÕÒµ½ÏÂÒ»¸öҪǿÁ¦ËÑË÷µÄµ¥Ôª¸ñµÄÌÖÂÛ£¬ÎÒ½«´Ó±¸Ñ¡Êý×Ö×îÉٵĵ¥Ôª¸ñÖÐËæ»úÑ¡ÔñÒ»¸ö¡£Òò´Ë£¬Îª´´½¨Ò»¸öËæ»ú¶øÇÒÍêÕûµÄ Sudoku ½â·¨£¬ÎÒÐèÒª×öµÄÈ«²¿¹¤×÷¾ÍÊÇÍê³ÉÒ»¸ö¿ÕÍø¸ñ¡£ÎÒÔÚÕâÀï×öÁË´óÁ¿¹¤×÷£¬ÒòΪÕ⽫ÊǹؼüµÄÒ»²½¡£Ê×´Îͨ¹ýʱ£¬Çó½âÆ÷½«Ëæ»úÔÚ 81 ¸öµ¥Ôª¸ñÖеÄÒ»¸öÌîÈëÊýÖµ£¬ÒòΪËùÓÐ 81 ¸öµ¥Ôª¸ñ¶¼ÓÐÏàͬÊýÄ¿µÄ±¸Ñ¡Êý×Ö¡£Ôڸõ¥Ôª¸ñÌîÈëÊý×Öºó£¬Ò»Ð©µ¥Ôª¸ñµÄ±¸Ñ¡Êý×־ͻá±ÈÆäËûµ¥Ôª¸ñÉÙ£¬solver ½«»á´ÓÕâЩµ¥Ôª¸ñÖÐËæ»úÑ¡ÔñÒ»¸ö¡£ÕâÑùÖ´ÐÐÏÂÈ¥£¬½«»áÉú³ÉÒ»¸öËæ»ú½â·¨¡£Ö»ÒªÒ»Á½ÐдúÂ룬¾ÍÄÜÉú³ÉÒ»¸öËæ»úµÄÓÐЧ½â·¨¡£ PuzzleState newPuzzle = Solver.Solve( new PuzzleState(), new SolverOptions()).Puzzle; ÉÏÃæ¾ÍÊÇÒª×öµÄÈ«²¿¹¤×÷£¡¾¡¹Ü¿ÉÄÜ´æÔÚ¸üΪÓÐЧµÄ·½Ê½£¬µ«ÊÇÉÏÊö´úÂëÖ»ÐèÔÚ±ãЯʽµçÄÔÉÏÔËÐм¸ºÁÃ룬±ÈÎÒÔ¤ÏëµÄ»¹¿ì¡£ Ëã·¨µÄÏÂÒ»²½ÊÇ£¬È«ÃæÕ¹¿ª²¢²»¶Ï´Óµ¥Ôª¸ñÖÐɾ³ýÊý×Ö£¬·½·¨ÊÇËæ»úÑ¡ÔñÍø¸ñÖÐÒÑÌî³äµÄÒ»¸öµ¥Ôª¸ñ²¢Çå³ýÆäÖеÄÊý×Ö¡£Ã¿É¾³ýÒ»¸ö£¬ÎÒ¾ÍÓÃÇó½âÆ÷È·¶¨Ò»´Î£¬¿´ÏÖÔÚÊÇ·ñ»¹ÓжàÖֽⷨ¡£ºÜÃ÷ÏÔ£¬ÔʼµÄÓÎÏ·ÅÌÖ»ÓÐÒ»¸ö½â·¨£¬É¾³ý¼¸¸öÊý×Öºó£¬Ó¦¸ÃÈÔȻֻÓÐÒ»¸ö½â·¨¡£µ«ÊÇÓÐЩʱºò£¬Çå³ýÁË´óÁ¿µ¥Ôª¸ñµÄÊý×Öºó£¬ÓÎÏ·Å̻ᴦÔÚÒ»¸ö¶à¿ÉÓ÷½·¨Íê³ÉµÄ״̬Ï£¬Ã¿ÖÖ״̬¶¼Í¨Ïò²»Í¬µÄ Sudoku ½â·¨¡£ÕâÓÐã£ÓÚ´´½¨½öÒ»ÖֽⷨµÄÓÎÏ·ÅÌÕâһĿ±ê£¬Òò´Ë±ØÐë±ÜÃâ³öÏÖÕâÖÖ״̬¡£Òò´Ë£¬Ã¿´Îɾ³ýºó£¬Solver ½«Í¨ÖªÎÒĿǰÊÇ·ñ״̬²»Ã÷¡£ÄÇÑùµÄ»°£¬ÎÒ¿ÉÒÔ³·Ïû¸Ãɾ³ý²½Ö裬ÒÔʵÏÖÓÐЧµÄÓÎÏ·ÅÌÆô¶¯¡£ÈçÏÂΪÕâÒ»·½·¨µÄºËÐÄ£¬ÔÚ Generator.cs ÎļþµÄ Generator ÀàÖÐÖ´ÐС£ Point [] filledCells = GetRandomCellOrdering(newPuzzle); int filledCellCount = filledCells.Length; for(int filledCellNum=0; filledCellNum < filledCellCount && newPuzzle.NumberOfFilledCells > _options.MinimumFilledCells; filledCellNum++) { byte oldValue = newPuzzle[filledCells[filledCellNum]].Value; newPuzzle[filledCells[filledCellNum]] = null; SolverResults newResults = Solver.Solve( newPuzzle, solverOptions); if (!IsValidRemoval(newPuzzle, newResults)) newPuzzle[filledCells[filledCellNum]] = oldValue; } GetRandomCellOrdering ·½·¨ÒÔËæ»ú˳ÐòÔÚÓÎÏ·Öд´½¨Ò»¸ö°üº¬ËùÓÐ 81 ¸öµ¥Ôª¸ñµÄÁÐ±í¡£IsValidRemoval ·½·¨»á½øÐмì²é£¬È·¶¨ÉÏÒ»´Îɾ³ýÊÇ·ñ×ñÊØÁËËùÓйæÔò£¬°üÀ¨ÓÎÏ·Ö»ÄÜÓÐÒ»¸ö½â·¨ÕâÒ»»ù±¾¹æÔò¡£Äú¿ÉÒÔÔڸôúÂëÖп´µ½£¬ÎÒ°´ÕÕ GetRandomCellOrdering ·µ»ØµÄ˳Ðò¶ÔËùÓÐ 81 ¸öµ¥Ôª¸ñ½øÐÐÑ»·²Ù×÷¡£¶ÔÓÚÿ¸öµ¥Ôª¸ñ£¬ÎҵijõÖÔÊÇɾ³ýËü£¬µ«Èç¹û¸ù¾Ý IsValidRemoval ×îÖÕ״̬ÎÞЧ£¬ÎһὫÆä»Ö¸´¡£ ÎÒµÄÉú³ÉÆ÷Ö´ÐеÄËã·¨³Æ×÷̰ÐÄËã·¨¡£Ò²¾ÍÊÇ˵£¬Ëü»áÖ´Ðз¢ÏÖµÄÿ¸öÓÐЧ²½Ö裬¼´Ê¹Èç¹ûûÓÐÕâÑù×ö£¬×îÖÕµÄÕûÌå½á¹ûÒ²»á¸üºÃ¡£Ê¹ÓÃ̰ÐÄËã·¨£¬¶àÊýÇé¿öÏ£¬¼´Ê¹²»Äܵõ½×î¼Ñ½á¹û£¬½á¹ûÒ²ÒѾºÜ²»´íÁË¡£´ËÍ⣬ºÜÄѶ¨ÒåʲôÊÇ×îºÃµÄ Sudoku ½â·¨£¬ÎÒÃÇҲû±ØÒªÕÒ³ö×î¼Ñ½â·¨¡£ÕâÊǼþºÃÊ¡£´´½¨ Sudoku ÓÎÏ·¿É¹éÈëÒ»Àà½Ð×÷ NP-complete µÄÎÊÌâ¡£ÔÚÍâÐÐÈËÑÛÖУ¬ÕâÒâζ×ÅÉú³ÉÒ»¸ö×î¼Ñ Sudoku ÌîÊýÓÎÏ·ÐèÒª»¨·ÑÏ൱³¤µÄʱ¼ä¡£¶øÊÂʵÉÏ£¬Ìṩ¡°ºÃ¡±½â·¨µÄÆôʾ¸üÊÜ»¶Ó¡£ ¹ØÓÚ Solver£¬ÎÒÒѾ×öÁ˼¸¸ö·½ÃæµÄ½âÊÍ¡£Ê×ÏÈ£¬Äú¿ÉÄÜÒѾעÒâµ½£¬ÔÚÓÎÏ·µÄÅäÖÃÑ¡ÏîºÍ GeneratorOptions ÀàÖУ¬Éú³ÉÆ÷¿ÉÒÔÉú³É 180 ¶È¶Ô³ÆµÄÌîÊýÓÎÏ·¡£Ò²¾ÍÊÇ˵£¬Ö»ÓÐ [10-x,10-y] λÖõĵ¥Ôª¸ñÌî³äÓÐÊý×Öʱ£¬²ÅÄÜÏò [x,y] λÖõĵ¥Ôª¸ñÌîÈëÊý×Ö£¨ÀýÈ磬Èç¹ûµÚÒ»ÁС¢µÚ¶þÐеĵ¥Ôª¸ñÖÐÓÐÊý×Ö£¬ÄÇôµÚ¾ÅÁС¢µÚ°ËÐеĵ¥Ôª¸ñÒ²Ó¦¸ÃÓÐÊý×Ö£¬·´Ö®ÒàÈ»£©¡£Ö»Ðè¶Ô֮ǰËùʾ´úÂëÉÔ¼Ó²¹³ä£¬¼´¿ÉÍê³ÉÕâ²½²Ù×÷¡£Ê×ÏÈ£¬Èç¹ûʹÓÃÁ˶ԳƷ¨Ôò£¬ÔòÓ¦ÐÞ¸Ä GetRandomCellOrdering ·½·¨·µ»ØµÄ˳Ðò£¬ÒÔ´´½¨¶Ô³Æµ¥Ôª¸ñµÄËæ»ú˳Ðò£¬¶ø²»Êǵ¥¸öµ¥Ôª¸ñµÄËæ»ú˳Ðò¡£ÕâÑù£¬¶Ô³ÆµÄµ¥Ôª¸ñÔÚ˳ÐòÖоͿÉÒÔ×ÜÊÇ´¦ÓÚÏàÁÚλÖá£Õâ¾ÍÒªÇó´úÂë¿ÉÒԳɶԶø²»Êǵ¥¸öµØÉ¾³ýÖµ¡£²¢ÇÒ£¬Èç¹ûɾ³ýÆäÖÐijһ¸ö¶øµ¼ÖÂÓÎÏ·ÎÞЧʱ£¬ÕâЩֵ¿ÉÒԳɶԻָ´¡£ Éú³ÉÆ÷»¹Ö§³Ö²»Í¬µÄÄѶȼ¶±ð¡£ÄѶȼ¶±ð»ùÓÚ²»Í¬µÄÅäÖÃÑ¡Ï°üÀ¨±ØÐëÌî³äµÄµ¥Ôª¸ñ×îСÊýÄ¿£¬ÒÔ¼°Íæ¼ÒÒªÍê³ÉÓÎÏ·ÐèҪʹÓõļ¼ÇÉ¡£ÕâЩ¼¼ÇÉ¿ÉÒÔʹÎÒÃÇ·µ»Ø Solver ºÍ SolverOptions.EliminationTechniques ÊôÐÔ¡£ Èç¹ûÄúÔĶÁ¹ý Sudoku Ïà¹ØµÄÊé¼®»òÉÏÍøä¯ÀÀ¹ýÏà¹ØµÄÐÅÏ¢£¬Äú»á·¢ÏÖ£¬¶ÔÓÚÈçºÎÍê³ÉÌîÊýÓÎÏ·£¬ÒѾÓÐÁËÐí¶à³ÉÌ׵ļ¼ÇÉ¡£ÎÒÔø¾½²¹ýÕâÑùÒ»¸ö¼¼ÇÉ£ºÈç¹ûij¸öµ¥Ôª¸ñµÄ±¸Ñ¡Êý×ÖÒѾ³öÏÖÔÚÏàͬÐС¢Áлò¿òÖеÄÁíÒ»¸öµ¥Ôª¸ñÖУ¬Ôò½«Æäɾ³ý¡£Èç¹ûÄúä¯ÀÀÏÂÔØµÄÔ´´úÂëµÄ¡°¼¼ÇÉ¡±Îļþ¼Ð£¬Äú»á·¢ÏÖÎÒÒѾʵÏֵö¼¼ÇÉ£¬ÀýÈç naked subset¡¢hidden subset ºÍ x-wing¡£ÕâЩ¼¼ÇÉ¿ÉÓÃÀ´ÉèÖÃÍø¸ñÄÚµ¥Ôª¸ñÖеÄÊý×Ö£¬²¢ÇÒ¿ÉÒÔ´Ó¸÷µ¥Ôª¸ñÖÐɾ³ý¿ÉÄܵı¸Ñ¡Êý×Ö¡£ÒÀ¾Ý Generator µÄÅäÖÃÄѶȼ¶±ð£¬¿ÉÒÔÔÊÐí Solver ½öʹÓÃÌØ¶¨µÄ¼¼ÇÉ£¬ÒòΪijЩ¼¼ÇÉÐèÒª¸ü¸ßµÄ¼¼ÄÜ¡£ Generator Éú³ÉÓÎÏ·µÄÊýÁ¿Ò²»áÓ°ÏìÄѶȼ¶±ð¡£¶ÔÓÚÌØ¶¨µÄÄѶȼ¶±ð£¬Generator »áÉú³É¶à¸ö¶ø²»Êǵ¥¸öÌîÊýÓÎÏ·£¬È»ºó´ÓÖÐÑ¡ÔñÒ»¸ö×÷Ϊ×îÄѵġ£Generator ÒÀ¾Ý Solver ·µ»ØµÄͳ¼ÆÊý¾Ý£¬ÈçʹÓÃÄĸö¼¼ÇÉÍê³ÉÓÎÏ·£¬ÒÔ¼°Ã¿ÖÖ¼¼ÇÉʹÓöàÉٴεÈÀ´×÷³ö¾ö¶¨¡£ ¾ÍÏñÄúÔÚÔ´ÎļþÖп´µ½µÄ£¬ÔÚĿǰµÄʵÏÖ³ÌÐòÖУ¬¹²ÓÐÈý¸öÄѶȼ¶±ð¡£ • µÍ¼¶£¬Éú³ÉÈý¸öÌîÊýÓÎÏ·£¬ÁôÓÐÖÁÉÙ 32 ¸öµ¥Ôª¸ñÐèÒªÌî³ä£¬Ö»ÐèÒªÒ»ÖÖ¼¼Çɼ´¿ÉÍê³ÉÕû¸öÌîÊýÓÎÏ·¡£ • Öм¶£¬Éú³É 10 ¸öÌîÊýÓÎÏ·£¬Ã»ÓбØÐëÌî³äµ¥Ôª¸ñµÄ×îСÊýÁ¿ÏÞÖÆ£¬¿ÉÄÜÐèÒª¼¸ÖÖ¼¼ÇÉ¡£ • ¸ß¼¶£¬Éú³É 20 ¸öÌîÊýÓÎÏ·£¬Ã»ÓбØÐëÌî³äµ¥Ôª¸ñµÄ×îСÊýÁ¿ÏÞÖÆ£¬¿ÉÄÜÐèÒªËùÓÐʵÏÖ¼¼ÇÉ¡£ÊÂʵÉÏ£¬ÄѶȼ¶±ð»áΪÇó½âÆ÷´ò¿ª AllowBruteForce Ñ¡Ïî¡£ÕâÒâζ×Å£¬¶ÔÓÚÉú³ÉµÄÌîÊýÓÎÏ·£¬Ö»Ê¹ÓÃÌṩµÄÒ»ÖÖ¼¼Çɸù±¾ÎÞ·¨Íê³É£¬Ò²Ðí»¹»áÓõ½¡°ÊÔÑé - ´íÎó¡±µÄÂ߼ѻ·¡£Èç¹ûÄú²»Ï²»¶ÕâÖÖ·½Ê½£¬ÄúÍêÈ«¿ÉÒÔÏñһЩ³ÕÃ﵀ Sudoku Íæ¼ÒÄÇÑù£¬×Ô¼ºÐ޸ĴúÂë¡£ Windows ´°ÌåºÍÓ²¼þ½»»¥
ÒÔÏÂÕ½ÚÌÖÂÛÁË Sudoku ÓÎÏ·ÓÐ¹Ø Tablet PC ¹¦ÄܺÍÓû§ÌåÑéµÄÄÚÈÝ¡£ Sudoku ²¼¾Ö ¶ÔÓÚÔÚ Tablet PC ÉÏÔËÐеÄËùÓÐÓ¦ÓóÌÐò£¬Ò»¸öÖØÒª·½ÃæÊǸÃÓ¦ÓóÌÐòµÄ×îÖÕÏÔʾÇé¿ö¡£ÔÚ²»Í¬³ß´çµÄ Tablet PC ÆÁÄ»ÉÏ£¬Ó¦ÓóÌÐòµÄÏÔʾЧ¹ûÈçºÎ£¿ÔÚ²»Í¬µÄÆÁÄ»·½ÏòÄÜ·ñ»ñµÃºÜºÃµÄͼÏñ£¿ÄÜ·ñºÜºÃµÄÊÊÓ¦¸ß DPI ·Ö±æÂʺʹó×ÖÌ壿ËùÓÐÕâЩÒòËØ¶ÔÓ¦ÓóÌÐòµÄ³É¹¦ÖÁ¹ØÖØÒª¡£µ±È»£¬ÎÒÃÇÐèÒªÏÈÌÖÂÛÓ¦ÓóÌÐò½á¹¹£¬·ñÔò»áÄÑÒÔÕ¹¿ª¶ÔÕâЩ·½ÃæµÄÌÖÂÛ¡£ ÎÒ½« Sudoku Éè¼ÆÎª Windows Forms Ó¦ÓóÌÐò£¬³ý¹©Óû§ÅäÖÃÓÎÏ·µÄ OptionsDialog Í⣬Õû¸öÓ¦ÓóÌÐòÏÔʾÔÚÒ»¸öÃûΪ MainForm µÄ´°ÌåÖУ¬Î»ÓÚ MainForm.cs ÎļþÖС£MainForm µÄ Controls ¼¯ºÏÖнöÓеÄÒ»¸ö¿Ø¼þÊÇ ImagePanel£¬¸Ã¿Ø¼þ¹Ì¶¨Ìî³ä´°Ì壬ÓÃ×÷Õû¸öÓ¦ÓóÌÐòµÄ±³¾°ÒÔ¼°´°Ìåϼ¶¿Ø¼þµÄÈÝÆ÷¡£ImagePanel ÊÇÒ»¸ö¼òµ¥µÄ¿Ø¼þ£¬´´½¨ËüµÄΨһĿµÄÊÇ»æÖÆÒ»¸öÌî³ä¿Ø¼þµÄͼÏñ¡£ internal class ImagePanel :NoFlickerPanel { public ImagePanel(){} [DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)] [Browsable(false)] public Image Image { get { return _img; } set { _img = value; } } private Image _img; protected override void OnPaint(PaintEventArgs e) { if (_img != null) { e.Graphics.DrawImage(_img, 0, 0, Width, Height); } base.OnPaint(e); } } ImagePanel ÅÉÉú×Ô NoFlickerPanel ¿Ø¼þ£¬¶ø·ÇÖ±½ÓÀ´×Ô System.Windows.Forms.Panel¡£ internal class NoFlickerPanel :Panel { public NoFlickerPanel() { SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); } } NoFlickerPanel ÓÃ×÷רÃÅµÄ Panel ÅÉÉúÀàµÄ»ùÀà¡£³ýÔÚµ÷Õû¿Ø¼þ´óСʱ×Ô¶¯ÖØ»æÍ⣬ÆäÕæÕýÓÃ;ÊÇÆôÓÃË«»º³å´¦Àí¡£Ë«»º³å´¦Àí¿ÉÔÚÖØ»æÊ±·ÀÖ¹ Panel ÉÁ˸£¨Òò´ËµÃÃû£©£¬¶ÔÓÚÓ¦ÓóÌÐòµÄÍâ¹ÛÖÁ¹ØÖØÒª¡£Èç¹ûÔÚµ÷Õû´°Ìå´óСʱÆôÓÃ×Ô¶¯Öػ湦ÄÜ£¬Ôòµ±Óû§¸ü¸Ä´°Ìå´óС»ò Tablet PC µÄÆÁÄ»·½Ïòʱ£¬¿ÉÒÔÇáËÉÖ§³ÖÖØÐ»æÖÆ´°ÌåµÄ²Ù×÷¡£ ±³¾°Ãæ°åÏÔʾÆäËûËĸöÃæ°å£º • Ò»¸ö NoFlickerPanel£¬ÓÃ×÷Íø¸ñ¿Ø¼þ£¨¸Ã¿Ø¼þʵ¼ÊÉϳÊÏÖÁËÓÎÏ·²¢ÔÊÐíÓû§½»»¥£©µÄÈÝÆ÷¡£ • Á½¸ö ImagePanels£¬ÆäÖоßÓС°ÆÁÄ»¡±µÄ¿Ø¼þ£¬ÓÃÀ´¿ªÊ¼ÐÂÓÎÏ·»ò¼ÌÐøÉϴεÄÓÎÏ·¡£ • Ò»¸ö ScalingPanel£¬ÅÉÉú×Ô NoFlickerPanel£¬´¦ÀíÓÃÓÚºÍÓÎÏ·½»»¥µÄ¿Ø¼þ£¨ÀýÈ绱ʰ´Å¥¡¢Êý×Ö°´Å¥ºÍÍ˳ö°´Å¥£©µÄÕû¸ö²¼¾Ö¡£ ÎÒÏëÔÚ Sudoku ÖÐʵÏÖÕæÕýÓäÔõÄʹÓÃÌåÑ飬ÄÜÈÃÍæ¼ÒÇé²»×Ô½ûµØÔÞ̾£º¡°ÍÛ£¬Õæ°ô£¡¡±ÎªÊµÏÖÕâ¸öÄ¿±ê£¬ÐèҪȷ±£ÔÚµ÷ÕûÖ÷´°Ìå´óСʱ£¬´°ÌåÉϵĿؼþÒ²ÄÜÏàÓ¦ÒÆ¶¯²¢Äܵ÷Õû´óС£¬Í¬Ê±ÈÔ±£³ÖÏà¶Ô´óСºÍλÖá£ScalingPanel ¿ÉÒÔʵÏÖÕâÒ»µã¡£ internal class ScalingPanel :NoFlickerPanel { public ScalingPanel(){} private Rectangle _initialBounds; private Hashtable _controlBounds; private bool _initialized; public void ConfigureByContainedControls() { _initialBounds = Bounds; _controlBounds = new Hashtable(); foreach(Control c in this.Controls) { _controlBounds.Add(c, c.Bounds); } _initialized = true; } protected override void OnLayout(LayoutEventArgs levent) { if (_initialized && Width > 0 && Height > 0 && levent.AffectedControl == this) { //±£³ÖͼÏñµÄÔʼ³¤¿í±È int newWidth = Width; int tmp = (int)(_initialBounds.Width / (double)_initialBounds.Height * Height); if (tmp < newWidth) newWidth = tmp; int newHeight = Height; tmp = (int)(_initialBounds.Height / (double)_initialBounds.Width * newWidth); if (tmp < newHeight) newHeight = tmp; //¼Ç¼×î´óºÍ×î¶Ì±ß½ç int minX=int.MaxValue, minY=int.MaxValue, maxX=-1, maxY=-1; //ÒÆ¶¯²¢µ÷ÕûËùÓпؼþµÄ´óС foreach(Control c in this.Controls) { Rectangle rect = (Rectangle)_controlBounds[c]; //È·¶¨¶ÔÔʼ³ß´çµÄ×î¼Ñ²ÂÏë int x = (int)(rect.X / (double) _initialBounds.Width * newWidth); int y = (int)(rect.Y / (double) _initialBounds.Height * newHeight); int width = (int)(rect.Width / (double) _initialBounds.Width * newWidth); int height = (int)(rect.Height / (double) _initialBounds.Height * newHeight); //É趨б߽ç Rectangle newBounds = new Rectangle( x, y, width, height); if (newBounds != c.Bounds) c.Bounds = newBounds; //¼Ç¼×î´óºÍ×î¶Ì±ß½ç if (c.Left < minX) minX = c.Left; if (c.Top < minY) minY = c.Top; if (c.Right > maxX) maxX = c.Right; if (c.Bottom > maxY) maxY = c.Bottom; } //½«ËùÓпؼþ¾ÓÖÐ int moveX = (Width - (maxX - minX + 1)) / 2; int moveY = (Height - (maxY - minY + 1)) / 2; if (moveX > 0 || moveY > 0) { foreach(Control c in this.Controls) { c.Location = c.Location + new Size(moveX - minX, moveY - minY); } } } //½øÐлù±¾²¼¾Ö base.OnLayout(levent); } } ScalingPanel ¹¤×÷µÄǰÌáÊǿؼþ£¨ÔÚ Visual Studio µÄ´°ÌåÉè¼ÆÆ÷ÖÐÒÔ¿ÉÊÓ»¯µÄ·½Ê½´´½¨£©µÄÔʼÏà¶Ô´óСºÍλÖÃÊÇÐèÒªÔÚÕû¸öÓ¦ÓóÌÐòÉúÃüÖÜÆÚÄÚά³ÖµÄÏà¶Ô´óСºÍλÖá£ÔÚ½«ËùÓÐ×ӿؼþÌí¼Óµ½ ScalingPanel ʵÀýºó£¬¿Í»§¶Ë½«µ÷Óà ScalingPanel ¿Ø¼þµÄ ConfigureByContainedControls ·½·¨¡£¸Ã·½·¨»á³õʼ»¯½«ÔÚËæºó´°ÌåÉè¼ÆÖÐʹÓõö״̬¡£Ê×ÏÈ´æ´¢µ±Ç°µÄ¿Ø¼þ±ß½ç¡£È»ºó±éÀúËùÓÐ×ӿؼþ£¬½«¸÷×ӿؼþµÄµ±Ç°±ß½ç´æÈëÓÉ Control Ë÷ÒýµÄ System.Collections.Hashtable¡£ÕÆÎÕÁË ScalingPanel ´óСÒÔ¼°¸÷¸ö¿Ø¼þµÄÆðʼ´óСºÍλÖÃÖ®ºó£¬ScalingPanel ¾ÍÁ˽âÁ˸÷¸ö¿Ø¼þ´óСÓëËùÔÚÃæ°å´óСµÄ±ÈÀý£¬ÒÔ¼°¸÷¸ö¿Ø¼þÖ®¼äµÄÏà¶Ô¼ä¸ô¡£¸ü¸ÄÃæ°å´óСʱ£¬ScalingPanel ʹÓøÃÐÅÏ¢ÒÆ¶¯Î»Öò¢¸ü¸Äÿ¸öËù°üº¬¿Ø¼þµÄ´óС£¬ÒÔά³ÖÕâЩ´óСºÍλÖñÈÀý¡£ ËùÓÐÆæÃîµÄÊÂÇé¶¼·¢ÉúÔÚ OnLayout ¸²¸ÇÖС£Ê×ÏÈ£¬ScalingPanel ½«¼ì²éÏÈǰÊÇ·ñµ÷Óùý ConfigureByContainedControls£¬ÒÔ±ÜÃâÔÚδµ÷ÓõÄÇé¿öϽøÐÐ×Ô¶¨Òå²¼¾Ö²Ù×÷¡£»¹½«¼ì²éÒÔÈ·±£Ãæ°åµÄ³ß´ç´óÓÚ 0£¬ÒÔ±ÜÃâÔÚ´°Ìå×îС»¯ºÍÖ´ÐÐÀàËÆ»áµ¼Ö¿ؼþ´óСÏÔÖøËõ¼õµÄ²Ù×÷ʱ½øÐв¼¾Ö¡£×îºó£¬ScalingPanel ½«È·±£²¼¾Ö²Ù×÷µÄÄ¿±ê¿Ø¼þÊÇÃæ°å±¾Éí£¬ÒÔ±ÜÃâµ±¶à¸öÓëÃæ°åÏà¹ØµÄ¿Ø¼þÐèÒª½øÐв¼¾ÖʱִÐв»±ØÒªµÄÖØ¸´²¼¾Ö²Ù×÷¡£ ²¼¾Ö²Ù×÷°üÀ¨Èý¸öÖ÷Òª²½Öè¡£ • È·±£ËùÓпؼþ±ß¿òµÄͼÏñ³¤¿í±È±£³Ö²»±ä£¬ÒÔ±ãʹÓø÷½·¨£¬¸ù¾ÝÃæ°åµÄµ±Ç°³¤ºÍ¿í£¬Ïà¶ÔÓÚÔʼ³¤ºÍ¿í¼ÆËã¸Ã±ß¿òеij¤ºÍ¿í¡£ • ±éÀúËùÓпؼþ£¬¸ù¾ÝÔʼ±ß¿òÓëб߿òµÄ±ÈÀý£¬È·¶¨¸÷¸ö¿Ø¼þµÄλÖúʹóС¡£ • ʹ¿Ø¼þ¿ò¾ÓÓÚÃæ°åÖÐÐÄ¡£´Ó¶øÈ·±£ÁËÔÚÃæ°åµÄ³¤¿í±ÈÓëÔʼ³¤¿í±ÈÏÔÖø²»Í¬Ê±£¬¿Ø¼þÈÔÈ»¾ÓÓÚÃæ°åÖÐÐÄ¡£ ScalingPanel µÄ×÷ÓÃÔ¶²»Ö¹ÔÚÓû§µ÷Õû´°Ìåʱά³ÖƯÁÁµÄÍâ¹Û¡£ËùÓмÆËã¶¼ÊÇ»ùÓÚÏà¶Ô´óСºÍλÖýøÐеģ¬ÕâÑù¾Í»ñµÃÁËÃâ·ÑµÄ¸ß DPI ·Ö±æÂÊÖ§³Ö£¨Í¨³£Ó¦ÓÃÓÚ Tablet PC ÉÏ£©£¡¶ÔÓÚÐí¶àÓ¦ÓóÌÐò¶øÑÔ£¬È±ÉÙ¶Ô¸ß DPI ·Ö±æÂʵÄÖ§³ÖÊÇÒ»¸ö³£¼ûȱÏÝ£¬ÒòΪÐí¶à¿ª·¢ºÍ²âÊÔÈËԱͨ³£²»»á¼Æ»®ÔÚ Tablet PC ÉÏÔËÐгÌÐò£¬Ò²Ã»ÓÐÒâʶµ½ Windows ÖÐµÄ DPI ÉèÖÿÉÒÔ¸ü¸Ä¡£¼ÓÈëÖîÈç ScalingPanel Ò»ÀàµÄ¿Ø¼þ¿ÉÒÔÓÅ»¯ÄúµÄ¿ª·¢ºÍ²âÊÔÉúÃüÖÜÆÚ¡£ µ±È»£¬Ö»Óе±µ÷ÕûÃæ°å±¾Éí£¬²¢ÇÒÒÔÏà¶ÔÓÚ´°Ì嵱ǰ´óСµÄ·½Ê½½øÐе÷Õûʱ£¬ScalingPanel ÖеÄËùÓÐÕâЩ¹¦ÄܲŻáÆð×÷Óá£ÈçǰËùÊö£¬Sudoku ÖеÄÖ÷´°Ìå¾ßÓÐËĸö×ÓÃæ°å¿Ø¼þ£¬µ«Ö»ÓÐÁ½¸ö¿Ø¼þÍ£¿¿ÔÚÃæ°åÖС£¿Ø¼þÃæ°åÍ£¿¿ÔÚ´°ÌåÓҲ࣬ͣ¿¿µÄÁíÒ»¸ö¾ßÓÐÍø¸ñ¿Ø¼þµÄÃæ°åÔòÌî³ä´°ÌåµÄÆäÓಿ·Ö¡£´Ó¶øÔÚµ÷ÕûÖ÷´°Ìå´óСʱ£¬¿ÉÒÔÇáËÉά³ÖÁ½¸öÃæ°åµÄÏà¶Ô´óС¡£ÎÒÈ·±£¿Ø¼þÃæ°åʼÖÕÕ¼¾Ý´°Ìå¿í¶ÈµÄÈý·ÖÖ®Ò»£¬ÔÊÐíÍø¸ñÃæ°åÕ¼¾Ý´°ÌåµÄÊ£Óಿ·Ö¡£ protected override void OnLayout(LayoutEventArgs levent) { pnlControls.Width = Width / 3; base.OnLayout(levent); } ÒªÁ˽âÉÏÊö³ÌÐòÈçºÎÓ°ÏìÓÎÏ·Ãæ°å£¬Çë²é¿´Í¼ 5 ÖÐµÄÆÁÄ»¿ìÕÕ¡£Ëõ¼õÖ÷´°ÌåµÄ¿í¶È½«µ¼ÖÂͼ 6 ÖеIJ¼¾Ö£¬¶øÀ©Õ¹¿í¶È½«µ¼ÖÂͼ 7 ÖеIJ¼¾Ö¡£  ͼ 5. ĬÈϲ¼¾Ö
 ͼ 6. Ëõ¼õ¿í¶ÈºóµÄ²¼¾Ö

ͼ 7. Ôö¼Ó¿í¶ÈºóµÄ²¼¾Ö ¸Ã·½·¨»¹¿ÉÒÔÇáËÉÊÊÓ¦Óû§µÄ×óÓÒÊÖʹÓÃϰ¹ß¡£ÔÚÆô¶¯Ó¦ÓóÌÐòÒÔ¼°Óû§ËæÊ±¸ü¸Ä Windows ×óÓÒÊÖʹÓÃϰ¹ßÉèÖÃʱ£¬¶¼»áµ÷Óà SetHandedness ·½·¨¡£ private void SetHandedness() { DockStyle targetStyle = PlatformDetection.UserIsRightHanded ? DockStyle.Right :DockStyle.Left; if (targetStyle != pnlControls.Dock) { pnlControls.SendToBack(); pnlControls.Dock = targetStyle; } } ÕâÊÇÒ»Öַdz£¼òµ¥ÇÒ¼«ÎªÓÐЧµÄ·½·¨¡£½öʹÓü¸ÐдúÂ룬¾Í¿ÉÒÔ¼ì²éÓû§µ±Ç°µÄ×óÓÒÊÖʹÓÃϰ¹ßÉèÖ㬲¢ÔÚµ±¿Ø¼þÃæ°åÍ£¿¿µ½Ö÷´°ÌåµÄÓÒ²à»ò×ó²àʱ½øÐÐÏàÓ¦µÄ¸ü¸Ä¡£ÕâÑù¾Í¿ÉÒÔÁË£¡ÉÏÊö²¼¾ÖÖ§³Ö½«¸ºÔðÆäÓಿ·Ö£¬ÇÒÓÎÏ·»á¶ÔÓû§µÄϲºÃ¸ü¸Ä×ö³ö¼´Ê±ÏìÓ¦£¨ÎÒ»áÔÚµçÔ´ÎÊÌⲿ·Öµ÷Óø÷½·¨Ê±½øÐÐÌÖÂÛ£©¡£ ͼ 8 ºÍͼ 9 ½«ËµÃ÷ÓÉÓÚ×óÓÒÊÖʹÓÃϰ¹ßÉèÖøü¸Ä¶øµ¼ÖµĴ°Ìå¸ü¸Ä¡£  ͼ 8. ÓÒÊÖʹÓÃϰ¹ß²¼¾Ö
ͼ 9. ×óÊÖʹÓÃϰ¹ß²¼¾Ö ÎÒÒÑ˵Ã÷ÈçºÎµ÷Õû¿Ø¼þÃæ°åÖеĿؼþ´óС£¬ÒÔ¼°ÈçºÎÏà¶Ô±Ë´Ë´óСÀ´µ÷Õû´°ÌåÉϵÄÁ½¸öÖ÷Ãæ°å¡£ÆäÓàµÄ×Ô¶¯²¼¾ÖÖ§³ÖÓÉ MainForm ÖÐµÄ OnResize ¸²¸Ç´¦Àí¡£ protected override void OnResize(EventArgs e) { //½øÐлù±¾µ÷Õû base.OnResize(e); //½«ÐÂÓÎÏ·ºÍ±£´æµÄÓÎÏ·Ãæ°å¾ÓÖзÅÖà pnlNewPuzzle.Location = new Point( backgroundPanel.Location.X + (backgroundPanel.Width - pnlNewPuzzle.Width) / 2, backgroundPanel.Location.Y + (backgroundPanel.Height - pnlNewPuzzle.Height) / 2); pnlSavedOrNewPuzzle.Location = new Point( backgroundPanel.Location.X + (backgroundPanel.Width - pnlSavedOrNewPuzzle.Width) / 2, backgroundPanel.Location.Y + (backgroundPanel.Height - pnlSavedOrNewPuzzle.Height) / 2); //È·±£ÔÚÎÒµ÷Õû´°Ìå´óСʱ£¬ÓÎÏ·Íø¸ñλÓÚÖÐÐÄÇÒΪÕý·½ÐΣ¬ //²¢Çҳߴ羡¿ÉÄÜ·Å´ó£¬Ö»Òª //²»³¬³öÆä¸¸±ß½ç int width = pnlGrid.Width; int height = pnlGrid.Height; int margin = 25; Size gridSize = width > height ? new Size(height - margin, height - margin) : new Size(width - margin, width - margin); thePuzzleGrid.Bounds = new Rectangle( new Point((width - gridSize.Width)/2, (height - gridSize.Height)/2), gridSize); //È·±£ÎÞÂÛ´°ÌåÈçºÎµ÷Õû£¬ //×ÖÄ»½ø¶ÈÌõ¶¼ÔÚ´°ÌåÖÐÐĽáÊø¡£ marqueeBar.Location = new Point( backgroundPanel.Location.X + (backgroundPanel.Width - marqueeBar.Width) / 2, backgroundPanel.Location.Y + (backgroundPanel.Height - marqueeBar.Height) / 2); } ¸Ã·½·¨ÓÃÓÚÖ´ÐÐһЩ¼òµ¥µÄ²¼¾Ö²Ù×÷¡£Ê×ÏÈ£¬Ëüʹ pnlNewPuzzle ºÍ pnlSavedOrNewPuzzle Ãæ°åÔÚ´°ÌåÖоÓÖУ¨¶àÊýʱ¼äÕâÐ©Ãæ°åÊÇÒþ²ØµÄ£¬Ö»ÓÐÔÚÆô¶¯»òÓû§Ñ¡Ôñ¿ªÊ¼ÐÂÓÎϷʱ²ÅÏÔʾ£©¡£»¹¿ÉÔÚÍø¸ñÃæ°åÖе÷ÕûÓÎÏ·Íø¸ñ¿Ø¼þµÄ´óСºÍλÖ㨿ÉÖ¤Ã÷£¬Í¨¹ý½«Íø¸ñÃæ°åÉèΪ ScalingPanel ¿ÉÒÔÇáËÉÍê³É¸Ã²Ù×÷£©¡£×îºó£¬»¹¿ÉÈ·±£ÓÃÓÚÏÔʾÓÎÏ·´´½¨½ø¶ÈµÄ½ø¶ÈÌõλÓÚ´°ÌåÕýÖС£ Éè¼Æ½ø¶ÈÌõÊÇÒ»¸öÓÐȤµÄÌôÕ½¡£Windows Forms 1.1 ¿ÉÒÔÏÔʾ½ø¶ÈÌõ£¬µ«ÊÇΪ¡°¿ªÏä¼´Óá±£¬²¢ÇÒ²»Ö§³Ö×ÖÄ»Ñùʽ¡£µ« Microsoft Windows XP Ö§³Ö¡££¨×ÖÄ»ÑùʽÓÃÓÚÏÔʾûÓÐÔ¤¶¨Ò忪ʼºÍ½áÊøµãµÄÈÎÎñ¡£Windows Æô¶¯Ê±ÏÔʾµÄ½ø¶ÈÌõΪ×ÖÄ»½ø¶ÈÌõ¡££©Windows Forms ProgressBar ¿Ø¼þÊÇ Windows XP ÖÐͨÓÿؼþ½ø¶ÈÌõµÄ°ü×°£¬µ«ÊÇûÓÐÌṩ×ÖÄ»ÉèÖã¨Windows Forms 2.0 Ìṩ£©¡£Òò´Ë£¬ÎÒ´´½¨ÁËÓÃÓÚʹÓÃ×ÖÄ»ÑùʽµÄ×Ô¼ºµÄ°ü×°£¬¶øÃ»ÓÐʹÓà Windows Forms ProgressBar£¬Èçͼ 10 Ëùʾ£¨µ±È»£¬µ±ÄúÉý¼¶ÖÁ Windows Vista ºó£¬½ø¶ÈÌõ»á±äµÃ¸üƯÁÁ£¬Èçͼ 11 Ëùʾ£©¡£ internal sealed class MarqueeProgressBar :Control { public MarqueeProgressBar() { SetStyle(ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.Selectable | ControlStyles.UserPaint, false); BackColor = Color.Transparent; ForeColor = SystemColors.Highlight; } protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.ClassName = "msctls_progress32"; if (!DesignMode) cp.Style |= PBS_MARQUEE; if (RightToLeft == RightToLeft.Yes) { cp.ExStyle |= WS_EX_LAYOUTRTL; cp.ExStyle &= ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR); } return cp; } } protected override void OnForeColorChanged(EventArgs e) { base.OnForeColorChanged(e); if (base.IsHandleCreated) { SendMessage(PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(this.ForeColor)); } } protected override void OnBackColorChanged(EventArgs e) { base.OnBackColorChanged(e); if (base.IsHandleCreated) { SendMessage(PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(this.BackColor)); } } protected override ImeMode DefaultImeMode { get { return ImeMode.Disable; } } protected override Size DefaultSize { get { return new Size(100, 23); } } protected override void CreateHandle() { if (!RecreatingHandle) { NativeMethods.INITCOMMONCONTROLSEX icc = new NativeMethods.INITCOMMONCONTROLSEX(); icc.dwSize = 0; icc.dwICC = ICC_PROGRESS_CLASS; NativeMethods.InitCommonControlsEx(icc); } base.CreateHandle(); } private IntPtr SendMessage(int msg, int wparam, int lparam) { return NativeMethods.SendMessage(new HandleRef(this, this.Handle), msg, new IntPtr(wparam), new IntPtr(lparam)); } protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); SendMessage(PBM_SETRANGE, MINIMUM, MAXIMUM); SendMessage(PBM_SETSTEP, STEP, 0); SendMessage(PBM_SETPOS, VALUE, 0); SendMessage(PBM_SETBKCOLOR, 0, ColorTranslator.ToWin32(BackColor)); SendMessage(PBM_SETBARCOLOR, 0, ColorTranslator.ToWin32(ForeColor)); Start(DEFAULTSPEED); } private void Start(int speed) { if (!DesignMode && IsHandleCreated) SendMessage(PBM_SETMARQUEE, speed == 0 ?0 : 1, speed); } ... //³£Á¿¶¨Òå }
 ͼ 10. Windows XP ÖеÄ×ÖÄ»½ø¶ÈÌõ  ͼ 11. Windows Vista ÖеÄ×ÖÄ»½ø¶ÈÌõ
»æÖÆ Sudoku PuzzleGrid ¿Ø¼þλÓÚ Controls Îļþ¼ÐµÄ PuzzleGrid.cs ÎļþÖУ¬ÊÇÓ¦ÓóÌÐòÖÐ×îÎªÖØÒªµÄ¿Ø¼þ¡£¸Ã¿Ø¼þ¿ÉÏÔʾ Sudoku ÓÎÏ·£¬Ìá¹©Íæ Sudoku ÓÎÏ·µÄºËÐÄÊó±êºÍ¼üÅÌÖ§³Ö£¬ÒÔ¼°ÆôÓÃÓëÓÎÏ·µÄ±Êʽ½»»¥¡£ ×÷ΪһÖÖÍÆ¼öµÄ×ö·¨£¬¿Ø¼þµÄËùÓлæÖƶ¼Ó¦ÏìÓ¦ WM_PAINT ÏûÏ¢¡£ÔÚ Windows Forms ÖУ¬Õâͨ³£Òâζ×ÅΪ¿Ø¼þµÄ Paint ʼþÌí¼Óʼþ´¦ÀíÆ÷»ò¸²¸Ç OnPaint ·½·¨¡£ÎÒÑ¡ÔñºóÕß¡£ protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; DrawToGraphics(e.Graphics, e.ClipRectangle); } »æÖÆÂß¼¿É·Ö½âΪһ¸öµ¥¶ÀµÄ DrawToGraphics ·½·¨ºÍÒ»¸ö¾ØÐΣ¬Ç°Õ߿ɽÓÊÜÄ¿±ê Graphics ¶ÔÏ󣬺óÕßÔò¿ÉÒÔ¶¨Òå¿Ø¼þÖÐÎÞЧµÄ±ß½ç¡££¨Ö»ÖØÐ»æÖƾø¶ÔÐèÒª»æÖƵÄͼÐοɼ«´óÌá¸ßÓ¦ÓóÌÐòµÄÐÔÄÜ£»ÔÚÎÒ×î³õÔö¼ÓʵÏָù¦ÄÜʱ£¬ÐÔÄܵÄÌá¸ß¼´¿ÌÏÔÏÖ£©¡£µ±ÄúÒÔÕâÖÖ·½Ê½·Ö½â»æÖÆ´úÂëʱ£¬´òÓ¡Ö§³Ö»áµÃµ½ÏÔÖøµÄ¼ò»¯£¬Í¬Ê±½«¿Ø¼þ³ÊÏÖ¸ø Bitmap ¶ÔÏóµÄÄÜÁ¦Ò²ÓÐËùÌá¸ß¡£ÕâÒ»µãÔÚʤÀû¶¯»ÖÐÓÐËùÕ¹ÏÖ£¬ÆäÖÐÎÒʹÓøù¦ÄÜÏÔÊ¾Íæ¼ÒÌîÊý³É¹¦Ê±µÄ×£ºØ¶¯»¡£ ´úÂë²»ÊǺܳ¤£¬ÒÔ¼ò½àÃ÷Á˵ķ½Ê½»æÖÆÓÎÏ·ÅÌ¡£´Ó»æÖƱ³¾°Í¼Ïñ¿ªÊ¼£¬ÕâЩͼÏñλÓÚ´úÂëÏÂÔØµÄ Images Îļþ¼ÐÖС£ graphics.DrawImage(ResourceHelper.BoardBackgroundImage, 0, 0, Width, Height); graphics.DrawImage(ResourceHelper.BoardImage, rect); »æÖƵÚÒ»¸öͼÏñÀ´Ìî³ä¿Ø¼þ¡£µÚ¶þ¸öͼÏñ¸ù¾Ý˽ÓÐ BoardRectangle ÊôÐÔ·µ»ØµÄ¾ØÐλæÖÆ£¬´Ë¾ØÐαȿؼþÂÔС£¬ÔÊÐíÔڱ߽çÖÜΧ´æÔÚһЩ½ÏյĿհױ߾ࡣ˽ÓÐµÄ GetCellRectangle ·½·¨³ý¿¼ÂDZ³¾°Í¼ÏñÏà¶ÔÓÚÕû¸öͼÏñµÄ¼ä϶´óСºÍλÖÃÍ⣬»¹¿¼ÂDZ³¾°Í¼ÏñÉϸ÷µ¥Ôª¸ñͼÏñÏà¶ÔÓÚÕû¸öͼÏñµÄÒÑÖª´óС£¬Òò´Ë¿ÉÓÃÓÚÈ·¶¨¸÷µ¥Ôª¸ñµÄ´óСºÍλÖá£ÎÒʹÓà GetCellRectangle ·µ»ØµÄ´óСÀ´È·¶¨Ä³¸öµ¥Ôª¸ñÊÇ·ñÐèÒª½øÒ»²½¼ì²éºÍ»æÖÆ¡£ for (int i = 0; i < State.GridSize; i++) { for (int j = 0; j < State.GridSize; j++) { RectangleF cellRect = GetCellRectangle(rect, new Point(i,j)); if (clipRectangle.IntersectsWith(Rectangle.Ceiling(cellRect))) { ... //ÔÚ´Ë»æÖƵ¥Ôª¸ñ } } } ÀàËÆµØ£¬ÎÒʹÓÃÓÉ OnPaint Ïò DrawToGraphics ÌṩµÄ clipRectangle ÐÅÏ¢£¬±ÜÃâ³ÊÏÖûÓÐʧЧµÄµ¥Ôª¸ñ¡£ÒòΪ»æÖÆÊDZȽϺÄÓà CPU ×ÊÔ´µÄ²Ù×÷£¬²¢ÇÒͨ³£Ö»ÓпؼþµÄ½ÏÐ¡ÇøÓò·¢ÉúʧЧ£¨ÀýÈçÏòµ¥Ôª¸ñÊäÈëÊý×Ö»òÑ¡¶¨µÄµ¥Ôª¸ñ·¢Éú¸ü¸Äʱ£©£¬Òò´Ë¿ÉÏÔÖøÌá¸ß¿Ø¼þµÄ¿ÉÓÃÐÔ¡£ µ±ÎÒ¾ö¶¨»æÖÆÒ»¸öµ¥Ôª¸ñʱ£¬Ó¦¸Ã½øÐÐÈçϲÙ×÷¡£Ê×ÏÈ£¬¼ÙÉèÌîÊýûÓгɹ¦£¬¼ì²éµ¥Ôª¸ñµ±Ç°ÊÇ·ñÑ¡¶¨¡£Èç¹ûµ¥Ôª¸ñ±»Ñ¡¶¨£¬Ê¹ÓÃÎå¸öÂÌɫͼÏñ£¨Î»ÓÚ Images Îļþ¼ÐÖУ©ÖеÄÒ»¸ö»æÖƵ¥Ôª¸ñ¡£²»¹ÜÑ¡¶¨µÄµ¥Ôª¸ñÊÇ·ñÊÇËĽÇÖеÄÒ»¸ö£¬ÕâÎå¸öͼÏñ¶¼ÓëÖ®¶ÔÓ¦¡£ Image selectedCellImage; if (i == 0 && j == 0) selectedCellImage = ResourceHelper.CellActiveUpperLeft; else if (i == 0 && j == State.GridSize-1) selectedCellImage = ResourceHelper.CellActiveUpperRight; else if (i == State.GridSize-1 && j == 0) selectedCellImage = ResourceHelper.CellActiveLowerLeft; else if (i == State.GridSize-1 && j == State.GridSize-1) selectedCellImage = ResourceHelper.CellActiveLowerRight; else selectedCellImage = ResourceHelper.CellActiveSquare; graphics.DrawImage(selectedCellImage, cellRect.X, cellRect.Y, cellRect.Width, cellRect.Height); Èç¹ûÓû§Ñ¡ÔñÌṩÓйØÏÂÒ»¸öÒªÌî³äµÄµ¥Ôª¸ñµÄÌáʾ£¬»òÕßµ±Ç°³ÊÏֵĵ¥Ôª¸ñ¾ÍÊǽ¨ÒéµÄµ¥Ôª¸ñ£¬Ôò»áʹÓÃÀàËÆµÄÂß¼ÔÚµ¥Ôª¸ñÉÏ»æÖÆ suggested cell ͼÏñ¡£Solver ºÍ Generator ʹÓÃijÖÖ¼¼ÊõÈ·¶¨ÓÎÏ·µÄÄѶȼ¶±ðºÍÓÎÏ·µÄ¿É½âÐÔ£¬ÏÖÔÚʹÓÃͬһ¼¼ÊõÀ´È·¶¨½¨ÒéµÄµ¥Ôª¸ñ¡£Ë½ÓÐ SuggestedCell ÊôÐÔ¸ù¾ÝÕâЩ¼¼Êõ¼ÆË㽨ÒéµÄµ¥Ôª¸ñ²¢»º´æ¼ÆËã½á¹û£¬Òò´ËÎÞÐèÔÚÿ´Î»æÖÆÍø¸ñÊ±ÖØÐ¼ÆË㣨¸ü¸ÄÓÎϷ״̬ʱ»º´æµÄ½á¹ûÎÞЧ£©¡£ÔÚÄÚ²¿£¬SuggestedCell ¿Éͨ¹ýÈçÏ´úÂëʵÏÖ¡£ TechniqueCollection tc = new TechniqueCollection(); FastBitArray [][] possibleNumbers = PuzzleState.InstantiatePossibleNumbersArray(State); foreach(EliminationTechnique et in EliminationTechnique.AvailableTechniques) { tc.Add(et); Hashtable ignored = null; State.ComputePossibleNumbers(tc, ref ignored, true, true, possibleNumbers); for(int row=0; row<State.GridSize; row++) { for(int column=0; column<State.GridSize; column++) { if (possibleNumbers[row][column].CountSet == 1) { return _suggestedCell = new Point(row, column); } } } } ¸ÃÂß¼ÒÔ´Ó×î¼òµ¥µ½×ÔÓµÄ˳Ðò¼ìË÷ËùÓпÉÓõļ¼Êõ£¨¸Ã˳Ðò¸ù¾ÝÎÒ¶Ô¼¼ÊõÄѶȼ¶±ðµÄÆÀ¹À´óÖÂÈ·¶¨£©¡£´Ó×î¼òµ¥µÄ¼¼Êõ¿ªÊ¼£¬È·¶¨Í¨¹ýʹÓøü¼Êõ£¬Íø¸ñÖÐÊÇ·ñ´æÔÚÖ»ÓµÓÐÒ»¸ö¿ÉÌî³äµÄ±¸Ñ¡Êý×ֵĵ¥Ôª¸ñ¡£Èç¹û´æÔÚ´ËÀà¿Õµ¥Ôª¸ñ£¬ÔòµÚÒ»¸ö²éÕÒµ½µÄµ¥Ôª¸ñ±»·µ»ØÎª½¨ÒéµÄµ¥Ôª¸ñ¡£Èç¹ûûÓÐÕÒµ½£¬ÔòÏÂÒ»¸ö¼¼Êõ½«±»Ìí¼Óµ½×éºÏÖУ¬ÖØÐ¼ÆËã¿ÉÄܵı¸Ñ¡Êý×Ö¡£Èç¹ûÕÒµ½ÁËÖ»ÓµÓÐÒ»¸ö¿ÉÄܱ¸Ñ¡Êý×ֵĶà¸öµ¥Ôª¸ñ£¬Ôò½«·µ»ØÆäÖеĵÚÒ»¸öµ¥Ôª¸ñ¡£´Ë¹ý³ÌÒ»Ö±¼ÌÐø½øÐУ¬Ö±µ½ÕÒµ½½¨ÒéµÄµ¥Ôª¸ñ»òËùÓм¼ÊõÓþ¡ÈÔûÓÐÕÒµ½½¨ÒéµÄµ¥Ôª¸ñΪֹ£¨Ö»ÓÐÔÚÓÎÏ·Éú³É¹ý³ÌÖУ¬ÔÊÐíÔÚÄѶȼ¶±ðʹÓÃÇ¿Á¦¼¼Êõ²Å»á·¢ÉúÕâÖÖÇé¿ö£©¡£ÕâÖÖÇé¿öÏ£¬´øÓÐ×îÉÙµÄÊ£Óà¿ÉÄܱ¸Ñ¡Êý×ֵĵ¥Ôª¸ñ½«±»·µ»ØÎª½¨ÒéµÄµ¥Ôª¸ñ¡£ µ¥Ôª¸ñ±³¾°»æÖÆÍê³Éºó£¬Èç¹ûÆäÖÐÒÑ´æÔÚÒ»¸öÊäÈëµÄÊý×Ö£¬Ôò¸ÃÊý×Ö½«±»³ÊÏÖ¡£ if (State[i, j].HasValue) { Brush b; if (ShowIncorrectNumbers && State[i, j].HasValue && _solvedOriginalState != null && State[i, j].Value != _solvedOriginalState[i, j].Value) { b = _incorrectValueBrush; } else if (_originalState != null && _originalState[i,j].HasValue) { b = _originalValueBrush; } else b = _userValueBrush; graphics.DrawString((State[i, j] + 1).ToString( CultureInfo.InvariantCulture), setNumberFont, b, cellRect, _centerNumberFormat); } ¿É´ÓÒÔÏÂÈý¸ö»º´æµÄ»±ÊÖÐÑ¡ÔñÒ»¸öÓë DrawString Ò»ÆðʹÓ㺠• ºìÉ«»±ÊÏÔʾ²»ÕýÈ·µÄÖµ¡£ • ºÚÉ«»±ÊÏÔʾ³õʼÓÎÏ·²¿·ÖÖµ¡£ • À¶É«»±ÊÏÔʾÊäÈëÖµ¡£ ÕâЩ»±Ê½«±»»º´æ£¬Òò´ËÎÞÐèÔÚÿ´Î»æÖÆÓÎϷʱÔٴδ´½¨£¨ÁíÒ»ÖÖÐÔÄÜÓÅ»¯£©¡£ÈçÏÂÇé¿öÖУ¬½«Ñ¡¶¨ÏÔʾ²»ÕýÈ·ÖµµÄ»±Ê£º • Óû§Ñ¡ÔñÔÚÓÎÏ·µ±Öо¯±¨´íÎó¡£ • µ¥Ôª¸ñÖеÄÖµÓëÍê³ÉµÄÌîÊýÓÎÏ·µÄÖµ²»Æ¥Å䣬¸ÃÖµÔÚÐÂÓÎÏ·³õʼ´æÈëʱÓÉÍø¸ñ´´½¨¡£ public void SetOriginalPuzzleCheckpoint(PuzzleState original) { _originalState = original; if (original != null) { SolverOptions options = new SolverOptions(); options.MaximumSolutionsToFind = 2; SolverResults results = Solver.Solve(original, options); if (results.Status == PuzzleStatus.Solved && results.Puzzles.Count == 1) { _solvedOriginalState = results.Puzzle; } else _solvedOriginalState = null; } } DrawToGraphics ×îºóÒª½øÐеIJÙ×÷ÊÇͨ¹ýÆô¶¯µ÷ÓÃÆä˽ÓÐ RenderInk ·½·¨£¬³ÊÏÖÈκÎÐèÒªÔÚÍø¸ñÖÐÏÔʾµÄÊÖдÄÚÈÝ¡£Õ⽫ÎÒ´øÈëÁË Sudoku ʵÏÖµÄ pi¨¨ce de r¨¦sistance£º´¥¿Ø±ÊÖ§³Ö¡£ ÆôÓà Tablet PC ½»»¥
Ϊ Tablet PC ±àдµÄÓÎÏ·Ó¦¼òµ¥Ò×Íæ£¬²¢ÇÒ±ØÐëÖ§³Ö±Êʽ½»»¥¡£×îµÍÏÞ¶ÈÉÏ£¬Ó¦ÓóÌÐòÓ¦½«´¥¿Ø±ÊÓÃ×÷µ¼º½É豸¡£ÀíÏëÇé¿öÏ£¬Ó¦ÓóÌÐòÓ¦Ö§³ÖÊÖдʶ±ð£¬²¢½«Æä×÷ΪÊý¾ÝÊäÈëµÄ·½·¨Ö®Ò» - ¾¡¹Ü¶Ô¸Ã¹¦ÄܵÄÖ§³Ö³Ì¶È²»¾¡Ïàͬ¡£Í¬Ñù£¬ÆôÓñÊʽ½»»¥ËùÉæ¼°µÄ¹¤×÷Á¿Ò²ÓÐËù²»Í¬¡£Ó¦ÓóÌÐòÒ²¿ÉÒÔÀûÓÃÊÖÊÆÖ§³Ö¹¦ÄÜ£¬½«Ìض¨µÄ´¥¿Ø±ÊÒÆ¶¯½âÊÍΪ²Ù×÷£¬¶ø²»ÊǽâÊÍΪҪʶ±ðµÄÊÖдÄÚÈÝ¡£¶ÔÓÚÔÚ´øÓд¥ÃþÆÁµÄ Tablet PC£¨ÀýÈç ultra-mobile PC£©ÉÏʹÓõÄÓ¦ÓóÌÐò¶øÑÔ£¬ÔÚÉè¼ÆºÍ¿ª·¢½×¶Î»¹Ó¦¿¼Âǵ㴥֧³Ö¡£ÎÒµÄ Sudoku ʵÏÖ×ñÑÁËËùÓÐÕâЩºËÐÄÔÔò¡£ ¶ÔÓÚ Tablet PC É쵀 Sudoku ÓÎÏ·£¬ºËÐÄÒªÇóÊÇÄú¿ÉÒÔ½«Êý×ÖдÈëµ¥Ôª¸ñ¡£Õâ»áʹÓÎÏ·¾ßÓÐÔÚÖ½ÉÏÊéдµÄ¸Ð¾õ¡£ÊÖдµÄÊý×ÖËæºó±»×ª»»Îª¿É³ÊÏÖµÄÀàÐÍ£¬¶ø·Ç±£ÁôΪÊéдÄÚÈÝÒÔÓÃÓÚʵÏÖ£»ÎÞÂÛÈçºÎ£¬Ó¦ÓóÌÐò¾ßÓÐʶ±ðÊäÈëµÄÄÜÁ¦·Ç³£ÖØÒª¡£ÎÒÑ¡Ôñʶ±ðÿ¸öµ¥¶ÀµÄÊäÈ룬ÒÔ´òÓ¡×ÖÌå³ÊÏÖËùÓÐÊÖдÊý×Ö£¬¶ø²»ÊÇÒÔÔʼµÄÊÖдÌå³ÊÏÖ¡£ ËùÓбÊʽ½»»¥¾ùÓÉ PuzzleGrid ¿Ø¼þ´¦Àí¡£PuzzleGrid ¾ßÓÐÁ½ÖÖÀàÐ͵Ä˽ÓгÉÔ±£¬À´×Ô Microsoft.Ink.dll ³ÌÐò¼¯¡£ private InkOverlay _inkOverlay; private RecognizerContext _recognizerCtx; InkOverlay ÀàÓÃ×÷ËùÓд¥¿Ø±Ê½»»¥´¦ÀíµÄ»ù´¡¡£InkOverlay ¸½¼ÓÔÚÈκÎÏÖÓÐµÄ Control ÉÏ£¬ÔÊÐíÓë¸Ã¿Ø¼þ½øÐбÊʽ½»»¥¡£RecognizerContext ÀàÌṩÁËÓÃÓÚʶ±ðÓû§ÊäÈëÊý×ÖµÄÊÖдʶ±ð»ù´¡¡£ PuzzleGrid ÖÐµÄ EnableTabletSupport ·½·¨½«³õʼ»¯ InkOverlay ºÍ RecognizerContext¡£Ê×ÏÈ£¬ÎÒͨ¹ýʹÓÃÏÈǰÌÖÂÛµÄÂß¼£¬¼ìË÷ҪʹÓõÄÀ´×Ô PlatformDetection ÀàµÄĬÈÏʶ±ð³ÌÐò¡£È»ºóʹÓ÷µ»ØµÄ Recognizer ÅäÖà RecognizerContext¡£ Recognizer defaultRecognizer = PlatformDetection.GetDefaultRecognizer(); _recognizerCtx = defaultRecognizer.CreateRecognizerContext(); _recognizerCtx.Factoid = Factoid.Digit; RecognizerContext ÌṩÁË Factoid ÊôÐÔ£¬Äú¿ÉÒÔͨ¹ýËü¾ÍÒªÇóʶ±ðµÄÊäÈëÀàÐÍÏò RecognizerContext ¸ø³öÌáʾ¡£Í¨¹ý factoid ÊôÐÔ¶Ôʶ±ð³ÌÐò½øÐÐÇ¡µ±µÄ·ÖÀàÖ®ºó£¬ÏÔÖøÌá¸ßÁËÓû§ÓëÓ¦ÓóÌÐò½»»¥µÄÖÊÁ¿£¬Ê¹Ê¶±ð³ÌÐòתÏòÖ¸¶¨ÊäÈëµÄ·ÖÀà¡£¶ÔÓÚ Sudoku£¬Ö»ÒªÇóʶ±ð³ÌÐòʶ±ðÊäÈëµÄÊý×Ö 1 µ½ 9¡£Í¨¹ý½« Factoid ÊôÐÔÉèΪ Factoid.Digit£¬Ê¹Ê¶±ð³ÌÐò²àÖØÓÚ¶Ô´ËÀàÊäÈëµÄʶ±ð¡£Factoid.Digit ½«Ê¶±ð³ÌÐòµÄ²àÖØµãÉèΪµ¥¸öÊý×Ö£¬ÒÔ±ã×¼±¸²¢Ç÷ÓÚֻʶ±ðµ¥¸öÊýÖµ£¨Çë×¢Ò⣬ֻÓÐÉÙÊý¼¸¸ö factoid ¿É±£Ö¤Óëʶ±ð³ÌÐòÔÚËùÓÐÓïÑÔʶ±ðÖÐÒ»ÆðʹÓã»ÐÒÔ˵ÄÊÇ£¬Digit ÊÇÆäÖÐÒ»¸ö£©¡£ ÅäÖà RecognizerContext Ö®ºó£¬ÎÒ³õʼ»¯ÁË InkOverlay¡£ _inkOverlay = new InkOverlay(this, true); _inkOverlay.Ink.CustomStrokes.Add(ScratchpadStrokesID, _inkOverlay.Ink.CreateStrokes()); _inkOverlay.Ink.CustomStrokes.Add(NormalStrokesID, _inkOverlay.Ink.CreateStrokes()); _inkOverlay.DefaultDrawingAttributes.Color = _inkColor; InkOverlay ½«±»ÊµÀý»¯²¢°ó¶¨µ½ PuzzleGrid£¨this ÒýÓã¬ÒòΪ´Ë´úÂë´æÔÚÓÚ PuzzleGrid ÀàÖУ©¡£ÎÒ½«Á½¸ö CustomStrokes ¼¯ºÏÌí¼Óµ½ InkOverlay¡£ÉÔºòÎÒ»áÔÚ±¾ÎÄÖжԴ˽øÐÐ˵Ã÷£¬µ«ÊǼò¶øÑÔÖ®£¬ËüÃÇʹµÃÇø·ÖÓû§ÊäÈëµÄ²»Í¬ÀàÐ͵ÄÊÖдÄÚÈݸüΪ¼òµ¥£¬»¹¿ÉÒÔÇáËɵØÓÀ¾Ã±£´æËù±£´æÓÎÏ·µÄÊÖдÄÚÈÝ¡£È»ºóÅäÖø²¸ÇͼÊÖдÄÚÈݵÄÑÕÉ«²¢ÉèÖø²¸ÇͼµÄ CollectionMode¡£ CollectionMode ÊôÐÔ»á¸æË߸²¸ÇͼÊÇ·ñÓ¦½«ÊäÈëµ±×÷ÊÖдÄÚÈÝ¡¢ÊÖÊÆ£¨´¥¿Ø±ÊµÄÒÆ¶¯±»½âÊÍÎªÌØ¶¨²Ù×÷£¬¶ø·ÇÄÚÈÝ£©»òÁ½Õß¡£Èç¹ûµ±Ç°Ã»ÓÐÊÖÊÆÊ¶±ð³ÌÐò£¬Ôò³¢ÊÔΪÊÖÊÆ¼¯ºÏÆôÓà InkOverlay ½«²úÉú´íÎó£¬Òò´ËÎÒÔÙ´ÎʹÓà PlatformDetection ÀàÈ·¶¨µ±Ç°ÊÇ·ñ´æÔÚ¿ÉÓõÄÊÖÊÆÊ¶±ð³ÌÐò¡£Èç¹û´æÔÚ£¬½«ÉèÖà CollectionMode.InkAndGesture£»·ñÔò½«ÉèÖà CollectionMode.InkOnly¡£ bool gestureRecognizerInstalled = PlatformDetection.GestureRecognizerInstalled; _inkOverlay.CollectionMode = gestureRecognizerInstalled ? CollectionMode.InkAndGesture :CollectionMode.InkOnly; Èç¹ûÉèÖÃÁË CollectionMode£¬½«¸æËß InkOverlay ÆäÊÇ·ñ¿ÉÒÔ½ÓÊÜÊÖÊÆ£¬µ«ÊÇûÓÐרÃÅ˵Ã÷ÒªÇóµÄÊÖÊÆ£¬Ò²Ã»ÓÐ˵Ã÷µ±ÕâЩÊÖÊÆ³öÏÖʱӦ½øÐеIJÙ×÷¡£¶ÔÓÚ Sudoku ¶øÑÔ£¬ÎÒÖ»¹Ø×¢ ApplicationGesture.Scratchout£¬Óû§Í¨¹ýËü¿ÉÒÔ²Á³ýÏÈǰÔÚÍø¸ñÖÐÊäÈëµÄÊý×Ö£¬½«Êý×Ö´ÓÓÎÏ·ÅÌÉÏɾ³ý¡£ÎªÁËÆôÓöÔÊÖÊÆµÄÖ§³Ö£¬ÎÒʹÓÃÁË InkOverlay.SetGestureStatus ·½·¨£¬²¢Ê¹Óà InkOverlay.Gesture ʼþ×¢²áÁËÒ»¸öʼþ´¦ÀíÆ÷£¬ÒԱ㴦Àí²Á³ýÊÖÊÆµÄ½ÓÊܲÙ×÷¡£ if (gestureRecognizerInstalled) { _inkOverlay.SetGestureStatus( ApplicationGesture.AllGestures, false); _inkOverlay.SetGestureStatus( ApplicationGesture.Scratchout, true); _inkOverlay.Gesture += new InkCollectorGestureEventHandler(HandleGesture); } È»ºóÅäÖà InkOverlay ÀàµÄ AutoRedraw ºÍ DynamicRendering ÊôÐÔ¡£Èç¹û½« AutoRedraw ÉèÖÃΪ true£¬Ôò InkOverlay ½«×Ô¶¯³ÊÏÖÈκβ¶»ñµ½µÄÍêÕûµÄ±Ê»¡£Èç¹û½« DyamicRendering ÉèÖÃΪ true£¬Ôò InkOverlay ½«×Ô¶¯³ÊÏÖµ±Ç°Ëù²¶»ñµÄÈκαʻ¡£ÎªÁËÆôÓÃÊÖдÄÚÈݳÊÏÖµÄË«»º³å´¦Àí£¬ÎÒÑ¡Ôñ×Ô¼º»æÖƲ¶»ñµÄÊÖдÄÚÈÝ£¬Òò´Ë½« AutoRedraw ÉèÖÃΪ false¡£µ«ÊÇ£¬ÎÒ¸üÀÖÒâÈà InkOverlay ³ÊÏÖµ±Ç°Óû§ÕýÔÚÊäÈëµÄÊÖдÄÚÈÝ£¬Òò´Ë½« DynamicRendering ÉèÖÃΪ true¡£ _inkOverlay.AutoRedraw = false; _inkOverlay.DynamicRendering = true; InkOverlay µÄÅäÖúܿì¾ÍÒªÍê³ÉÁË¡£ÐèÒª¸æËß InkOverlay ÈçºÎÏìÓ¦ÌØ¶¨µÄ´¥¿Ø±Ê½»»¥£¬¿Éͨ¹ý InkOverlay ÉÏÒ»×éʼþµÄ×¢²á´¦Àí³ÌÐòÀ´ÊµÏÖ¡£ • µ±Óû§Íê³ÉÒ»¸öбʻʱ£¬½«´¥·¢ Stroke ʼþ¡£ • µ±´¥¿Ø±Ê½øÈë tablet PC ´¥ÃþÆÁµÄÎïÀí¼ì²â·¶Î§£¨¿¿½ü£©Ê±£¬½«´¥·¢CursorInRange ʼþ¡£ • µ± InkOverlay ÊÕµ½Ò»¸öеÄÐÅÏ¢°ü£¬ÆäÖаüº¬À´×ÔÕýÔÚ½Ó´¥ÆÁÄ»µÄ´¥¿Ø±ÊÊý¾Ý£¬½«´¥·¢ NewPackets ʼþ¡£ • µ± InkOverlay ÊÕµ½µÄÐÅÏ¢°üÖаüº¬À´×ÔÆÁÄ»ÉÏ·½µÄ´¥¿Ø±Ê¿ÕÖÐÒÆ¶¯Êý¾Ýʱ£¬½«´¥·¢ NewInAirPackets ʼþ¡£ • µ±´Ó InkOverlay ÀàµÄ Strokes ¼¯ºÏÖÐɾ³ý±Ê»Ê±£¬½«´¥·¢ StrokesDeleting ʼþ¡£ Çë×¢ÒâCursorInRange ºÍ NewInAirPackets ʼþ½«±¨¸æ¸øµç´ÅÊý×Öת»»Æ÷ºÍÊó±ê£¬µ«²»»á±¨¸æ¸ø´¥ÃþʽÊý×Öת»»Æ÷¡£ _inkOverlay.Stroke += new InkCollectorStrokeEventHandler(HandleStroke); _inkOverlay.CursorInRange += new InkCollectorCursorInRangeEventHandler(HandleCursorInRange); _inkOverlay.NewPackets += new InkCollectorNewPacketsEventHandler(HandleNewPackets); _inkOverlay.NewInAirPackets += new InkCollectorNewInAirPacketsEventHandler(HandleNewInAirPackets); _inkOverlay.StrokesDeleting += new InkOverlayStrokesDeletingEventHandler(HandleStrokesDeleting); ×îºóÆôÓÃÁË InkOverlay¡£ _inkOverlay.Enabled = true; ʶ±ð¶à¸ö±Ê»×é³ÉµÄÊý×Ö
ÐèҪʶ±ðÓû§ÊäÈëʱ£¬ÎÒ»áʹÓà RecognizeStrokes ·½·¨¡£ private bool RecognizeStrokes(Strokes strokes, out byte number) { number = 0; if (_recognizerCtx != null && strokes.Count > 0) { _recognizerCtx.Strokes = strokes; _recognizerCtx.EndInkInput(); RecognitionStatus rs; RecognitionResult rr = _recognizerCtx.Recognize(out rs); if (rr != null && rs == RecognitionStatus.NoError) { string inputNumberText = rr.TopString; if (inputNumberText != null && inputNumberText.Length > 0) { try { number = byte.Parse(inputNumberText, CultureInfo.InvariantCulture); } catch(OverflowException){} catch(FormatException){} if (number >= 1 && number <= 9) return true; } } } return false; } RecognizeStrokes ½ÓÊÜÁ½¸ö²ÎÊý£ºÒªÊ¶±ðµÄ±Ê»¼¯ºÏÒÔ¼°°üº¬Ê¶±ðÊý×ÖµÄÊä³ö×Ö½Ú¡£Ëü¾ßÓÐÒ»¸ö²¼¶û·µ»ØÖµ£¬±íʾÊäÈëÊÇ·ñÒԿɽÓÊܵķ½Ê½Ê¶±ð£¬¼´Êä³öµÄÊý×ÖÊÇ·ñ¿ÉÒÔÓÃ×÷ÊäÈë¡£Ê×ÏÈ£¬RecognizeStrokes ½«¼ì²éÒÔÈ·±£Æä¾ßÓÐÒ»¸ö¿ÉÓõÄÓÐЧ RecognizerContext£¬²¢¼ì²éÊÇ·ñΪÆäÌṩÁËÈκαʻ¡£¼Ù¶¨Á½¸öÌõ¼þ¶¼¾¹ý¼ìÑ飬Ëü½«±Ê»´æÈë RecognizerContext ²¢Ê¹Óà EndInkInput ·½·¨Í¨Öª RecognizerContext ÒѾÍê³É½ÓÊÜÓÃÓÚʶ±ðµÄÊäÈ롣Ȼºóµ÷Óà Recognize ·½·¨½øÐÐʶ±ð¡£ Recognize ÌṩÁ˼¸ÌõÐÅÏ¢¡£´Ó Recognize ·µ»ØµÄ RecognitionResult ¶ÔÏó°üº¬¹ØÓÚ²Ù×÷½á¹ûµÄÐÅÏ¢£¬Äú¿ÉÒÔʹÓÃËùÌṩµÄ RecognitionStatus ö¾ÙÖµ£¨×÷Ϊ·½·¨Êä³ö²ÎÊý£©È·¶¨Ê¶±ð¹ý³ÌÖÐÊÇ·ñ·¢ÉúÁË´íÎó£¬Èç¹ûÆä¼ä·¢ÉúÁË´íÎó£¬ÔòÈ·¶¨´íÎóÀàÐÍ¡£RecognitionResult ¶ÔÏóµÄ TopString ÊôÐÔ½«×÷ΪÒÑʶ±ðÎı¾µÄ×Ö·û´®·µ»Ø£¬´ËʾÀýÖÐÓ¦×÷Ϊµ¥¸öÊý×Ö·µ»Ø¡£È»ºóʹÓà byte.Parse ·½·¨ÑéÖ¤Êý×ÖÊÇ·ñ±»ÕæÕýʶ±ð£¬Èç¹ûÊý×Ö±»Ê¶±ð£¬Ôò½«Æä·µ»Øµ½µ÷ÓóÌÐò¡£ ÎÒ¿ÉÒÔÔÚÓû§ÊäÈë±Ê»Ê±Ê¹Óà RecognizeStrokes£¬ÕâÕýÊÇÔÚ×î³õʵÏÖ Sudoku ʱËù½øÐеIJÙ×÷¡£µ«ÊǶÔÓÚÈκζà±Ê»ÊéдµÄÊý×Ö¶¼»á²úÉúÎÊÌ⣨ÕâÔÚ×î³õûÓÐÁϵ½£¬ÒòΪÎÒÓõ¥¸öÁ¬ÐøµÄ±Ê»ÊéдËùÓеÄÊý×Ö£©¡£ÊÕµ½¶à±Ê»ÊäÈëµÄijһ¸ö±Ê»ºó£¬½«µ÷Óà RecognizeStrokes£¬Èç¹û»¹ÓдóÁ¿±Ê»ÉÐδÊäÈ룬Ôò´ó¶àÊýÇé¿ö϶¼²»ÄÜÕýȷʶ±ðÊäÈë¡£ ÎÒÔÚ Sudoku ÖÐʹÓüÆÊ±Æ÷½â¾öÁË´ËÎÊÌâ¡£µ± InkOverlay ½ÓÊÕµ½ Stroke ʱ£¬½«Æô¶¯Ò»¸öÖÜÆÚΪ 0.5 ÃëµÄ¼ÆÊ±Æ÷¡£Èç¹û InkOverlay ÔÚ¼ÆÊ±Æ÷³¬Ê±Ç°ÓÖ½ÓÊÕµ½Ò»¸ö±Ê»£¬ÔòÖØÉè¼ÆÊ±Æ÷£¬ÔÊÐíÁíÒ»¸ö 0.5 ÃëµÄ¼ä¸ô¡£×îÖÕ£¬¼ÆÊ±Æ÷»áÔÚÍæ¼ÒÌí¼Ó¸ü¶à±Ê»Ç°³¬Ê±¡£¼ÆÊ±Æ÷³¬Ê±ºó£¬½«µ÷ÓüÆÊ±Æ÷µÄʼþ´¦ÀíÆ÷£¬´ÓÖÐʶ±ðÓû§ÊäÈ룬²¢¸ù¾ÝÍæ¼ÒÊäÈëµÄ±Ê»Î»Öý«Æä´æÈëÓÎÏ·ÏàÓ¦µÄµ¥Ôª¸ñÖС£ if (strokes.Count > 0) { byte number = 0; bool recognized = false; Point cell = GetCellFromStroke(strokes[0]); if (CanModifyCell(cell)) { recognized = RecognizeStrokes(strokes, out number); } _inkOverlay.Ink.DeleteStrokes(strokes); strokes.Clear(); if (recognized) SetStateCell(cell, (byte)(number-1)); } µ«ÊÇÇë×¢Ò⣬ÎÒ²¢Ã»ÓÐÒÔÏàͬ·½Ê½´¦ÀíËùÓбʻ¡£ÐèÒªÌØ±ðÖ¸³öµÄÊÇ£¬ÎÒ»áºöÂÔÎÒÈÏΪ´íÎóµÄ±Ê»£¬ÀýÈç´¥¿Ø±ÊżȻ´¥ÅöÆÁÄ»»òÓû§µã»÷ÆÁÄ»¸ü¸Äµ±Ç°Ñ¡¶¨µÄµ¥Ôª¸ñ¶ø²úÉúµÄ±Ê»¡£Îª´Ë£¬ÎÒ½« Stroke µÄ±ß½ç¿ò¾ØÐδÓÊÖдÄÚÈݿռä×ø±êת»»Îª¿Ø¼þ¿Õ¼ä¡£ Rectangle boundingBox = e.Stroke.GetBoundingBox(); InkToPixelSpace(ref boundingBox); È»ºó½«±ß½ç¿òµÄ³¤ºÍ¿íÓëÍø¸ñÖе¥Ôª¸ñµÄ³¤ºÍ¿í½øÐбȽϡ£Èç¹û±ß½ç¿òµÄ³¤»ò¿íСÓÚµ¥Ôª¸ñ³ß´çµÄij¸öÔ¤¶¨Òå·ÖÊý£¬ÔòºöÂÔ Stroke¡£Í¨¹ý½«Ê¼þ²ÎÊýµÄ Cancel ÊôÐÔÉèÖÃΪ true ¼´¿ÉÍê³É£¬ÕâÑù Tablet PC API ½«×Ô¶¯É¾³ý±Ê»¡£ÎÒ»¹ÐèҪȷ±£ÔÚÊäÈëÕý³£±Ê»µ«ÉÐδʶ±ðÖ®Ç°ÖØÐÂÆô¶¯¶à±Ê»¼ÆÊ±Æ÷¡£ RectangleF cellRect = GetCellRectangle(ClientRectangle, Point.Empty); if (boundingBox.Width < cellRect.Width * MINIMINUM_BOUNDING_RATIO_TO_RECOGNIZE && boundingBox.Height < cellRect.Height * MINIMINUM_BOUNDING_RATIO_TO_RECOGNIZE) { e.Cancel = true; ... } ÎÒ»¹Ê¹ÓÃÐí¶àÆäËûʼþÀ´Ê¶±ð±Ê»¡£¼ÙÉèÓû§ÔÚÉÏÒ»µ¥Ôª¸ñÊäÈëÊý×Ö½áÊøºó£¬»á½«´¥¿Ø±ÊÒÆÏòÆäËûµ¥Ôª¸ñ£¬Í¬Ê±´¥¿Ø±Ê»á´¥Åöµ½ÆÁÄ»»òÐüÍ£ÔÚ¿ÕÖС£Òò´Ë£¬ÕâʱÎҾͻáÍ£Ö¹¼ÆÊ±Æ÷²¢×Ô¶¯Ê¶±ð±Ê»£¬Èçͬ¼ÆÊ±Æ÷ÒѾ³¬Ê±¡£(RecognizePreviousCellFromPacket ½«¼ì²éÒÔÈ·¶¨µ±Ç°±Ê»ËùÔڵĵ¥Ôª¸ñÊÇ·ñÓë´¥¿Ø±Êϵĵ¥Ôª¸ñ²»Í¬£»Èç¹ûÁ½ÕßλÓÚͬһµ¥Ôª¸ñÄÚ£¬Ôò¿ÉÒÔ±à¼ÕâЩ±Ê»£¬Õâʱ½«µ÷Óà RecognizeStrokes¡££© private void HandleNewInAirPackets( object sender, InkCollectorNewInAirPacketsEventArgs e) { if (e.PacketData.Length >= 2) { RecognizePreviousCellFromPacket( new Point(e.PacketData[0], e.PacketData[1])); } } ÏìÓ¦ NewPackets ºÍ Stroke ʼþËùÓõÄÂß¼Ïàͬ¡£Í¼ 12 ÏÔʾÁËͨ¹ý¶à¸ö±Ê»ÊäÈëµÄÊý×Ö£¨ÀýÈçÊý×Ö 4 ÓÉÁ½¸ö±Ê»×é³É£©£¬Í¼ 13 ÏÔʾÁËÊý×Ö±»Ê¶±ð²¢³ÊÏÖºóµÄ½á¹û¡£  ͼ 12. Êý×Ö 4 Óɶà¸ö±Ê»×é³É ͼ 13. Êý×Ö 4 Óɶà¸ö±Ê»Ê¶±ð
³·ÏûÖ§³Ö ¶à¼¶³·ÏûÊÇËùÓÐÓ¦ÓóÌÐòÖÐÎÒ×îϲ»¶µÄ¹¦ÄÜÖ®Ò»£¬×ÔȻҲÔÚ Sudoku Ó¦ÓóÌÐòÖÐʵÏÖÁ˸ù¦ÄÜ¡£Í¨¹ýʹÓà PuzzleState.Clone£¬¿ÉÒÔÏ൱ÈÝÒ×µÄʵÏָù¦ÄÜ¡£ »ù±¾ÀíÄîÊÇÿ´Î¶ÔÓÎÏ·Å̽øÐиü¸Äʱ£¬¶¼»á½«µ±Ç°×´Ì¬µÄ¸±±¾Ñ¹Èë³·Ïû¶ÑÕ»ÖС£ private PuzzleStateStack _undoStates = new PuzzleStateStack(); PuzzleStateStack ÀàÐÍÊÇÒ»¸öСµÄÇ¿ÀàÐÍÀ࣬´Ó System.Collections ÖÐµÄ Stack ÀàÅÉÉú¶øÀ´¡£ÒÔºóÈç¹ûÒª³·Ïû²¢·µ»Øµ½ÏÈǰ״̬£¬Ôò¿É´Ó³·Ïû¶ÑÕ»Öе¯³ö״̬£¬²¢½«ÆäÉèΪµ±Ç°×´Ì¬¼´¿É¡£ public void Undo() { if (_undoStates.Count > 0) this.State = _undoStates.Pop(); } ΨһµÄ¼¼ÇɾÍÊÇÈ·±£Ã¿´Î¸ü¸Äµ±Ç°×´Ì¬Ê±£¬¶¼Òª½«µ±Ç°×´Ì¬Ñ¹Èë³·Ïû¶ÑÕ»¡£ÔÚÄÚ²¿£¬PuzzleGrid ¿ÉÈ·±£ÔÚÈκÎʱºò×÷³ö¸ü¸Äʱ¶¼»á½«×´Ì¬Ñ¹Èë¶ÑÕ»£¬µ«ÊÇ PuzzleGrid »á¹«¿ªÌṩµ±Ç°µÄ PuzzleState ʵÀý£¬Òò´ËÀûÓøÃʵÏÖ£¬²»Äܱ£Ö¤Ã¿´Î¸ü¸Ä¶¼»á±»³É¹¦¼Ç¼¡£Òò´Ë£¬PuzzleGrid ͨ¹ýÌṩ SetUndoCheckpoint ³ÉÔ±²¢¸³Óè¿Ø¼þʹÓÃÕßÊʵ±ÔðÈΣ¬È·±£¶Ôµ±Ç°×´Ì¬½øÐÐÈκÎÖØ´ó¸ü¸Ä֮ǰ¶¼µ÷Óà SetUndoCheckpoint¡£ public void SetUndoCheckpoint() { _undoStates.Push(State.Clone()); } PuzzleGrid »¹ÔÚÄÚ²¿Ê¹Óà SetUndoCheckpoint¡£ÒªÏëÁ˽âºÎʱºÎµØ±»µ÷Óã¬Ê¾ÀýÖ®Ò»¾ÍÊǶà±Ê»¼ÆÊ±Æ÷ʼþ´¦ÀíÆ÷£¨ÈçǰËùÊö£©ÖÐʹÓÃµÄ SetStateCell ·½·¨¡£ private void SetStateCell(Point cell, byte number) { if (State[cell] != number) { SetUndoCheckpoint(); ClearTabletStateCell(cell); State[cell] = number; } } ¸Ã·½·¨Ê×Ïȼì²éÒÔÈ·±£Ä¿±êµ¥Ôª¸ñµÄÐÂÖµÓëÒÑ´æÔÚµÄÖµ²»Í¬¡£¼ÙÉè±ØÐë½øÐиü¸Ä£¬¸Ã·½·¨½«ÉèÖÃÒ»¸ö³·Ïû¼ì²éµã£¬½«µ±Ç°µÄ PuzzleState ѹÈë³·Ïû¶ÑÕ»¡£È»ºóÏòµ¥Ôª¸ñÌí¼ÓÒ»¸öÖµ¡£¸Ã·½·¨»¹Òªµ÷Óà ClearTabletStateCell ·½·¨¡£ClearTabletStateCell ÔÚÓÎÏ·ÖÐÖ÷Òª´¦Àí±ãÇ©²¾¹¦ÄÜ¡£ ±ãÇ©²¾Ö§³Ö ΪÁËÈÃÍæ¼ÒÔÚÍæÓÎÏ·µÄʱºòÄÜÓÐÔÚÖ½ÉÏÍæµÄ¸Ð¾õ£¬ÎÒÔÚ Sudoku ÖÐʵÏÖµÄÖ÷Òª¹¦ÄÜÖ®Ò»¾ÍÊÇǦ±Ê±ãÇ©¹¦ÄÜ£¬ÔÚ´úÂëÖгÆ×÷ scratchpad¡£Ò²¾ÍÊÇ˵£¬Äú¿Éͨ¹ý¸Ã¹¦ÄܽøÐмǼ£¬ÄÜÔÚÕû¸öÓÎÏ·¹ý³ÌÖиú×ÙÄúµÄÂß¼ÑÝÒÀàËÆÓÚÔÚÖ½ÉÏÓÎÏ·ÖÐʹÓÃǦ±Ê½øÐмǼ£¨¼ûͼ 14£©¡£ÕâЩ¼Ç¼²»»á±»Ê¶±ðΪ´¥¿Ø±ÊдÈëµÄÄÚÈÝ£¨ÔÚ´úÂëÖгÆ×÷ normal£©£»¶øÊÇ×÷Ϊ±ê¼ÇÊý¾ÝÓë PuzzleState Ò»Æð´æ´¢¡£ 
ͼ 14. ʹÓñãÇ©²¾½øÐмǼ ÔÚ˵Ã÷ÈçºÎʵÀý»¯ºÍÅäÖÃËùÓÃµÄ InkOverlay ʱ£¬ÎÒÔø¼òµ¥Ì¸ÆðÁ½ÌõÖ¸Áî¡£ _inkOverlay.Ink.CustomStrokes.Add(ScratchpadStrokesID, _inkOverlay.Ink.CreateStrokes()); _inkOverlay.Ink.CustomStrokes.Add(NormalStrokesID, _inkOverlay.Ink.CreateStrokes()); InkOverlay ÀàµÄ Ink ÊôÐÔ½«·µ»Øµ±Ç°Ó븲¸ÇͼÏà¹ØµÄ Ink ¶ÔÏ󣬲¢ÇÒÆä CreateStrokes ·½·¨½«´´½¨Óë¸Ã Ink ¶ÔÏóÏà¹ØµÄРStrokes ¼¯ºÏ¡£Í¨¹ý´´½¨ºÍά»¤Á½¸ö²»Í¬µÄ Strokes ¼¯ºÏ£¬¿ÉÒÔÇáËɵÄÇø±ðÔÚÕý³£´¥¿Ø±ÊģʽºÍ±ãÇ©²¾Ç¦±ÊģʽÏ´´½¨µÄ±Ê»¡£´Ó¶ø¿ÉÒÔÇáËɵØÕë¶ÔÆäÖÐÒ»ÖÖÀàÐ͵ıʻ½øÐвÙ×÷¡£ÎªÁ˼òµ¥µÄ¼ìË÷¼¯ºÏ²¢È·±£ËüÃÇÓë Ink ¶ÔÏóÒ»Æð±»ÕýÈ·µÄÐòÁл¯£¬ÎÒ½«ÕâЩ¼¯ºÏ¶¼Ìí¼Óµ½ Ink ¶ÔÏóµÄ CustomStrokes ¼¯ºÏÖУ¨ÐòÁл¯Êǽ« Ink ¶ÔÏóת»»³É¹©´æ´¢µÄ×Ö½ÚÊý×éµÄ¹ý³Ì¡£ÓÐÁ½¸ö²»Í¬µÄʹÓÃÄ¿µÄ£¬ÎÒ½«¼òҪ˵Ã÷£©¡£CustomStrokes ¼¯ºÏÖаüº¬Èô¸ÉÃûΪ Strokes µÄ¼¯ºÏ£¬ÕâЩ¼¯ºÏ½«±»±£ÁôÒÔ¹©½ñºóʹÓá£ÎÒ»¹¶¨ÒåÁ˼¸¸öÖúÊÖÊôÐÔ£¬¿ÉÒÔÇáËɵزéѯºÍʹÓÃÕâЩ¼¯ºÏ¡£ private Strokes NormalStrokes { get { return _inkOverlay.Ink.CustomStrokes[NormalStrokesID]; } } private Strokes ScratchpadStrokes { get { return _inkOverlay.Ink.CustomStrokes[ScratchpadStrokesID];} } private bool HasScratchpadStrokes { get { using(Strokes strokes = ScratchpadStrokes) { return strokes.Count > 0; } } } Çë×¢Ò⣬Èç¹û±ãÇ©²¾±Ê»¼¯ºÏÖдæÔÚÈκαʻ£¬Ôò HasScratchpadStrokes ½«·µ»Ø true£¬Ê¹ÓÃÍê±ÏÖ®ºó»á¶Ô±ãÇ©²¾ Strokes ¼¯ºÏ½øÐд¦Àí¡£ÕâËÆºõºÜÆæ¹Ö£¬µ«ÄúÔÚ¼ì²é CustomStrokes Ë÷ÒýÉú³ÉÆ÷ʵ¼ÊÖ´ÐеIJÙ×÷ºó¾Í»áÃ÷°×¡£ÒÔÏÂÊÇÆäʵÏÖµÄα´úÂë¡£ public Strokes this[string name] { get { return new Strokes(this.m_CustomStrokes.Item(name)); } } Ëü»á·µ»ØÐ嵀 Strokes ¶ÔÏ󣬶ø²»ÊÇ×î³õ´æ´¢ÔÚ¼¯ºÏÖеĶÔÏó¡£É¾³ýµÄ¾ÍÊÇÕâ¸ö Strokes ¶ÔÏ󣬶ø²»ÊÇÔʼµÄÄǸö¡£µ±´Ó InkOverlay É쵀 Ink ¶ÔÏó´æÈ¡Ö÷ Strokes ¼¯ºÏʱ£¬Çé¿öÒ²ÊÇÈç´Ë¡£ÒÔÏÂÊÇ Strokes ¶ÔÏó»ñÈ¡´æÈ¡Æ÷µÄα´úÂë¡£ public Strokes Strokes { get { if (this.disposed) { throw new ObjectDisposedException(GetType().FullName); } return new Strokes(this.m_Ink.Strokes); } } Äúÿ´Î´æÈ¡ Strokes ÊôÐÔʱ¶¼»á·µ»ØÒ»¸öÐ嵀 Strokes ʵÀý£¬ÄúÐèҪȷ±£ÔÚ½áÊøÊ±É¾³ýÕâЩ¶ÔÏó¡£ÀýÈ磬ÒÔÏ´úÂëÏÔʾÁË PuzzleGrid ÓÃÓÚÊÖ¶¯»æÖÆËùÓÐÊÖдÄÚÈÝµÄ RenderInk ·½·¨£¨Çë¼ÇסÎÒÃǹرÕÁË InkOverlay É쵀 AutoRedraw£©¡£ private void RenderInk(Graphics graphics) { if (_inkOverlay != null) { using(Strokes strokes = _inkOverlay.Ink.Strokes) { if (strokes.Count > 0) { _inkOverlay.Renderer.Draw(graphics, strokes); } } } } PuzzleGrid ¼Ç¼ÁËÍø¸ñÊÇ·ñ´¦ÓÚ±ãÇ©²¾Ä£Ê½£»¸Ãת»»ÓÉÖ÷´°ÌåÉϵÄǦ±ÊºÍ»±Ê°´Å¥¿ØÖÆ¡£´¦ÓÚ±ãÇ©²¾Ä£Ê½Ê±£¬InkOverlay ÉÏ Stroke ʼþµÄʼþ´¦ÀíÆ÷²»»áÖ´ÐÐÇ°ÃæËùÊöµÄ¶à±Ê»Âß¼¡£Ïà·´£¬Ëü»áÏò×Ô¶¨Òå±ãÇ©²¾µÄ Strokes ¼¯ºÏÌí¼ÓËùÓмìË÷µ½µÄ±Ê»¡£µ«»¹ÐèҪһЩ¸½¼Ó¹¤×÷¡£ Ê×ÏÈ£¬¸÷¸ö±ãÇ©²¾±Ê»±ØÐ뽫Æä×ÔÉíµÄ¸ü¸Ä±£´æÔÚ³·Ïû¶ÑÕ»ÖУ¬ÒԱ㵥¸ö¼Ç¼¿ÉÒÔ³·Ïû¡£ÔÚ½«±Ê»Êý¾ÝÌí¼Óµ½ Ink ¶ÔÏó֮ǰ£¬InkOverlay Éϲ»´æÔÚÈκδ¥·¢µÄʼþ£¬Òò´ËºÜÄÑʵÏÖÕâÒ»²Ù×÷¡£ÐèÒªÒ»¸öС¼¼ÇÉÀ´´¦ÀíÕâÖÖÇé¿ö¡£±ØÐë´Ó Ink ¶ÔÏóÖÐɾ³ý±Ê»£¬ÉèÖó·Ïû¼ì²éµã£¬È»ºóÔÙÖØÐÂÌí¼Ó±Ê»¡£²»ÐÒµÄÊÇ£¬É¾³ýµÄ±Ê»²»ÔÙ¿ÉÓ᣽â¾ö·½·¨Êǽ«±Ê»ÐòÁл¯£¬É¾³ý×î³õµÄ±Ê»£¬È»ºóÔÚÉèÖó·Ïû¼ì²éµãºó´´½¨Ò»¸öȫеıʻ£¨»ùÓÚÀ´×ԾɱʻµÄÊý¾Ý£©¡£È»ºó¾Í¿ÉÒÔ½«Ð±ʻÌí¼ÓÖÁ Ink ¶ÔÏó¡£ Stroke s = e.Stroke; TabletPropertyDescriptionCollection tpdc = new TabletPropertyDescriptionCollection(); foreach (Guid g in s.PacketDescription) { TabletPropertyDescription tpd = new TabletPropertyDescription( g, e.Cursor.Tablet.GetPropertyMetrics(g)); tpdc.Add(tpd); } int[] packetData = e.Stroke.GetPacketData(); _inkOverlay.Ink.DeleteStroke(e.Stroke); SetUndoCheckpoint(); s = _inkOverlay.Ink.CreateStroke(packetData, tpdc); Stroke ÓÉһϵÁеã×é³É£¬Ã¿¸öµã¶¼ÊÇÓÉÊý¾Ý°ü²úÉúµÄ¡£Äú¿ÉÒÔÔÚ Stroke ÉÏʹÓà GetPacketData ·½·¨¼ìË÷¸ÃÐÅÏ¢°üÊý¾ÝµÄÊý×飬²¢ÇÒ Ink ¶ÔÏóÖÐµÄ CreateStroke ·½·¨ÓÐÒ»¸öÖØÔØ£¬¿ÉÓÃÓÚ½ÓÊÜ´ËÀàÊý×é²¢´ÓÖд´½¨Ò»¸öÐ嵀 Stroke¡£ÎÊÌâÊÇ£¬¸Ã·½·¨»¹ÐèÒªÐÅÏ¢°üÊý¾ÝÖÐËù°üº¬ÄÚÈݵÄÃèÊö£¬²¢ÇÒ¸ÃÃèÊöÓ¦¸ÃÒÔ TabletPropertyDescriptionCollection µÄÐÎʽ¸ø³ö¡£Òò´Ë£¬ÔÚɾ³ý Stroke ֮ǰ£¬ÄúÐèÒª¼ì²éÆäÊÇ·ñ¾ßÓиÃÃèÊö¡£Stroke ÖÐµÄ PacketDescription ÊôÐÔ½«·µ»ØÒ»¸ö Guids Êý×飬Õë¶Ô Stroke Öд洢µÄÿ¸öÐÅÏ¢°üÊý¾ÝÀàÐ;ù·µ»ØÒ»¸öÊý×é¡£¿ÉÒÔö¾ÙÕâЩÊý×飬ÒÔ½¨Á¢ CreateStroke µ÷ÓÃËùÐèµÄ TabletPropertyDescriptionCollection¡£ ÀûÓÃÏÖÓеij·ÏûÖ§³Ö£¬¿ÉÈñãÇ©²¾±Ê»ÏÔʾ²»Í¬µÄÑÕÉ«£¬ÒÔ½«±ãÇ©²¾±Ê»ÓëÕý³£±Ê»Çø±ð¿ªÀ´¡£ s.DrawingAttributes.Color = _scratchpadInkColor; »¹ÓÐһС¶Î¼¼ÇÉÐԵĴúÂë¡£ÔÚ±¾ÎÄ¿ªÊ¼£¬ÎÒÏêϸ˵Ã÷ÁËÖ§³Ö¿Éµ÷Õû´°Ìå´óСµÄÖØÒªÐÔ£¬²¢ÇÒ˵Ã÷ÁËÓ¦¸ÃÈçºÎµ÷Õû²»Í¬¿Ø¼þµÄ´óС£¬ÀýÈç PuzzleGrid¡£Ã¿¸ö±Ê»¶ÔÏóÒ²ÐèÒªµ÷Õû´óСºÍλÖã¬ÒԱ㱣³ÖÏà¶ÔÓÚ PuzzleGrid µÄ´óСºÍλÖá£ËµÆðÀ´ÈÝÒ××öÆðÀ´ÄÑ¡£ÎҵĽâ¾ö·½°¸ÊDZØÐëʹÿ¸ö±ãÇ©²¾±Ê»ÔÚ´´½¨Ê±Óë±Ê»ºÍÍø¸ñµÄµ±Ç°´óСÏà¹ØÁª¡£Ã¿´Îµ÷ÕûÍø¸ñ´óСʱ£¬ÕâÒ»ÐÅϢʹµÃ±Ê»´óСҲÄܵõ½ÏàÓ¦µ÷Õû¡£¸ù¾Ý±Ê»µÄÔʼ´óСºÍλÖýøÐÐת»»£¬¶ø²»ÊÇÆäµ±Ç°µÄ´óСºÍλÖ㬴Ӷø±ÜÃâÁËÉáÈëÎó²î£¨ÓëÒÔ¸¡µãÊý´æ´¢¸ÃÊý¾ÝÏà¹Ø£©Ëù²úÉúµÄÎÊÌ⣻Èç¹ûƵ·±µ÷Õû´óС£¬Ò»¶Îʱ¼ä¹ýºó´ËÀà´íÎ󽫵¼ÖÂÊéдÄÚÈÝÒÆÎ»¡£ ÎÒ½«ËùÓаó¶¨ÐÅÏ¢´æ´¢ÔÚ Stroke ¶ÔÏóµÄ ExtendedProperties ÖС£Ó¦ÓóÌÐò¿ÉÒÔʹÓà Stroke.ExtendedProperties ´æÈ¡´æ´¢ÔÚ Stroke ¶ÔÏóÖеÄ×Ô¶¨ÒåÊý¾Ý¡£ÕâЩ×Ô¶¨ÒåÊý¾Ý½«×Ô¶¯Óë¶ÔÏóÐòÁл¯¡£Òò´Ë£¬ÎÒÔÚ ExtendedProperties Öд洢ÁËÒÔÏÂÁùÌõÐÅÏ¢£ºStroke ±ß¿òµÄλÖà (x,y) ºÍ´óС£¨³¤£¬¿í£©£¬ÒÔ¼° PuzzleGrid ¿Ø¼þµÄµ±Ç°´óС£¨³¤£¬¿í£©¡£ExtendedProperties ÖÐ Add ·½·¨µÄµÚÒ»¸ö²ÎÊýÊÇ Guid£¬¿ÉÓÃÓÚʶ±ðËù´´½¨µÄÌØ¶¨À©Õ¹ÊôÐÔ¡£Ëü¿ÉÒÔÊÇÄúÐèÒªµÄÈκΠGuid£¬µ«Èç¹ûÄú½« Strokes ÐòÁл¯ÒÔÓÀ¾Ã´æ´¢£¬¹©ÒÔºóÔËÐÐÓ¦ÓóÌÐòʱ¼ìË÷ Strokes£¬ÔòÇëÈ·±£ÓÃÓÚÀ©Õ¹ÊôÐ﵀ Guids ÔÚÿ´ÎÔËÐÐÓ¦ÓóÌÐòʱ±£³ÖÒ»Ö¡£ Rectangle boundingBox = s.GetBoundingBox(); using(Graphics graphics = CreateGraphics()) { InkSpaceToPixelSpace(graphics, ref boundingBox); s.ExtendedProperties.Add(OriginalStrokeBoundRectXGuid, boundingBox.X); s.ExtendedProperties.Add(OriginalStrokeBoundRectYGuid, boundingBox.Y); s.ExtendedProperties.Add(OriginalStrokeBoundRectWidthGuid, boundingBox.Width); s.ExtendedProperties.Add(OriginalStrokeBoundRectHeightGuid, boundingBox.Height); s.ExtendedProperties.Add(OriginalClientRectWidthGuid, ClientRectangle.Width); s.ExtendedProperties.Add(OriginalClientRectHeightGuid, ClientRectangle.Height); } ½ÓÏÂÀ´£¬µ±µ÷Õû PuzzleGrid µÄ´óСʱ£¬ÎÒʹÓôËÐÅÏ¢Ð޸ĸ÷¸ö Stroke¡£ protected override void OnResize(EventArgs e) { _cachedEmSize = -1; ResizeScratchpadInk(); base.OnResize(e); } internal void ResizeScratchpadInk() { if (_inkOverlay != null) { Rectangle currentClientRect = ClientRectangle; using(Strokes scratchpadStrokes = ScratchpadStrokes) { foreach(Stroke s in scratchpadStrokes) { int originalBoundsX = (int)s.ExtendedProperties[ OriginalStrokeBoundRectXGuid].Data; int originalBoundsY = (int)s.ExtendedProperties[ OriginalStrokeBoundRectYGuid].Data; int originalBoundsWidth = (int)s.ExtendedProperties[ OriginalStrokeBoundRectWidthGuid].Data; int originalBoundsHeight = (int)s.ExtendedProperties[ OriginalStrokeBoundRectHeightGuid].Data; int originalClientRectWidth = (int)s.ExtendedProperties[ OriginalClientRectWidthGuid].Data; int originalClientRectHeight = (int)s.ExtendedProperties[ OriginalClientRectHeightGuid].Data; double scaleX = currentClientRect.Width / (double)originalClientRectWidth; double scaleY = currentClientRect.Height / (double)originalClientRectHeight; Rectangle newBounds = new Rectangle( (int)(originalBoundsX*scaleX), (int)(originalBoundsY*scaleY), (int)(originalBoundsWidth*scaleX), (int)(originalBoundsHeight*scaleY)); using(Graphics graphics = CreateGraphics()) { PixelSpaceToInkSpace(graphics, ref newBounds); s.ScaleToRectangle(newBounds); } } } } } ¶ÔÓÚÿ¸ö±Ê»£¬½«¼ÆËã¿Ø¼þµ±Ç°´óСÓë±Ê»´´½¨Ê±¿Ø¼þ´óСµÄ±ÈÀý£¨Çë×¢Òâ±ØÐë¶Ôÿ¸ö±Ê»½øÐмÆË㣬ÒòΪ´´½¨²»Í¬±Ê»Ê±µÄ¡°³õʼ¡±¿Ø¼þ´óС¿ÉÄܲ»Í¬£¬¼´ PuzzleGrid ¿ÉÄÜÔÚ²»Í¬±Ê»´´½¨¹ý³ÌÖе÷ÕûÁË´óС£©¡£È»ºóͨ¹ý´Ë±ÈÀýºÍ±Ê»µÄ³õʼ±ß½ç¼ÆËã±Ê»µÄб߽硣ÔÙ½«´ËÐÅÏ¢Ìṩ¸ø Stroke ÖÐµÄ ScaleToRectangle ·½·¨£¬¸Ã·½·¨ÔÚ Stroke ÖÐÖ´ÐÐʵ¼ÊµÄµ÷Õû²Ù×÷¡£ µ±ÔÚÆÕͨģʽºÍ±ãÇ©²¾Ä£Ê½¼äÇл»Ê±£¬ÔÚ×Ô¼ºµÄ¼¯ºÏÖÐÓµÓбãÇ©²¾±Ê»½«·Ç³£·½±ã¡£Ó¦ÓóÌÐòÔÚÕâÁ½ÖÖģʽÏÂÔËÐвúÉúµÄÊÓ¾õÇø±ðÖ®Ò»ÊÇ£¬ÔÚÆÕͨģʽÏÂʱ£¬±ãÇ©²¾ÊÖдÄÚÈÝ»ÒÏÔ£¬¶øÔÚ±ãÇ©²¾Ä£Ê½ÏÂʱ£¬ÊÖдÄÚÈÝΪ´¿ºÚÉ«¡£Í¨¹ý¸ü¸Äÿ¸ö±ãÇ©²¾ Stroke ʵÀýÖÐµÄ DrawingAttributes ¿ÉÒÔʵÏÖ¡£ÀýÈ磬Ҫ½«¸÷¸ö±Ê»µÄÑÕÉ«¸ü¸ÄΪ»ÒÉ«£¬¿ÉʹÓÃÒÔÏ´úÂë¡£ using(Strokes strokes = ScratchpadStrokes) { foreach(Stroke s in strokes) { s.DrawingAttributes.Color = Color.Gray; } } µ±Óû§½«Êý×ÖÊäÈëµ¥Ôª¸ñʱ£¬ÔÚ×Ô¼ºµÄ¼¯ºÏÖÐÓµÓбãÇ©²¾±Ê»Ò²»á·Ç³£·½±ã¡£µ±Óû§½«Êý×ÖÊäÈëµ¥Ôª¸ñʱ£¬ÎÒ»áµ÷Óà SetStateCell ·½·¨£¨ÈçǰËùʾ£©¡£¸Ã·½·¨½Ó×Å»áµ÷Óà ClearTabletStateCell ·½·¨¡£ClearTabletStateCell ½«É¾³ýµ¥Ôª¸ñÖеÄËùÓбãÇ©²¾±Ê»£¬ÒòΪÓû§¸Õ¸ÕÏòÄ¿±êµ¥Ôª¸ñÊäÈëÁËÒ»¸öÂß¼ÑÝÒïÊý×Ö£¬Òò´Ë¿ÉÒÔɾ³ýÓë¸Ãµ¥Ôª¸ñÏà¹ØµÄ¼Ç¼£¬ÒÔ±ÜÃâ²»±ØÒªµÄ¸ÉÈÅ¡£ClearTabletStateCell µÄ¹¤×÷·½Ê½ÊÇ£¬±éÀú±ãÇ©²¾±Ê»¼¯ºÏÖеÄËùÓбʻ£¬É¾³ýÄÇЩλÓÚµ¥Ôª¸ñÖеıʻ¡£ private void ClearTabletStateCell(Point cell) { if (_inkOverlay != null) { //ɾ³ý¸Ãµ¥Ôª¸ñÖÐËùÓеıãÇ©²¾±Ê» using(Strokes strokes = ScratchpadStrokes) { for(int i=strokes.Count-1; i>=0; --i) { Stroke s = strokes[i]; if (GetCellFromStroke(s) == cell) { strokes.RemoveAt(i); _inkOverlay.Ink.DeleteStroke(s); } } } } } ÏðÆ¤²ÁÖ§³Ö
±ÊʽÏðÆ¤²ÁÖ§³ÖÊÇÊܵ½Ðí¶à Tablet PC Ö§³ÖµÄÒ»¸ö±ãÀû¹¦ÄÜ¡£½«ÄúµÄ´¥¿Ø±Êµ¹×ª¹ýÀ´£¬Äú¿ÉÒÔʹÓÃÁíÒ»¶Ë×÷Ϊһ¸öÐéÄâµÄÏðÆ¤²Á£¬Ç°ÌáÊǸÃÓ¦ÓóÌÐòÖ§³Ö´Ë¸ÅÄî¡£Sudoku ¿ÉÒÔʵÏÖÕâÒ»µã¡£ InkOverlay ÀàÌṩÁË InkOverlayEditingMode ÀàÐ굀 EditingMode ÊôÐÔ¡£¸ÃÊôÐÔµÄĬÈÏÖµÊÇ InkOverlayEditingMode.Ink¡£µ«ÊÇ£¬µ±ÉèÖÃΪ InkOverlayEditingMode.Delete ʱ£¬¹â±ê»á±ä³ÉÒ»¸öÏðÆ¤²Á£¬ÔÚÏÖÓеıʻÉÏÍÏ×§¿ÉÒÔ½«Æäɾ³ý£¬¶ø²»ÊÇ´´½¨ºÍ±£´æÐµÄÊÖд±Ê»¡£Í¨³££¬Ó¦ÓóÌÐò»áÔÚʼþ´¦ÀíÆ÷ÖÐΪ InkOverlay É쵀 CursorInRange ʼþ¼ì²éºÍÉèÖøÃÊôÐÔ£¬¶ø Sudoku ÕýÊÇÕâÑù×öµÄ¡£InkCollectorCursorInRangeEventArgs ʵÀý×÷Ϊ CursorInRange ʼþµÄʼþ´¦ÀíÆ÷Ò»¸ö²ÎÊý¶øÌṩ£¬¸ÃʵÀýÓµÓÐÒ»¸ö Cursor ÊôÐÔ£¬»á·µ»ØÒ»¸ö Microsoft.Ink.Cursor ʵÀý£¬¶ø¸Ã¶ÔÏó¾ßÓв¼¶ûÀàÐ굀 Inverted ÊôÐÔ£¬»á·µ»Ø´¥¿Ø±ÊÊÇ·ñÉÏÃæÏòÏ£¬´Ó¶øÈ·¶¨Óû§ÊÇ·ñ³¢ÊÔʹÓÃÏðÆ¤²Á¡£ÎÒµÄʼþ´¦ÀíÆ÷ͨ¹ýÊʵ±µÄÉèÖà InkOverlay É쵀 EditingMode À´¶Ô´Ë×ö³öÏìÓ¦¡£ Çë×¢Òâ ¿ÕÖÐÊý¾Ý°ü¼ì²âÓÃÓÚµç´ÅÊý×Öת»»Æ÷ºÍÊó±ê£¬µ«²»ÓÃÓÚ´¥ÃþʽÊý×Öת»»Æ÷¡£ ÊÕµ½É¾³ý±Ê»ºó£¬½«´¥·¢ InkOverlay É쵀 Stroke ʼþ¡£µ«ÊÇ£¬´Ë¿Ì»áÖ´ÐÐÇå³ýÏðÆ¤²Áϵ¥Ôª¸ñµÄ´úÂ룬¶ø²»ÊÇÖ´Ðжà±Ê»Ê¶±ðÂß¼£¬ÔÚ EditingMode ÉèÖÃΪ InkOverlayEditingMode.Ink ʱ»áÖ´ÐиÃÂß¼¡£ Point currentStrokeCell = GetCellFromStroke(e.Stroke); if (CanClearCell(currentStrokeCell)) { ClearStateCell(currentStrokeCell); } ´Ë´úÂë½öÇå³ýÒ»¸öµ¥Ôª¸ñ£¬ÒòΪ GetCellFromStroke ½«»ùÓÚ Stroke Éϵı߿òÌôÑ¡×îÓпÉÄܵÄÄ¿±êµ¥Ôª¸ñ¡£µ«ÊÇ£¬Èç¹ûÍæ¼ÒÍ϶¯ÏðÆ¤²Á¿çÔ½¶à¸öµ¥Ôª¸ñ£¬ÔòËûÃǺܿÉÄÜÏëÒªÇå³ýËùÓнӴ¥µ½µÄµ¥Ôª¸ñ¡£ÒªÔÚÓû§Í϶¯ÏðÆ¤²Á¿çÔ½¶à¸öµ¥Ôª¸ñʱÇå³ýËùÓнӴ¥µÄµ¥Ôª¸ñ£¬¿É½«ÀàËÆµÄÂß¼Ìí¼Óµ½ InkOverlay ÉÏ NewPackets ʼþµÄʼþ´¦ÀíÆ÷ÖС£ if (_mode == PuzzleGridMode.Eraser || e.Cursor.Inverted) { if (e.PacketData.Length >= 2) { Point cell = TabletToCell( new Point(e.PacketData[0], e.PacketData[1])); if (CanClearCell(cell) && State[cell].HasValue) { ClearStateCellWithInvalidation(cell); } if (_selectedCell.HasValue) { InvalidateCell(_selectedCell.Value); } SetSelectedCell(cell); InvalidateCell(_selectedCell.Value); } } µ±´¦ÓÚ±ãÇ©²¾Ä£Ê½Ï£¬ÎÒ½«Ó¦ÓÃÒ»¸ö²»Í¬µÄÂß¼¡£InkOverlay ÉÏ InkOverlayEditingMode.Delete µÄĬÈÏ´¦ÀíÊÇÇå³ý¹â±êϵÄÈκαʻ¡£ÕâÕýÊÇÔÚ±ãÇ©²¾Ä£Ê½ÏÂÎÒÏëÒªµÄÐÐΪ£¬Òò´ËÎÒÈà InkOverlay À´Íê³ÉÕâÏî×÷Òµ¡£Ö»ÐèÁ½²½¡£µÚÒ»£¬ÎÒÐèÒªÔÚɾ³ý±Ê»Ç°ÉèÖÃÒ»¸ö³·Ïûµã£¬ÕâÑù¾Í¿ÉÒÔʹÓó·ÏûÀ´»Ö¸´É¾³ýµÄ±Ê»¡£µÚ¶þ£¬ÎÒÐèÒª´Ó×Ô¶¨ÒåÊÕ¼¯ÖÐÊÖ¶¯Çå³ýÒÑɾ³ýµÄ±Ê»¡£ÎÒ¿ÉÒÔÔÚ InkOverlay ÉÏ StrokesDeleting ʼþµÄʼþ´¦ÀíÆ÷ÖÐʵÏÖÕâÁ½²½¡£ private void HandleStrokesDeleting( object sender, InkOverlayStrokesDeletingEventArgs e) { SetUndoCheckpoint(); using(Strokes normalStrokes = NormalStrokes) { normalStrokes.Remove(e.StrokesToDelete); } using(Strokes scratchpadStrokes = ScratchpadStrokes) { scratchpadStrokes.Remove(e.StrokesToDelete); } } ÈçǰËùÊö£¬Íæ¼Ò»¹¿ÉÒÔʹÓòÁ³ýÊÖÊÆÇå³ýÒÔǰÔÚµ¥Ôª¸ñÖÐÊäÈëµÄÊý×Ö£¨Íæ¼Ò»¹¿ÉÒÔʹÓòÁ³ýÊÖÊÆÇå³ý±ãÇ©²¾Ä£Ê½Ï´´½¨µÄ×¢ÊÍ£©£¬Èçͼ 15 Ëùʾ¡£  ͼ 15. ²Á³ýÊý×Ö½«ÆäÇå³ý ÔÚÕý³£Ä£Ê½Ï£¬Õâ±íʾͨ¹ýÈ·¶¨²Á³ýÊÖÊÆËù²Á³ýµÄµ¥Ôª¸ñ£¬È»ºóÇå³ý¸Ãµ¥Ôª¸ñÀ´¶Ô Gesture ʼþ×ö³öÏìÓ¦¡£µ«ÊÇ£¬ÐèҪעÒâµÄÒ»¸öÖØÒªÊÂÇéÊÇ£¬Çå³ýµ¥Ôª¸ñµÄ²Ù×÷ÐèÒªÉèÖó·Ïû¼ì²éµã¡£¸Ã³·Ïû¼ì²éµã°üÀ¨µ±Ç°´æ´¢ÔÚ InkOverlay ÖеÄËùÓÐÊÖдÄÚÈÝ¡£´Ëʱ£¬ÊÖÊÆµÄ±Ê»ÈÔÔÚ¸²¸ÇͼÖУ¬Òò´Ë£¬ÔÚÇå³ýµ¥Ôª¸ñǰÎÒ½«Ã÷ȷɾ³ýÊÖÊÆµÄ±Ê»¡£ void HandleGestureInNormalMode( object sender, InkCollectorGestureEventArgs e) { switch (e.Gestures[0].Id) { case ApplicationGesture.Scratchout: Point cell = GetCellFromStroke(e.Strokes[0]); _inkOverlay.Ink.DeleteStrokes(e.Strokes); RecognizePreviousNormalStrokes(); if (CanClearCell(cell)) ClearStateCell(cell); break; default: e.Cancel = true; break; } Invalidate(); } ±ãÇ©²¾Ä£Ê½ÏµIJÁ³ýÊÖÊÆ´¦Àí¸ü¼ÓÈÝÒס£Ó¦ÓóÌÐò½«¼ìË÷ÈκÎÓë²Á³ýÊÖÊÆ±Ê»Ïཻ²æµÄ±ãÇ©²¾±Ê»£¬²¢½«Æäɾ³ý¡£ ʤÀû¶¯»
ËùÓÐÓÎÏ·¶¼»áÔÚÍæ¼Ò»ñʤ֮ºó£¬¶ÔÆäËùÈ¡µÃ³É¼¨¸øÓè¹ÄÀø¡£µ±È»£¬ÔÚÄúÌîÍê×îºóÒ»¸öµ¥Ôª¸ñ²¢µÃÒâÓÚÄúµÄÂß¼ÍÆÀíÄÜÁ¦Ê±£¬ÒѾÓÐÁ˼«´óµÄ×ÔÎҳɾ͸У¬µ«Èç¹û´Ë¿ÌÓÎÏ·ÄܽøÒ»²½¼ÓÉîÕâÖÖÃÀºÃ¸Ð¾õ£¬¸øÄúÒ»¸öÇ¡µ½ºÃ´¦µÄÔÞÉÍ£¬ÄÇôÄúµÄ¸Ð¾õ¿Ï¶¨»á¸üºÃ£¬±ÈÈç Microsoft µÄֽůÓÎÏ· Solitaire ¾ÍÊÇÕâô×öµÄ£¨²Î¼ûͼ 16£©¡£  ͼ 16. ÓÎÏ·»ñʤºóµÄ Solitaire ´°¿Ú ÎÒʵÏÖÁ˼¸¸ö´ýÑ¡ Sudoku »ñʤ¶¯»£¬¶øÇÒÎÒµÄ fianc??e Tamara£¨Sudoku ÃÔÃÇ£¬ÎÒ×îºÃµÄ beta °æ²âÊÔÕߣ©Ò²ÎªÎÒÌṩÁ˼¸¸ö²»´íµÄ½¨Ò飬×îºóÎÒ¾ö¶¨Ê¹ÓÃÕâÑùÒ»¸ö¶¯»£ºÐí¶àµ¥Ôª¸ñ |