ProConcepts Asynchronous Programming in ArcGIS Pro - EsriJapan/arcgis-pro-sdk GitHub Wiki
ArcGIS Pro ã¯éåæã¢ããªã±ãŒã·ã§ã³ã§ããã¢ãã€ã³ãäœæããéçºè ã¯ãéåæã§ã®å®è£ ãæºåããå¿ èŠããããŸãããã®ã³ã³ã»ãã ããã¥ã¡ã³ãã§ã¯ãã¢ãã€ã³ã§äœ¿çšã§ããæ§ã ãªéåæããã°ã©ãã³ã°ã®ææ³ã«ã€ããŠèª¬æããŸãããã®ã³ã³ã»ãã ããã¥ã¡ã³ã㯠ProConcepts Framework working with multithreading in arcgis pro ã®æ å ±ãè£å®ãããã®ã§ããéçºè ã¯ãæå ã«äžèšã³ã³ãã³ãã®å 容ãç解ããå¿ èŠããããŸãã
ãã ãããã®ã³ã³ã»ãã ããã¥ã¡ã³ãã¯ããã«ãã¹ã¬ãããŸãã¯éåæããã°ã©ãã³ã°ã®ã€ã³ãããã¯ã·ã§ã³ããã¥ãŒããªã¢ã«ã§ã¯ãããŸãããããã¯ãArcGIS Pro API ãšã¢ãã€ã³ã§ã®äœ¿çšã«çŠç¹ãåœãŠãŠããŸãã éåæããã°ã©ãã³ã° ããã³é¢é£ãããã¯ã«é¢ããäžè¬çãªæ å ±ã«ã€ããŠã¯ããªã³ã©ã€ã³ã§å ¥æå¯èœãªãã¥ãŒããªã¢ã«ãšå ¥éæžãèªãããšããå§ãããŸãã
Language: C#
Subject: Framework
Contributor: ArcGIS Pro SDK Team <[email protected]>
Organization: Esri, http://www.esri.com
Date: 10/06/2024
ArcGIS Pro: 3.4
Visual Studio: 2022
ãããã¯
- æŠèŠ
- ArcGIS Pro ã®ã¹ã¬ãã
- API ã®ç¹åŸŽ
- éåæã¡ãœããã®äœ¿çš
- åæã¡ãœãã (#calling-asynchronous-methods-within-a-queuedtask)
- ã«ã¹ã¿ã ã¡ãœããã®éçº
- BackgroundTask ã®äœ¿çš
ArcGIS Pro ã¯éåæã¢ããªã±ãŒã·ã§ã³ã§ãã ArcGIS Pro ã¯å€ãã®çç±ã§éåææ§ãå©çšããŸãããã¢ãã€ã³éçºè ã«ãšã£ãŠéåæ API ã®äž»ãªç®çã¯ãArcGIS Pro ã® UI ã®å¿çæ§ãç¶æããªããã¢ãã€ã³ã "äœæ¥" ãè¡ããããã«ããããšã§ããããã§ã® "å¿çæ§" ãšã¯ãããã¯ã°ã©ãŠã³ãã§åŠçãè¡ãããŠããéã« UI ãã³ã³ãã³ããæç»ãããæŽæ°ãããã§ããããšãæå³ããŸããäžæ¹ ArcMap ã§ã¯ãé·æéã®åŠçäžã« UI ã "ããªãŒãº" ããå¿çæ§ãäœäžããããšããããŸããéåæ API ã®äž»ãªç®çã¯ãã¢ãã€ã³éçºè ã«äžŠåæ§ã䞊è¡æ§* ãæäŸããããšã§ã¯ãããŸãããåŸè¿°ããããã«ãAPI ãä»ãã ArcGIS Pro ã®ãã©ã€ã㪠ã¯ãŒã«ãŒ ã¹ã¬ããã䜿çšãããšã(ã¢ãã€ã³ãŸã㯠Pro ã³ãŒãããã®) ãã¹ãŠã®éä¿¡ãããããã¯ã°ã©ãŠã³ãæäœããã¥ãŒã«å ¥ããŠãããããé 次å®è¡ãããããã«ããããšã§ãåæå®è¡ãé²ããŸããããã«ã€ããŠã¯ã以äžã®ã»ã¯ã·ã§ã³ã§è©³ãã説æããŸãã
*ããŒãžã§ã³ 2.6 㧠BackgroundTask ã䜿çšããããã¯ã°ã©ãŠã³ãã§ã®åæåŠçããµããŒãããŸããã
éåžžãArcGIS Pro ã®ã¢ãã€ã³éçºè ã¯ã2 ã€ã®ã¹ã¬ããã«ã®ã¿å¯Ÿå¿ããå¿ èŠããããŸããUI ã¹ã¬ãããšã"QueuedTask" ãä»ããŠã¢ã¯ã»ã¹ããããã¢ããªã±ãŒã·ã§ã³ãæäŸããç¹æ®ãªã¯ãŒã«ãŒ ã¹ã¬ããã® 2 ã€ã§ãããã ããããŒãžã§ã³ 2.6 ã§ã¯ãBackgroundTask ãšåŒã°ãããã¢ãã€ã³éçºè ã䜿çšããããã® 3 ã€ç®ã®ããã¯ã°ã©ãŠã³ã ã¹ã¬ãã (å®éã«ã¯ããã¯ã°ã©ãŠã³ã ã¹ã¬ãã ããŒã«) ãžã®ã¢ã¯ã»ã¹ãå°å ¥ãããŸãããQueuedTask ãšBackgroundTask ã¯ã©ã¡ãã Microsoft ã® .NET ã¿ã¹ã¯äžŠåã©ã€ãã©ãª TPL ãšãã¿ã¹ã¯éåæãã¿ãŒã³ TAP ãšåŒã°ããããã°ã©ãã³ã° ãã¿ãŒã³ã䜿çšããŸããQueuedTask ãš BackgroundTask 㯠System.Threading.Tasks.Task ã¯ã©ã¹ãã¢ãã«ã«ããŠããŸããã以äžã®ãããªããã€ãã®éèŠãªéãããããŸã:
- QueuedTask ãš BackgroundTask ã¯ãCOM ã³ã³ããŒãã³ãããã³ã¹ã¬ãã ã¢ãã£ããã£ãæã€ã³ã³ããŒãã³ããšã®äœ¿çšã«äºææ§ã®ãã ã·ã³ã°ã«-ã¹ã¬ãã ã¢ããŒãã¡ã³ã ã䜿çšããŠããŸã*ã
- QueuedTask ãš BackgroundTask ã¯ãMicrosoft ã® Task ã¹ã¬ãã ããŒã«ã§ã¯ãªããArcGIS Pro ã®ãããŒãžã ã¹ã¬ãã ããŒã«äžã§æäœãå®è¡ããŸãã
- QueuedTask ã®åŠçã¯ãè«ççãªç«¶åãé²ãããã«ãã¥ãŒã«å ¥ã FIFO ã·ãŒã±ã³ã¹ã§å®è¡ãããŸããããã«ãããåŒã³åºãã®é©åãªé åºãä¿èšŒããã競åã®ãªã¹ã¯ã軜æžããäžè²«ããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãç¶æã§ããŸããQueuedTask äžã®å¥ã ã®åŠçã¯äžŠåã«å®è¡ãããªããããMicrosoft ã® "System.Threading.Tasks.Task" (ããã³ ArcGIS Pro ã® BackgroundTask) ã䜿çšããå Žåãšæ¯èŒããŠãã¿ã¹ã¯äœæã®é£æ床ã軜æžãããŸãã
*BackgroundTask ã¯ãã¹ã¬ãã ã¢ãã£ããã£ãæã€ãªããžã§ã¯ãã§ã®äœ¿çšãæ³å®ããŠããŸãããã"TaskPriority.single" åã® TaskPriority ã䜿çšããŠã¹ã¬ãã ã¢ãã£ããã£ãæäŸã§ããŸãã
QueuedTask ã¯ã©ã¹ã¯ãã¢ãã€ã³éçºè ãã¢ãã€ã³ ã³ãŒããéåæã«å®è¡ããããã«äœ¿çšããäºå®äžã®äž»èŠãªã¡ã«ããºã ã§ããQueuedTask ã¯ããã¹ãŠã®åŠçã ArcGIS Pro ã¢ããªã±ãŒã·ã§ã³ã®ãã©ã€ã㪠ã¯ãŒã«ãŒã¹ã¬ããã§å®è¡ããŸããããã¯ãMCT (Main CIM Thread) ãšåŒã°ããããšããããŸãã
ArcGIS Pro ã¢ããªã±ãŒã·ã§ã³ã®ã¹ããŒãã®å€§éšåã¯ãCIM (Cartographic Information Model) ãšåŒã°ããã¢ãã«ã«ãã£ãŠç¶æãããŠããŸããCIM ã¯ãããããã¬ã€ã¢ãŠããã¬ã€ã€ãŒãã¹ã¿ã€ã«ãã·ã³ãã«ãªã©ã®ã³ã³ãã³ããæ§æãè¡šçŸãå«ããéããããããžã§ã¯ãã®å®å šãªç¶æ ãèšè¿°ããå€æ°ã®ã¢ãã«ã§æ§æãããŠããŸããCIM ãžã®ã¢ã¯ã»ã¹ã¯ MCT ã«ãã£ãŠå¶åŸ¡ãããMCT 㯠QueuedTask ãä»ããŠã¢ã¯ã»ã¹ãããŸããMCT ã¯ãCIM ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã®å€æŽã CIM ã¹ã¬ãã ããŒã«ã®ãã¹ãŠã®ã¹ã¬ããéã§åæãããããã«å¿ã㊠ArcGIS Pro ã® UI ãæŽæ°ãããããã«ããŸããUI ã®ã¢ããªã±ãŒã·ã§ã³ã®æå¹/ç¡å¹ç¶æ ã MCT ã¿ã¹ã¯ã®å®è¡äžã«èªåçã«å¶åŸ¡ããããã¥ãŒã®äœæãã¢ããªã±ãŒã·ã§ã³ã®ã·ã£ããããŠã³ãªã©ãã¢ããªã±ãŒã·ã§ã³ã®éèŠãªãã§ãŒãºã MCT ã§èª¿æŽãããŸãã
ã»ãšãã©ã®å Žåãã¢ãã€ã³éçºè ã¯ãã«ã¹ã¿ã ã¢ãã€ã³ ã³ãŒãã§éåææäœãå®è¡ããéã«ãQueuedTask ã æä»ç ã«äœ¿çšããŸãã
ArcGIS Pro ã®ã»ãšãã©ã® GIS æäœã¯ QueuedTask äžã§å®è¡ãããŸãããæäœãããã¯ã°ã©ãŠã³ã ã¹ã¬ããäžã§ãã³ã¢ãŒãã«ã«å®è¡ããããšãé©ããŠããå ŽåããããŸã (COM ã³ã³ããŒãã³ãã®äœ¿çšãšäºææ§ããããŸã)ããã®ãããªæäœã¯é·æéè¡ãããå¯èœæ§ããããã¢ãã€ã³éçºè ã¯ãArcGIS Pro ã® UI ãæå¹ã«ãããŸãŸããã®é QueuedTask ãææããããšãé¿ããããšèããŠããŸãããã®ãããããŒãžã§ã³ 2.6 ã§ã¯ãBackgroundTask ããããªã㯠API ã«è¿œå ãããŸãããBackgroundTask ã¯äžè¬çã«ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«ã¢ã¯ã»ã¹ããæäœã§ã¯äœ¿çšã§ããŸãããBackgroundTask ã®äœ¿çšæ¹æ³ãå¶éäºé ã«ã€ããŠã¯ãUsing BackgroundTask ã®ã»ã¯ã·ã§ã³ã§èª¬æããŠããŸãã
éçºè ã¯ãã¢ãã€ã³ã®äžã§ System.Threading.Tasks.Task ã䜿çšããããšã劚ããããŸãããã"System" ã¿ã¹ã¯ã¯ (ArcGIS Pro API ã®å€§éšåãæ¯ãã) COM ã³ã³ããŒãã³ããšã®äœ¿çšã«ã¯äºææ§ããªãããšã«æ³šæããå¿ èŠããããŸãã"System" ã¿ã¹ã¯ã§ã®äœ¿çšã«é©ããæäœã¯ãµãŒã ããŒãã£ã®ã³ãŒãã®ã¿ãå«ãæäœã§ãããããããç¬èªã® Web ãµãŒãã¹ãæ¬è³ªçã«éåæ㧠ArcGIS Pro ã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ã«ã¢ã¯ã»ã¹ããå¿ èŠã®ãªãä»ã®ãµãŒã ããŒãã£ã®ã¢ããªã±ãŒã·ã§ã³ ãšã³ã ãã€ã³ããšéä¿¡ããããã®ãã®ã§ãããã"System" ã¿ã¹ã¯ã§äœ¿çšãããæäœã¯ãæ¬è³ªçã«éã¢ãŒãã«ã§ããå¿ èŠããããã¢ããªã±ãŒã·ã§ã³ã®ããžãŒç¶æ 㧠ArcGIS Pro ã® UI ãç¡å¹ã«ããå¿ èŠã¯ãããŸãã (QueuedTask ã®å Žåãšåæ§)ãBackgroundTask ãå©çšå¯èœã§ããããšãèãããšãã¢ãã€ã³ã§ã® "System" ã¿ã¹ã¯ã®äœ¿çšã¯ãŸãã§ããããããã®äœ¿çšã¯ãã®ããã¥ã¡ã³ãã®ç¯å²å€ã§ãã
ãã§ã« ArcGIS Pro ã®ã¢ãã€ã³éçºã«åãçµãã ããšã®ããéçºè ã¯ãArcGIS Pro ã® API ã®å€§éšåãåæåã§ã¯ãªãéåæåã§ããããšã«æ°ã¥ããããšã§ããããéåæã¢ããªã±ãŒã·ã§ã³ãäž»ã«åæ API ãæã£ãŠããããšã¯çŽæçã«ç解ãã«ãããããããŸããããã¢ããªã±ãŒã·ã§ã³ã®ã¯ãŒã¯ãããŒã¯äžè¬çã« "é çªã«" å®è¡ãããã³ã³ããŒãã³ãã®ã¹ãããã§æ§æãããŠããŸã (äŸ: æ©èœéžæãå±æ§å€æŽãä¿å)ãã¯ãŒã¯ãããŒèªäœã¯ãéåžžãéåæçã«å®è¡ããããã®ã§ãåäžã®äœæ¥åäœãŸã㯠"æäœ" ãšããŠå®è¡ãããŸãããŸããAPI ã¯éåžžã«çŽ°ãããéåæã¡ãœããã®äœ¿çšã¯ããå°é£ã«ãªãå¯èœæ§ããããããåæ API ã«ãé©ããŠããŸãããããã£ãŠãArcGIS Pro API ã®ã¡ãœããã¯äž»ã« 2 ã€ã®ã«ããŽãªã«åé¡ãããŸã:
- ã©ã®ã¹ã¬ããããã§ãåŒã³åºãããšãã§ããå°æ°ã®éåæã¡ãœããã§ãããã®ã¿ã€ãã®ã¡ãœãã㯠Async ãšããæ¥å°Ÿèªã䜿ã£ãŠåœåãããéåžžã¯ã¿ã¹ã¯ãè¿ããŸã ("éåžž" ãšããã®ã¯ void ãè¿ãããšããããã)ãã»ãšãã©ã®å Žåãéåæã¡ãœããã®åæããŒãžã§ã³ãçšæãããŠããŸããéåæã¡ãœããã¯ç²ãç²åºŠã®ãã®ãå€ã "åç¬" ã§äœ¿çšã§ããããšããããããŸãã
- ArcGIS Pro ã®ã¯ãŒã«ãŒ ã¹ã¬ããã§ã®ã¿åŒã³åºãããã¹ãéåžžã«å€ãã®åæã¡ãœãã - 䞻㫠QueuedTask çµç±ã® MCTããã㯠API ã¡ãœããã®å€§éšåã§ããUI ã¹ã¬ãããã QueuedTask ãå¿
èŠãšããåæã¡ãœãããåŒã³åºãããšãããš
CalledOnWrongThreadExcetpion
ãçºçããŸãã
ãŸãã3 ã€ç®ãš 4 ã€ç®ã®ã«ããŽãªãŒããããŸã:
- GUI ã¹ã¬ããã§åŒã³ã ãå¿
èŠãããã¡ãœãã (éåžž ArcGIS Pro ã® UI èŠçŽ ãäœæãŸãã¯æŽæ°)ããŸãã¹ã¬ãã ã¢ãã£ããã£ã®ãªãåæã¡ãœãã (å€ãã®ãžãªã¡ã㪠ã¡ãœãããããã£ãã·ã¥ãããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã "ã¹ãããã·ã§ãã" ã䜿çšããã¡ãœããïŒ
map.GetLayersAsFlattenedList(), etc..
ïŒãªã©ããã€ãã®ã¡ãœãã (éåæãšåæã®äž¡æ¹) ããããŸãã泚èšïŒã¹ã¬ãã ã¢ãã£ããã£ã®ãªãã¡ãœãã㯠(UI ã QueuedTask ã«å ããŠ) BackgroundTask ã§ã®äœ¿çšã«é©ããŠããŸãã
ArcGIS Pro API ã®éåæã¡ãœããã¯ãUI ã¹ã¬ãã* ããå®å šã«åŒã³åºãããããã«èšèšãããŠããŸã (ãã ããQueuedTask ã BackgroundTask ãããåŒã³åºããŸã)ãéåæã¡ãœããã¯ãã¡ãœããåã« "Async" ãšãããµãã£ãã¯ã¹ãä»ããŠãããæ»ãå€ã®ã¿ã€ãã Task ã§ããããšã§èå¥ã§ããŸã (äŸ: PanToSelected AsyncãRedrawAsyncãSetViewingModeAsync)ãéåæã¡ãœããã¯æ§è³ªäžãç²ãç²åºŠã«ãªãåŸåããããéåžž "å®ååããã" ã¯ãŒã¯ãããŒãæäœãã«ãã»ã«åããŸããã»ãšãã©ã®å ŽåãAPI ã®éåæã¡ãœããã¯åæ察å¿ã®ãã®ãæã£ãŠããŸãããåè¿°ã®ããã« API ã¡ãœããã®å€§åã¯åæã®ã¿ã§ãã
ã¯ãŒã¯ãããŒã®äŸã䜿çšããŠãArcGIS Pro API ã®éåæã¡ãœããã®äœ¿ãæ¹ã説æããŸãããã以äžã®ã³ãŒãã§ã¯ãAPI ã䜿çšã㊠GP ããŒã« "SelectLayerByAttribute" ãå®è¡ãã"TerritorialStats_3" ã¬ã€ã€ãŒã§ "KentCC" ãšããååã®é¡ãéžæããŠããŸãã次ã«ãéžæããããã£ãŒãã£ã®ç¯å²ã«ãºãŒã ãå®è¡ããŸã (ãºãŒã ã€ã³ã®éã«ã¢ãã¡ãŒã·ã§ã³å¹æãåºãããã«æéé 延ãèšããŠããŸã)ãéåæã¡ãœãã㯠UI ããçŽæ¥åŒã³åºãããŠããŸã:
internal class SelectCountyAsyncButton : Button {
//select the county of Kent. Zoom to its extent
protected override void OnClick() {
//Create the GP parameters
var values = new string[] { "TerritorialStats_3", "NEW_SELECTION", "name = 'Kent CC'" };
//Execute the GP Tool
Geoprocessing.ExecuteToolAsync("SelectLayerByAttribute_management", values);
//Zoom to the result
MapView.Active.ZoomToSelectedAsync(new TimeSpan(0, 0, 3));
}
}
ãã®ã³ãŒããå®è¡ãããšãåäœã«ççŸãçããŸããé¡ã®ãã£ãŒãã£ãéžæãããŠããã«ãããããããºãŒã ã€ã³ãããArcGIS Pro ã®ç¯å²ãå€æŽãããªãå ŽåããããŸã (ãã£ãŒãã£éžæã®åŸã«ãéžæããããã£ãŒãã£ã®ç¯å²ãžãºãŒã ããŸã)ããŸããéžæããããã£ãŒãã£ãŒã«æåŸ éãã«æ¡å€§ãããã³ãŒããæ£ããåäœããå ŽåããããŸããã³ãŒããæåã«å®è¡ãããéã«ãåºç€ãšãªã GP ç°å¢ãåæåãããŠããããã«èŠãããšããã®ççŸãç¹ã«é¡èã«ãªããŸãã
åé¡ã¯ã"ExecuteToolAsync" ã®åŒã³åºããéåæã§å®è¡ãã (ååã瀺ãããã«) ãã£ãŒãã£ãéžæãããåã«ãåŒã³åºãå ã«ããã«å¶åŸ¡ãè¿ãããããšã§ããã¢ãã€ã³ã¯ã³ãŒãã®å®è¡ãç¶è¡ããã¯ãŒã¯ãããŒã® 2 çªç®ã®éåæã¡ãœããã§ãã "ZoomToSelectedAsync" ãåŒã³åºããŸãããã®ã¡ãœããã¯ãExecuteToolAsync ã®å éšå®è¡ç¹æ§ã«å¿ããŠããã£ãŒãã£éžæãå®è¡ãããåã«ãºãŒã ããžãã¯ãå®è¡ããå ŽåããããŸãããã®ããã(ã³ãŒãã«æžãããŠããããã«) ã¯ãŒã¯ãããŒã® æåŸ ããã åŠçé åºããæåã«éžæã次ã«ãºãŒã ã§ããã«ãããããããã¡ãœãããéåæã®ããããºãŒã ãæåã«å®è¡ãããéžæã 2 çªç®ã«å®è¡ãããå¯èœæ§ããããŸã (ãã®å Žå "ãºãŒã ã€ã³" ã¯ãããŸãã)ã
ç§ãã¡ãå¿ èŠãšããŠããã®ã¯ãéåæã¡ãœãããåŒã³åºãäžè²«ããæ¹æ³ã§ãããããããç¹°ãè¿ããäžè²«ããæ¹æ³ã§å®è¡ãããæå³ããé åºã§å®è¡ãããããšã§ããä»åã®ã±ãŒã¹ã§ã¯ããŸã GP ã® "éžæ" ããŒã«ãå®è¡ãããããå®äºããŠããã"éžæç¯å²ãžã®ãºãŒã " ãå®è¡ããŸããéåæã¡ãœãããäžè²«æ§ã®ããæ¹æ³ã§åŒã³åºãæ¹æ³ã«ã€ããŠã¯ã次ã®ã»ã¯ã·ã§ã³ã§èª¬æããŸãã
*API ã®ç¹åŸŽã®æŠèŠã§è¿°ã¹ãããã«ãã¹ã¬ãã ã¢ãã£ããã£ã®çç±ãã UI äžã§åŒã³åºãå¿ èŠãããéåæã¡ãœãããããã€ããããŸãããããã¯ãŸãã§ãã
èŠçŽ: GP ããŒã«ã®å®è¡ãšéžæç¯å²ãžã®ãºãŒã ã¡ãœãããæ£ããé åºã§å®è¡ãããããã«ããã«ã¯ãæåã®ã¡ãœãã (å®éã«ã¯è¿ããã Task) ãå®äºããã®ãåŸ ã£ãŠããã2 çªç®ã®ã¡ãœããã«é²ããªã©ã®æ¹æ³ãå¿ èŠã§ããããã«ãUI ããããã¯ããã "ããªãŒãº" ãããã«æåã®ã¡ãœãããå®äºããã®ãåŸ ã¡ããšã倧äºã§ãã.NET ã«ã¯ãã®ãããªç®çã®ããã«ã"async" ãš "await" ãšåŒã°ããæ©èœãæäŸããŠããŸãã
async 修食åã¯ãã¡ãœãããéåæãšããŠããŒã¯ããããã«äœ¿çšãããåãã¡ãœãããå éšæµã« await æŒç®åã䜿çšããããšã瀺ããŸããawait æŒç®åã¯ãåŒã³åºãåŽãéåæã¡ãœããã® "å®äº" (å ·äœçã«ã¯ãè¿ããã task ãå®äºãããŸã§) ã®éã non-blocking åŸ æ©ãããããšãã«ãåéåæã¡ãœããã« "è¿œå "ãããŸãã å "waited" ã³ãŒã«ã¯ãã³ããããã³ã°ã®ãããåŒã³åºãåŽã®ã¹ã¬ãã (éåžžã¯ã¢ãã€ã³ã® UI ã¹ã¬ãã) ã¯ãéåæã¡ãœããã®å®è¡äžãã¬ã¹ãã³ã¹ãç¶æã§ããŸãã
åŸè¿°ããããã«ãawait æŒç®åã¯éåæããã°ã©ãã³ã°ã®ããã®éåžžã«åŒ·åã§å€åœ©ãªæ§æèŠçŽ ã§ãããå å«ããã¡ãœããã® async 修食åãšåžžã«ãã¢ã«ãªã£ãŠããŸããasync ãš await ã䜿ã£ãäŸã以äžã«ç€ºããŸã:
internal class SelectCountyAsyncButton : Button {
//select the county of Kent. Zoom to its extent
//Note the addition of the 'async'
protected async override void OnClick() {/
//Create the GP parameters
var values = new string[] { "TerritorialStats_3", "NEW_SELECTION", "name = 'Kent CC'" };
//Execute the GP Tool - we await it's execution.
//UI remains responsive
await Geoprocessing.ExecuteToolAsync("SelectLayerByAttribute_management", values);
//Zoom to selection only when ExecuteToolAsync has completed
await MapView.Active.ZoomToSelectedAsync(new TimeSpan(0, 0, 3));
}
}
async ãš await (ã€ãŸã "ãã³ããããã³ã°" ãªåŸ æ©) ãè¿œå ããããšã§ãæ¹è¯ãããã³ãŒãã¯æåŸ éãã«å®è¡ãããããã«ãªããŸããããã£ãŒãã£éžæã¯æåã«å®äºããã¢ãã€ã³ã¯ãã³ããããã³ã°ã§åŸ æ©ããŸããArcGIS Pro ã® UI ã¯å¿çæ§ãç¶æããŠããŸã (ããªãŒãºããã "ãããã¯" ããããããŸãã)ããºãŒã 㯠2 çªç®ã«å®è¡ãããéžæãã£ãŒãã£ã®ç¯å²ã«äžè²«ããŠãºãŒã ããŸãã
éåæé¢æ°ã¯ãTask<result> ("'result' ã® Task" ãŸã㯠"'result' åã® Task" ãšèªã¿æ¿ããããŸã) ãšããæ»ãå€ã®åã䜿ã£ãŠãåŒã³åºãå ã«å€ãè¿ããŸã (result ã¯æ»ãå€ã®åãè¡šããŸã)ã æ»ãå€ã¯ãã¿ã¹ã¯ã® Task.Result ããããã£ã«æ ŒçŽãããŠããŸãã(泚èš: "Task" ã®ã¿ãè¿ãéåæé¢æ°ã®å Žåãè¿ãããçµæã¯ãªããTask.Result 㯠null ã§ã)ã
ã¿ã¹ã¯ãå®äºãããŸã§ã¯ãéåžž Task.Result ã«çŽæ¥ã¢ã¯ã»ã¹ããŠã¯ãããŸãããTask.Result 㯠ããããã³ã° ããããã£ã§ããã¿ã¹ã¯ãå®äºããåã«ã¢ã¯ã»ã¹ãããšãã¿ã¹ã¯ãå®äºããŠçµæã®å€ãå©çšå¯èœã«ãªããŸã§åŒã³åºãåŽããããã¯ãããŸãã幞ããªããšã«ãã¿ã¹ã¯ããã³ããããã³ã°ã§åŸ æ©ããããã«äœ¿çšããŠããããŒã¯ãŒã await ã«ã¯ãè¿ããã Task.Result ããããã£ããæ»ãå€ãæœåºããªããŠããæ»ãå€ãèªåçã«è§£æ±ºãããšããå©ç¹ããããŸãã
ããã¯ã以äžã®äŸã§ç€ºãããŠããŸãã
//Assume this async function returns an int representing some kind of count
//Note the return type of 'Task<int>'- "Task of 'int'"
public Task<int> GetCountOfFeaturesAsync(FeatureLayer featureLayer) { ... }
//We are calling 'GetCountOfFeaturesAsync' from the UI...perhaps via a button click
//Attempt 1 - add-in consumes the method to "get" the count...
//no await is being used
var count = GetCountOfFeaturesAsync(parcelLayer);
//This compiles but use of "var" masks the fact that our "count" local
//variable is actually set to Task<int> -not- int. The method is also not
//being awaited...most likely, the add-in developer forgot to add the await...
//Attempt 2 - using Task.Result directly
var task = GetCountOfFeaturesAsync(parcelLayer);
var count = task.Result;
//This approach works - but - should generally be avoided. Accessing
//Task.Result before the task has finished _blocks_ the UI
//Attempt 3 - using await
var count = await GetCountOfFeaturesAsync(parcelLayer);
//This is the preferred method. Await does a non-blocking wait _and_ resolves
//the Task.Result automatically. 'count' will contain the returned value when the
//task completes.
.NET ã¿ã¹ã¯ ã€ã³ãã©ã¹ãã©ã¯ã㣠ãçŽé¢ããåé¡ã® 1 ã€ã¯ãæªåŠçã®äŸå€ãåŒã³åºãå ã®ã¹ã¬ãã (é垞㯠UI) ã«æ»ãæ¹æ³ã§ããã¿ã¹ã¯ ã€ã³ãã©ã¹ãã©ã¯ãã£ã¯ãã®äŸå€ã AggregateException ã¯ã©ã¹ã§ã©ããããããšã§ã¹ã¬ããã«æ»ããŸããã¢ã¿ãããããåã¿ã¹ã¯ã芪ã¿ã¹ã¯ã«äŒæãããäŸå€ãçºçããå ŽåãAggregateExceptions ããã¹ãããããšãã§ããŸã (AggregateException ãç¬èªã®AggregateException ãªã©ã§ã©ããããŸã)ããã ããã»ãšãã©ã®ã¢ãã€ã³ã¯ "System" ã¿ã¹ã¯ã§ã¯ãªãããã¹ãŠã®åŠçã åäžã®ã¹ã¬ããã§å®è¡ãã ArcGIS Pro ã® "QueuedTask" ã䜿çšããŠãããããã¢ãã€ã³ã§ãã®ãããªã·ããªãªãçºçããå¯èœæ§ã¯éåžžã«äœããšèšããŸãã
AggregateException ã«ã©ãããããæªåŠçã®äŸå€ã¯ãAggregateException.InnerExceptions ããããã£ã«å«ãŸããŠããŸããAggregateException.InnerExceptions ãåæããããšã§ãçºçå ã®äŸå€ã«ã¢ã¯ã»ã¹ã§ããŸãããã ããåŒã³åºãåŽãå®è¡äžã®ã¿ã¹ã¯ã åŸ æ© ããŠããªãéã (ããã㯠Task.Wait ã䜿ã£ãŠããããã³ã°åŸ ã¡ãããŠããªãéã)ãã¿ã¹ã¯ããã® AggregateException ã¯ãã¿ã¹ã¯ãã¬ããŒãž ã³ã¬ã¯ã·ã§ã³ããããŸã§åŒã³åºãåŽã«ã¯ äŒãããŸãããããã«ãããæäœãå®äºããåŸ (äŸ: ãã¿ã³ã®ã¯ãªãã¯) 1ïœ2ç§åŸã« AggregateException ãçºçãããšãããäžèŠå¥åŠãªåäœãçºçããå¯èœæ§ããããŸãã
ã¿ã¹ã¯ã await ãããŠããå ŽåãæªåŠçã®äŸå€ã¯ãæäœã®å®è¡äžã«åŒã³åºãåŽã«äŒãããawait ã«ãã£ãŠèªåçã« AggregateException.InnerExceptions
ããããã£ããæœåºãããçºçããŸããã€ãŸããasync ãš await ã䜿çšããã¢ãã€ã³ ã³ãŒãã¯ãéåžžã® try/catch ã䜿çšããŠãéåæã¡ãœããããçºçããäŸå€ãéåžžã®æ¹æ³ã§åŠçã§ããŸãã
以äžã®ã³ãŒãäŸã§ã¯ãã¢ãã€ã³ã®éåæã¡ãœããã«ããæ§ã ãªã·ããªãªãšé¢é£ããäŸå€åäœã瀺ããŠããŸãã
//We are calling 'DoWorkAsync' from the UI thread via a button click
//Scenario 1. The Task is not awaited...the "catch" will never catch an exception.
//Not recommended
try {
DoWorkAsync(...);
}
catch(Exception ex) {
//This code will never be hit
...
}
//Because we are not awaiting the asynchronous method, the add-in code will
//exit the scope of the try/catch the moment after DoWorkAsync() is called. Any
//exception thrown within DoWorkAsync will be propagated _after_ the returned task
//is garbage collected (which could be at a much later point in time depending on
//how long DoWorkAsync runs before failing ...)
...
//Scenario 2. use Task.Wait. Not recommended as it is a blocking wait
try {
DoWorkAsync(...).Wait();//ditto for DoWorkAsync().Result
}
catch(AggregateException ae) {
//access unhandled exception(s) via the InnerExceptions property...
foreach(var ex in ae.InnerExceptions) {
...
//Because we are waiting for the task to complete, the add-in remains
//within the scope of the try/catch. We can catch AggregateException and
//enumerate 'InnerExceptions' to retrieve the thrown exception.
//However, we are blocking the UI
...
//Scenario 3. Await the Task. This is the recommended approach.
try {
await DoWorkAsync(...);//non-blocking wait
}
catch(InvalidOperationException ioe) {
//await "unwraps" the unhandled exception which we can catch
//directly
...
//Because we are awaiting the task, we remain within the scope
//of the try/catch while DoWorkAsync() is executing and can catch
//the relevant exception type(s). Notice we do not need to
//handle 'AggregateException'...
ããã°ã¬ããµãšãã£ã³ã»ã©ãã« ããã°ã¬ããµã®äœ¿çšã«ã€ããŠã¯ãProConcepts-Framework, Progress and Cancellation ã§è©³ãã説æããŠããŸãããã®ã»ã¯ã·ã§ã³ã§ã¯ã䞻㫠CancellationTokenSource ãš CancellationToken ã®äœ¿çšã«ã€ããŠèª¬æãããã®è³æãè£å®ããŸãã
泚èš: Progressor ãã©ã¡ãŒã¿ãŒãå¿
èŠãšããéåæã¡ãœããã§é²æç¶æ³ã衚瀺ããªãããŸãã¯ãã£ã³ã»ã«ããªãå Žåã®ããã©ã«ãã¯ãçµã¿èŸŒã¿ã®éç㪠Progressor.None(ãŸã㯠CancelableProgressor.None) ã䜿çšããŸãããŸããããã°ã¬ããµ ãã€ã¢ãã°ã Visual Studio ãããã¬ãŒã® UI ã¹ã¬ããã«å¹²æžããã®ãé²ãããã« (ãã®éãåæ§ã«)ããããã¬ãŒã§å®è¡ããŠããéã¯ããã°ã¬ããµã®è¡šç€ºãæå¶ãããŸããããã°ã¬ããµã®è¡šç€ºã¯ãProgressorSource ã³ã³ã¹ãã©ã¯ã¿ ããªãŒããŒããŒãã delayedShow
ãã©ã¡ãŒã¿ãŒãæ±ãããšã§å¶åŸ¡ã§ããŸãã"delayedShow = true" ãèšå®ãããšãã¿ã¹ã¯ãæ©ãå®äºããå Žåã«ãã€ã¢ãã°ã®è¡šç€ºãé
ãããããšãã§ããŸãã"delayedShow = false" (ããã©ã«ã) ãèšå®ãããšãé
延ããªãããã€ã¢ãã°ãããã«è¡šç€ºãããŸãã
CancellationTokenSource ãš CancellationToken
.NET ã® TAP API ã§ã¯ãCancellationTokenSource and a CancellationToken ã䜿ã£ããéèŠèŠçãªçµã¿èŸŒã¿ã®ãã£ã³ã»ã« ã¡ã«ããºã ãæäŸããŸããCancellationTokenSource ã¯ããã£ã³ã»ã«ã® "ããªã¬ãŒ" ãŸãã¯ã€ãã·ãšãŒã¿ãŒãæäŸããäžæ¹ãCancellationToken ã¯ããã£ã³ã»ã«å¯èœãªã¡ãœããããã£ã³ã»ã«ããããã©ããã "ç£èŠ" ããããã®ãªã¹ããŒãæäŸããŸããCancellationTokenSource ã«ã¯ãCancellationTokenSource.Token
ããããã£ã«é¢é£ãã CancellationToken ãå«ãŸããŠããŸãã
CancellationTokenSource ã䜿çšããã«ã¯ãã¢ãã€ã³ã¯ CancellationTokenSource ãçŽæ¥ã€ã³ã¹ã¿ã³ã¹åããéåžžã¯ã¯ã©ã¹ ã¬ãã«ã®ã€ã³ã¹ã¿ã³ã¹å€æ°ãšããŠä¿æããããCancellationTokenSource ããããã£ã "çµã¿èŸŒã¿" ãããŠãã CancelableProgressorSource ãã€ã³ã¹ã¿ã³ã¹åããŸããã¢ãã€ã³ã¯ãã¢ãã€ã³ã¯ãCancellationTokenSource.Token ãæ¶è²»ããä»»æã®éåæã¡ãœããã«æž¡ãããšãã§ããŸã*(ãããŠã¢ãã€ã³ããã£ã³ã»ã«ãããå ŽåããããŸã)ãã¢ãã€ã³ã¯ãCancellationTokenSource.Cancel()
ãåŒã³åºããŠãœãŒã¹ããã£ã³ã»ã«ããŸããããã«ãããCancellationToken.IsCancellationRequested ããããã£ã true ã«èšå®ããããªã¹ã㌠(ãã®å Žåã¯å®è¡äžã®éåææäœ) ããã£ã³ã»ã«ããããã©ãããå€æã§ããããã«ãªããŸãã
CancellationToken ã®ãã£ã³ã»ã« ã¹ããŒã¿ã¹ãå®æçã«ãã§ãã¯ããã®ã¯ãéåæãªãã¬ãŒã·ã§ã³ã®è²¬ä»»ã§ãããã£ã³ã»ã«ããããªãã¬ãŒã·ã§ã³ã¯ãåã«çµäºããããããäžè¬çã«ã¯ãå¿ èŠãªã¯ãªãŒã³ã¢ãããå®è¡ã㊠OperationCancelledException ãã¹ããŒããããéžæã§ããŸããã¢ãã€ã³ã¯ããããã£ããããŠåŠçããæºåãå¿ èŠã§ãã
ã¢ãã€ã³ã¯ãç¹ã«ãã£ã³ã»ã«ãããŠããå Žåã¯ã䜿çšåŸã« CancellationTokenSource ãç Žæ£ããå¿ èŠããããŸãã ãã£ã³ã»ã«ããããšãCancellationTokenSource ã "ãã£ã³ã»ã«è§£é€" ããŠåå©çšã§ããŸãããäžè¬çãªãã¿ãŒã³ã¯æ¬¡ã®ããã«ãªããŸã:
//class level variable - perhaps in the module or view model of a dockpane
private CancellationTokenSource _cts = null;
//A cancel command bound to a "Cancel" button on a dialog or dockpane
public ICommand CancelCmd {
get {
if (_cancelCmd == null) {
_cancelCmd = new RelayCommand(() => {
//Request cancellation.
_cts?.Cancel();
}, () => _cts != null, true, false);
}
return _cancelCmd;
...
//Runs the cancellable operation - called directly from a 'Start'
//button or similar...
private async void DoWorkAsync() {
_cts = new CancellationTokenSource();
try {
await RunCancelableProgressAsync(_cts.Token);
}
catch (OperationCanceledException e) {
//we were cancelled - take appropriate action
}
finally {
_cts = null;//Dispose of any old source
}
}
//The Cancellable operation elsewhere in the add-in...
//Consumes a CancellationToken
public Task RunCancelableProgressAsync(CancellationToken token) {
return QueuedTask.Run(()=> {
...
while(stillWorking) {
//Doing work
...
//Check cancellation
if (token.IsCancellationRequested) {
//we are cancelled - so we can either simply exit....
stillWorking = false;//or "break", etc.
...
//...or... we can terminate throwing an
//OperationCanceledException
token.ThrowIfCancellationRequested();
}
}
}
}
*泚èš: CancelableProgressorSource ãæ¶è²»ãããã£ã³ã»ã«å¯èœãªéåæé¢æ°ã¯ãåžžã«ããã°ã¬ããµãã CancellationTokenSource ãååŸããŠãCancellationToken ãååŸã§ããŸãã
ArcGIS Pro API ã®ã¡ãœããã®å€§éšåã¯åæåã§ãåæåã¡ãœããã®ã»ãšãã©ãã¹ãŠã¯ãMCT ãšåŒã°ããç¹å¥ãª ArcGIG Pro ã®ã¯ãŒã«ãŒ ã¹ã¬ããäžã§å®è¡ããå¿ èŠããããŸããArcGIS Pro ã¯ãŒã«ãŒ ã¹ã¬ãããå¿ èŠãšããåæã¡ãœããã¯ãIntellisense ããã³ API ãªãã¡ã¬ã³ã¹ã§ã"This method must be called on the MCT. Use QueuedTask.Run (ãã®ã¡ãœãã㯠MCT ã§åŒã³åºãå¿ èŠããããŸããQueuedTask.Run ã䜿çšããŠãã ãã)" ãšèšèŒãããŠããŸãã
ããã¯ãQueuedTask.Run ã®ã³ã³ããã¹ãå ã§ã¡ãœããã (æäœã®äžã§) å®è¡ããå¿ èŠãããããšãéçºè ã«éç¥ããŠããŸããQueuedTask.Run (ããã³ BackgroundTask.Run) ã¯ãåŒã³åºãããéã«åŒã³åºãå ã«ã¿ã¹ã¯ãè¿ããå®äºãããŸã§åŸ æ©ã§ããŸãã
ã¢ãã€ã³éçºè
ã ArcGIS Pro API ãåããŠäœ¿çšããéã«é¥ãããããã¹ã¯ãåæã¡ãœããã QueuedTask.Run ã§ã©ããããã«ãã¢ãã€ã³ ããžãã¯å
ã§çŽæ¥åŒã³åºãããšã§ããããã«ãããMCT ãå¿
èŠãšããæåã® API ã¡ãœãããåŒã³åºããããšãã«ãArcGIS.Core.CalledOnWrongThreadException
äŸå€* ãçºçããŸãããã®ãšã©ãŒã¯ãé©åãªã¡ãœãããQueuedTask.Run ã§ã©ããããããšã§ç°¡åã«ä¿®æ£ã§ããŸãã次ã®ã»ã¯ã·ã§ã³ã§èª¬æããŠããŸãã
* CalledOnWrongThreadException ãæ¬çªç°å¢ã§çºçããããšã¯æå³ããŠããŸããããã®äŸå€ã¯ãã¡ãœããã®åŒã³åºãã«ééã£ãã¹ã¬ããã䜿çšããŠããããšãéçºè ã«ç¥ãããããã®ãããã°è£å©ãšããŠæäŸãããŠããŸãããã®ç¶æ ã¯ãéåžžã®éçºéçšã§ä¿®æ£ãããããšãæ³å®ããŠããŸãã
QueuedTask.Runã䜿çšããŠåæã¡ãœãããåŒã³åºãããã®æãäžè¬çãªæ§æã¯ã"ç¡å" é¢æ°ãããªã²ãŒãããŸã㯠"ã©ã ã" ãšåŒã°ãããã®ã䜿çšããããšã§ããå€ãè¿ããªãã©ã ã㯠'Action' ãšããŠå®çŸ©ãããå€ãè¿ãã©ã ã㯠'Func' ãšããŠå®çŸ©ãããŸããQueuedTask.Run (ããã³ BackgroundTask.Run) ã¯äž¡æ¹ã®ã¿ã€ããåãå ¥ããŸããã€ãŸããã¢ãã€ã³éçºè ã¯ãå¿ èŠã«å¿ããŠãã©ã ãããåŒã³åºãå ã«å€ãè¿ããã©ãããéžæã§ããŸã*ãã©ã ãã¯ãåŒæ°ãåãå ¥ããããã«ãã©ã¡ãŒã¿ãŒåã§ããŸãããããã¯äžè¬çã§ã¯ãããŸããã
QueuedTask* ã§ã©ã ãã䜿çšããããã®äžè¬çãªæ§æã¯æ¬¡ã®ãšããã§ã (æ瀺çã«ã¯èšåãããŠããŸãããã"äžè¬çãª" æ§æã¯ãBackgroundTask.Run ã§äœ¿çšããå Žåãã»ãŒåãã§ã):
Task t = QueuedTask.Run(() => {
//code goes here
...
});
//Though this is the more common syntax - rather than assigning the returned
//task into a local variable we simply await it - same as we did with asynchronous
//methods in the preceding section
await QueuedTask.Run(() => {
//code goes here
...
});
ã©ã ãã¯ãQueuedTask.Run ã®ã¹ã³ãŒãå
ã«é
眮ããã () => {...}
æ§æã«ãã£ãŠå®çŸ©ãããŸãããã©ã¡ãŒã¿ãŒãããå Žåã¯ãã©ã ãã® "()" æ¬åŒ§ã«æ¬¡ã®ããã«è¿œå ãããŸã: (x_val, y_val) => {...}
ããã©ã¡ãŒã¿ã«åæå®ã¯å¿
èŠãªãããšã«æ³šæããŠãã ãããåæå®ã¯ãã©ã¡ãŒã¿ãŒã® type ããæšæž¬ãããŸããéåæã¡ãœããã§èŠãŠããããã«ãè¿ãããã¿ã¹ã¯ãåŸ
ã€ããšãã§ããŸãã
*ã¢ãã€ã³éçºè ã¯ãå¿ ãããã¢ãã€ã³ãã©ã ãã« "ããã±ãŒãžå" ããå¿ èŠã¯ãããŸãããå¿åããªã²ãŒãã®ä»£ããã«ã«ã¹ã¿ã ã¡ãœããã䜿çšããããšãã§ããŸããã©ã ãã䟿å©ãªã®ã¯ã"ã¯ãŒã«ãŒ ã³ãŒã" ãå¥ã®ã¡ãœããã« "æ ŒçŽ" ããã®ã§ã¯ãªããåãé¢æ°å ã«ç¶æã§ããããã§ãã
以äžã®ã¯ãŒã¯ãããŒäŸã䜿çšããŠãQueuedTask.Run ã®äœ¿çšæ¹æ³ã説æããŸããã¢ãã€ã³éçºè ãã"SelectFeaturesButton" ãã¯ãªãã¯ããããã³ã«ãçŸåšã®ãããç¯å²å ã®éžæå¯èœãªãã£ãŒãã£ããã¹ãŠéžæããããã®ã以äžã®ã³ãŒããå®è£ ããŠããããšãåæãšããŠããŸãã
///<summary>Select all visible and selectable features within the "middle" of
///the current view extent</summary>
internal class SelectFeaturesButton : Button {
protected override void OnClick() {
Envelope selExtent = null;
if (MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.Map) {
var extent = MapView.Active.Extent;
selExtent = extent.Expand(-0.25, -0.25, true);
MapView.Active.SelectFeatures(selExtent);
}
else if (MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneGlobal ||
MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneLocal) {
//get the extent in pixels
var sz = MapView.Active.GetViewSize();
var builder = new EnvelopeBuilderEx() {
XMin = 0,
YMin = sz.Height,
XMax = sz.Width,
YMax = 0
};
selExtent = builder.ToGeometry().Expand(-0.25, -0.25, true);
MapView.Active.SelectFeatures(selExtent);
}
}
}
ã¢ãã€ã³éçºè ãã³ãŒãã®åæãããã°ãè¡ããšãããã« CalledOnWrongThreadException äŸå€ãçºçããŸã:
ã³ãŒãã確èªãããšãéçºè ãã¢ãã€ã³ã®ã³ãŒãã QueuedTask ã§ã©ããããã®ãå¿ããŠããããšãããããŸãããã®ãããã¢ãã€ã³ã¯ (誀ã£ãŠ) UI ã¹ã¬ããäžã§ ArcGIS Pro API ã®ã¡ãœããããã¹ãŠå®è¡ããããšããŠããŸãããããäŸå€çºçã®åå ãšãªã£ãŠããŸããMapView.Active.SelectFeaturesã¯ãMCT ã䜿çšããå¿ èŠããããQueuedTask.Run å ã§åŒã³åºãå¿ èŠããããŸããUI ã¹ã¬ããäžã§å®è¡ãããšãCalledOnWrongThreadException ãçºçããŸãã
ãã®åé¡ã解決ããããã«ãã¢ãã€ã³éçºè ã¯ãQueuedTask.Run ãå¿ èŠãšãããã¹ãŠã®ã¡ãœããã« QueuedTask.Run ã䜿çšããããã«ã³ãŒããå€æŽããããããã®åŒã³åºããç¬èªã®ã©ã ãã§ã©ããããŸã:
///<summary>Select all visible and selectable features within the "middle" of
///the current view extent</summary>
internal class SelectFeaturesButton : Button {
//Notice addition of 'async' and 'await'...
protected async override void OnClick() {
Envelope selExtent = null;
if (MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.Map) {
var extent = MapView.Active.Extent;
selExtent = extent.Expand(-0.25, -0.25, true);
//SelectFeatures must be run on the MCT.
await QueuedTask.Run(() => {
MapView.Active.SelectFeatures(selExtent);
});
}
else if (MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneGlobal ||
MapView.Active.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneLocal) {
//get the extent in pixels
var sz = MapView.Active.GetViewSize();
var builder = new EnvelopeBuilderEx() {
XMin = 0,
YMin = sz.Height,
XMax = sz.Width,
YMax = 0
};
selExtent = builder.ToGeometry().Expand(-0.25, -0.25, true);
//SelectFeatures must be run on the MCT.
await QueuedTask.Run(() => {
MapView.Active.SelectFeatures(selExtent);
});
}
}
}
"æè¡çã«ã¯"ãäžèšã®äŸã«é¢ããŠãéçºè 㯠QueuedTask.Run ã䜿çšã㊠API ã®èŠä»¶ãæºããããããå¿ èŠãšããé¢é£æäœã MCT ã«éä¿¡ããŸãããã ãããã®å®è£ ã¯æé©ã§ã¯ãããŸãããAPI ã¯ããããã®ã¡ãœãããå®éã« MCT ã®äœ¿çšã "å¿ èŠ" ã ãšè¿°ã¹ãŠããŸãããåã¡ãœãããåå¥ã« (ã€ãŸãåå¥ã®ã©ã ãå ã§) MCT ã®äœ¿çšãå¿ èŠãšããããšãæåéãæå³ããããã§ã¯ãããŸããã
代ããã«ãAPI ã¡ãœããã®ã·ãŒã±ã³ã¹ã (å¯èœãªéã) åäžã®ã©ã ãã«çµ±åããå¿ èŠããããŸã:
//Avoid this...
await QueuedTask.Run(() => {
method1(...);
});
await QueuedTask.Run(() => {
method2(...);
});
await QueuedTask.Run(() => {
method3(...);
});
//Prefer this...
await QueuedTask.Run(() => {
method1(...);
method2(...);
method3(...);
});
ãŸããQueuedTask.Run ãæ§ç¯ããéã«ããããªãªãŒããŒããããçºçããŸãããã«ãŒãå 㧠QueuedTask ãåŒã³åºãããŠããå Žåã«ã¯ããã®ãªãŒããŒãããã倧ãããªãå¯èœæ§ããããŸããããããã³ãŒãã åäžã®ã©ã ãã«ãŸãšããããšãæšå¥šããçç±ã§ã:
//Avoid this...
foreach(var some_item in ...) {
await QueuedTask.Run(() => { //QTR inside the loop
//do work
});
}
//Prefer this...
await QueuedTask.Run(() => { //QTR outside the loop
foreach(var some_item in ...) {
//do work
}
});
ããäžã€ã®ä¿®æ£ç¹ã¯ãã©ã ããèµ·åãã å ã«ã¢ã¯ãã£ããªãã¥ãŒã®ç¶æ ããã£ããã£ããããšã§ã:
var mv = MapView.Active;//capture the active view state
//use "mv"...
èŠãç®ã«ã¯ããããããã§ãããMapView.Active
ã䜿çšãããšãã¢ããªã±ãŒã·ã§ã³å
ã§äŸå€ãçºçããããšããããŸãããããããéžæããžãã¯ãåŒã³åºããããšãã«ã以åã«ãã¥ãŒã«å
¥ããããæäœã MCT ã§å®è¡ãããŠããå¯èœæ§ããããæäœã®å®è¡ãéå§ããåã«ããããå®äºããå¿
èŠããããŸãã
ããã¯ãšããž ã±ãŒã¹ã§ãããã¢ãã€ã³ ã³ãŒããåŒã³åºãããŠãããQueuedTask.Run ãå®éã«å®è¡ããããŸã§ã®éã«ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ãå€åããå¯èœæ§ããããŸããäŸãã°ãã©ã ããå®è¡ãããåã«ãã¢ã¯ãã£ããªãã¥ãŒãããŒãã«ã«å€æŽããããããã¥ãŒ ãã€ã³ãéãããããããå ŽåããããŸãã ãã®å ŽåãMapView.Active
ã null ã«ãªããäŸå€ãçºçããå¯èœæ§ããããŸããæè¡çã«ã¯ å¿
é ã§ã¯ãããŸããããããŒã«ã«å€æ°ã䜿çšããŠã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã®ã¹ãããã·ã§ããããã£ããã£ããããšã«ãããã¢ãã€ã³ ã³ãŒããå
ç¢ã«ã§ããŸããã©ã ããåŒã³åºãããåã«ããŒã«ã«å€æ°ã«ãã£ããã£ãããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã¯ãããªãã®äžããå€æŽãããããšã¯ãããŸããã
ããã¯ã³ãŒãã®äŸã§ãããåäžã®ã©ã ãã«çµ±åãããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãããŒã«ã«å€æ°ã«åã蟌ãŸããŠããŸã:
///<summary>Select all visible and selectable features within the "middle" of
///the current view extent</summary>
internal class Button1 : Button {
//Notice addition of 'async' and 'await'...
protected async override void OnClick() {
Envelope selExtent = null;
var mv = MapView.Active;//capture the Mapview state...add null check if needed
//Consolidate the code into a single QueuedTask.Run.
await QueuedTask.Run(() => {
if (mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.Map) {
var extent = mv.Extent;
selExtent = extent.Expand(-0.25, -0.25, true);
mv.SelectFeatures(selExtent);
}
else if (mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneGlobal ||
mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneLocal) {
//get the extent in pixels
var sz = mv.GetViewSize();
var builder = new EnvelopeBuilderEx() {
XMin = 0,
YMin = sz.Height,
XMax = sz.Width,
YMax = 0
};
selExtent = builder.ToGeometry().Expand(-0.25, -0.25, true);
mv.SelectFeatures(selExtent);
}
});//end of the QueuedTask.Run lambda
}
}
泚èš: ãã®äŸã§ã¯ QueuedTask.Run ã®åŸã«ã³ãŒãããªãããã"await" ã¯å³å¯ã«ã¯å¿ èŠãããŸããããŸããOnClick 㯠void ãè¿ããŸããã"async" ãšè¡šç€ºã§ããŸããvoid ãšããŒã¯ãããéåæã¡ãœããã¯ãUI ã«çŽæ¥é¢é£ä»ããããã€ãã³ããã¡ãœããã«é¢é£ä»ããããŠãããéåžžã«ããèŠãããŸã (ä»åã®ã±ãŒã¹ã®ããã«)ã
ã©ã ãå ã®äŸå€ãã³ãã©ãŒã¯ç¹å¥ãªåŠçãå¿ èŠãšããŸãããã©ã ãå ã§ãåŸ æ©ããŠããªãéåæã¡ãœãããå®è¡ãããŠããªãéããå¿ èŠã«å¿ã㊠try/catch/finally ã®éåžžã®ãã¿ãŒã³ã䜿çšã§ããŸããã©ã ãã®å€ã§äŸå€ããã£ããããã«ã¯ãåè¿°ã®éåæã¡ãœããããã®äŸå€åŠçãšåæ§ã«ãtry/catch ãããã¯å ã§åŸ ã¡åããå¿ èŠããããŸããã©ã ãã®å€ã§äŸå€ããã£ããããã«ã¯ãQueuedTask.Run ãŸã㯠BackgroundTask.Run ããåè¿°ã®éåæã¡ãœããããã®äŸå€åŠçãšåæ§ã«ãtry/catch ãããã¯å ã§åŸ ã¡åããå¿ èŠããããŸãã
await QueuedTask.Run(()=> {
//"normal" try/catch inside the lambda
try {
...
}
catch(InvalidOperationExcepion ioe) { ... }
});
try {
//QTR is awaited!...
await QueuedTask.Run(()=> ....);
}
catch(ArgumentNullException ane) {
... etc ...
QueuedTask.Run (ããã³ BackgroundTask.Run) ã¯ãæ»ãå€ã®ããã©ã ããšãªãã©ã ã (ãããã "Func" ãš "Action" ã®ããªã²ãŒã) ã®ãªãŒããŒããŒããæäŸããŸããã©ã ãããå€ãè¿ãã«ã¯ãã¢ãã€ã³ã®éçºè ã¯ãå¿ èŠãª "return" ã¹ããŒãã¡ã³ããã©ã ãå ã®å¿ èŠãªå Žæã«è¿œå ããã ãã§ããæ»ãå€ã¯ãéåæé¢æ°ããã®æ»ãå€ ã®ã»ã¯ã·ã§ã³ã§èª¬æããããã«ãTask.Result ããããã£ã«æ ŒçŽãããŠãããawait ã¹ããŒãã¡ã³ãã«ãã£ãŠ "ã¢ã³ã©ãã" ã§ããŸããTask.Result ããè¿ãããå€ãå€æ°ã«ä»£å ¥ããå Žåãã¢ãã€ã³éçºè 㯠await æŒç®åã䜿çšããå¿ èŠããããŸãã
//no return value. QueuedTask is being awaited
await QueuedTask.Run(() => {
//do work
});
//the value in item_count is returned in the Task.Result.
//It is assigned into the local variable 'result' when
//the task completes
var result = await QueuedTask.Run(() => {
//do work - return a value
return item_count;
});
//Be aware of this, notice - no await
//'result' will be assigned a value of type Task<int> and _not_
//int which may not be what the add-in developer was expecting...
var result = QueuedTask.Run(() => {
//do work - return a value
return item_count;
});
//to get the result the task still needs to be awaited...
var count = await result;
ã©ã ãã§å®è£ ãããŠããã¯ãŒã¯ãããŒã®ç¹æ§ã«ãã£ãŠã¯ãã©ã ãå ã§éåæã¡ãœãããåŒã³åºãå¿ èŠãããå ŽåããããŸããUI ã¹ã¬ããããéåæã¡ãœãããåŒã³åºãå Žåãšåãã«ãŒã«ãé©çšãããŸããã€ãŸããã¡ãœãããå®äºãããŸã§ãã³ããããã³ã°ã®åŸ æ©ã®å®è¡ãç®çã®å Žåã¯ãéåæã¡ãœãããåŸ æ©ããå¿ èŠããããŸããawait ã䜿çšããã«ã¯ãQueuedTask.Run ã®ã©ã ã* ã« 'async' 修食åãä»ããå¿ èŠããããŸã:
//execute a QueuedTask that contains an asynchronous method
//note the 'async' modifier on the lambda
await QueuedTask.Run( async () => {
...
//async method within the lambda is awaited...
var result = await FooAsync(...);
...
});
*ãã¹ãããã QueuedTask ã¯ã"芪" ã® QueuedTask ã®äžã§ãããããããããã éåžžã®åæé¢æ° ã§ãããã®ããã«åäœã (ãã¹ãããã QueuedTask ã®) asyncã await ã¯å®éã«ã¯å¿ èŠãããŸãã (ãã¹ãŠã® QueuedTask 㯠MCT äžã§ 1 ã€ãã€ãé çªã«å®è¡ãããŸãããã¹ããã QeuedTask ãåæ§ã§ã)ããã ãããã¹ããããéåæã¡ãœããã QueuedTask ã䜿çšããŠãããšç¢ºä¿¡ã§ããªãéãã"async" ãš "await" ãç¹°ãè¿ã 'recipe-style' ã§äœ¿çšããããšã«ãã€ãã¹é¢ã¯ãããŸããã
ã¢ãã€ã³éçºè ã¯ãã¯ã©ã¹ãã¡ãœãããç¹ã«åå©çšå¯èœãªã¯ãŒã¯ãããŒãã©ãããããŠãŒãã£ãªãã£ããã«ããŒé¢æ°ã®ç¬èªã®ã©ã€ãã©ãªãäœæãããšäŸ¿å©ã§ããã¢ãã€ã³éçºè ã«æšå¥šããããã¿ãŒã³ã¯ãAPI ãšåãèŠåã«åŸãããšã§ããã€ãŸããäž»ã«åæã¡ãœãããèšè¿°ããå¿ èŠã«å¿ããŠéåæã®ä»£æ¿ã¡ãœãããæäŸããŸãã
以äžã®èª¬æã§ã¯ãåã®ã»ã¯ã·ã§ã³ã§èª¬æãããéžæãšãºãŒã ã®ã¯ãŒã¯ãããŒããåæãšéåæã¡ãœããã« "å€æ" ããããšãæ³å®ããŠããŸãã
ã¢ãã€ã³ã®ã¡ãœãããåæåã§ããããã«ã¯ãçæ³çã«ã¯éåæåã®ã³ãŒã* ãå«ãŸããTask åãè¿ãããšã¯ã§ããŸããã
ããã¯ãåæã¡ãœããã«çµ±åãããæåã®ã¯ãŒã¯ãããŒã§ããããã«ã¯ 3 ã€ã®ãã©ã¡ãŒã¿ãŒããããŸãã1 ã€ã¯ MapView åã§ãä»ã® 2 ã€ã¯éžæç¯å²ã®æ¡åŒµã«ã€ããŠã§ãããã®ã¡ãœããã¯ãéžæçµæãåŒã³åºãå ã«è¿ããŸã:
//Synchronous utility method
public SelectionSet SelectFeaturesWithViewExtent(
MapView mv, double ratio, bool asRatio) {
Envelope selExtent = null;
//null check
if (mv == null) throw new ArgumentNullException(nameof(mv));
if (mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.Map) {
selExtent = mv.Extent.Expand(ratio, ratio, asRatio);
}
else if (mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneGlobal ||
mv.ViewingMode == ArcGIS.Core.CIM.MapViewingMode.SceneLocal) {
//get the extent in pixels
var sz = mv.GetViewSize();
var builder = new EnvelopeBuilderEx() {
XMin = 0,
YMin = sz.Height,
XMax = sz.Width,
YMax = 0
};
selExtent = builder.ToGeometry().Expand(ratio, ratio, asRatio);
}
return mv.SelectFeatures(selExtent);
}
ãã®ã³ãŒãã¯ç¹ã«ãŠããŒã¯ãªãã®ã§ã¯ã¯ãªããå¿ èŠã«å¿ããŠä»ã®ã¯ãŒã¯ãããŒã«çµã¿èŸŒãããšãã§ããŸãããã ããæ¶è²»ããŠããåºæ¬ç㪠ArcGIS Pro API ã®èŠä»¶ã«ãããQueuedTask.Run å ã§å®è¡ãããæäœã®äžéšãšããŠã®ã¿åŒã³åºãããšãã§ããŸãã
*ããã¯çµ¶å¯Ÿçãªãã®ã§ã¯ãªããçµéšåã«è¿ããã®ã§ããéèŠãªã®ã¯ãã©ã®ãããªç¶æ³ã§ããã¡ãœããã "éåæ" ãšããŠããŒã¯ããŠåæãšèŠãªãããšã¯ã§ããªããšããããšã§ãã ãããã£ãŠãåæã¡ãœããã¯éåæã³ãŒããå®è¡ã§ããŸãããåæãç¶æããããã«ããããåŸ ã€ããšã¯ã§ããŸããã
å¯äžã®ã¹ã¬ããæ¶è²»è ã§ã¯ãªãå Žåã¯ãã¹ã³ãŒãããããªãã¯ã§ãããããåæé¢æ°ã«ã¹ã¬ãã ãã§ãã¯ãè¿œå ããããšããå§ãããŸããåæ API ã¡ãœããã¯ãééã£ãã¹ã¬ããã§åŒã³åºãããå Žåã«äŸå€ãã¹ããŒããããã«å¯ŸåŠããã®ã§ãã«ã¹ã¿ã ã¡ãœãããã¹ã¬ãã ãã§ãã¯ãå®è¡ããããšã¯ å¿ é ã§ã¯ãããŸããããã³ãŒããåç §ããä»ã®ã¢ãã€ã³éçºè ã«ãšã£ãŠãèªå·±ææžåã®ããã®æçšãªæããããšãªããŸãã
QueuedTask ã«ã¯ãã³ãŒããçŸåšå®è¡ãããŠããã¹ã¬ããããã§ãã¯ããããã«ã¢ãã€ã³ã䜿çšã§ãã 2 ã€ã®éçããããã£ããããŸã: QueuedTask.OnGUI ãš QueuedTask.OnWorker ã§ããããã UI ãš MCT ã®ã©ã¡ãã«ããããå€æããŸããä»åã®ç®çã§ã¯ã©ã¡ãã§ãæ§ããŸãããMCT äžã«ããªãå Žåã¯ãCalledOnWrongThreadException ãã¹ããŒããŠãä»ã®éçºè ã«ãã®ã¡ãœããã®ã¹ã¬ãã ã¢ãã£ããã£èŠä»¶ãæããã«ããŸããéåžžãã¹ã¬ãã ãã§ãã¯ã¯æåã«è¡ããŸã:
public SelectionSet SelectFeaturesWithViewExtent(
MapView mv, double ratio, bool asRatio) {
Envelope selExtent = null;
//self-documenting thread check
if (!QueuedTask.OnWorker) {
throw new ArcGIS.Core.CalledOnWrongThreadException();
}
//null check
if (mv == null) throw new ArgumentNullException(nameof(mv));
...
}
ç¹å®ã®ã±ãŒã¹ã§ã¯ãã«ã¹ã¿ã ã¡ãœãã㯠UI ã® UI èŠçŽ ã®ã³ã³ãã³ã (é²è¡ç¶æ³ã衚瀺ããã³ã³ãããŒã«ãªã©) ãæŽæ°ããå¿ èŠããããŸãã WPF ã§ã¯ãUI èŠçŽ ã®ã³ã³ãã³ã㯠UI ã¹ã¬ãã* ããã®ã¿æŽæ°ã§ããŸãããããã£ãŠãã«ã¹ã¿ã ã¡ãœãããããã¯ã°ã©ãŠã³ã ã¹ã¬ãã (QueuedTask ã BackgroundTask ãªã©) ã§å®è¡ãããŠããå Žåã¯ãUI ã¹ã¬ããã§æŽæ°ãè¡ãã³ãŒããé©åã«åŒã³åºãå¿ èŠããããŸãã WPF ã¯ããã®ç®çã®ããã«ç¹å¥ã«Dispatcher ã¯ã©ã¹ãæäŸããŸãã
Dispatcher 㯠WPF ã® ä»»æã® UI èŠçŽ ããã¢ã¯ã»ã¹ã§ããŸãããã»ãšãã©ã®ã¢ãã€ã³ ã³ãŒãã¯ããã¥ãŒ ã¢ãã«ãã¢ãã€ã³ ã¢ãžã¥ãŒã«ãªã©ã®éããžã¥ã¢ã«ã¯ã©ã¹ã§å®è¡ãããå¯èœæ§ããããããWPF ã® UI èŠçŽ ã«ããã«ã¢ã¯ã»ã¹ã§ããŸããã ã€ã³ã¹ã¿ã³ã¹ (Dispatcher ã«ã¢ã¯ã»ã¹ãããã)ã ãã ããArcGIS Pro ã¢ããªã±ãŒã·ã§ã³ ã€ã³ã¹ã¿ã³ã¹ã¯ FrameworkApplication.Current
ãä»ããŠããã«å©çšã§ãããã® Dispatcher ã¯ãã®ç®çã«äŸ¿å©ã«äœ¿çšã§ããŸã:
//access the Pro application dispatcher to invoke UI updates
var dispatcher = FrameworkApplication.Current.Dispatcher;
...
WPF ã® Dispatcher ãªããžã§ã¯ãã¯ãUI èŠçŽ ãæŽæ°ããããã® 2 ã€ã®äž»èŠãªã¡ãœãããæäŸããŸã - éåæã® Dispatcher.BeginInvoke ãšåæã® Dispatcher.Invoke ã§ã (ãããŠããããã¯ãããŸã)ãBeginInvoke
ã¯ãæå®ãããã¢ã¯ã·ã§ã³ã UI ã§å®è¡ããããã«ã¹ã±ãžã¥ãŒã«ããããã«å¶åŸ¡ãã¢ãã€ã³ã«æ»ããŸããBeginInvoke ãåŸ
æ©ããå¿
èŠã¯ãããŸãã (åæã¡ãœãããéåæã¡ãœããã« "å€æ" ãããã)ãããã¯åºæ¬çã«ãå®è¡ãããå¿ãããšããã¡ã«ããºã ã§ããUI ãã¢ãã€ã³ã®ã³ãŒããšæ£ç¢ºã«åæããŠããããšãéèŠãªå Žåã¯ãInvoke
ã䜿çšããŠãã ãããInvoke ã¯ãUI ãæŽæ°ããããŸã§åŒã³åºãå
ããããã¯ããŸããå®éã«ã¯ãInvoke ã䜿çšããããšã¯ã»ãšãã©ãªãã§ãããã
UI ã®æŽæ°ãè¡ãã¢ãã€ã³ ã³ãŒãã UI ãŸã㯠ArcGIS Pro ããã¯ã°ã©ãŠã³ã ã¹ã¬ããã®äž¡æ¹ã§å®è¡ã§ããå Žåã¯ãDispatcher.CheckAccess()
ã䜿çšã㊠Invoke ãå¿
èŠãã©ããããã¹ãããããšãã§ããŸããCheckAccess ã¯ãçŸåšã®ã¹ã¬ããããDispatcher ã«é¢é£ä»ããããŠããã¹ã¬ãã (ããã§ã¯ UI ã¹ã¬ãã) ãšåããã©ããããã§ãã¯ããŸããCheckAccess ã true ãè¿ããå Žåãinvoke 㯠å¿
èŠãããŸãã (UI ã¹ã¬ããã«ãããã)ãCheckAccess ã false ãè¿ããå Žåãinvoke ãå¿
èŠã«ãªããŸã (UI ã¹ã¬ããã«ããªããã)ã
å®æããããžãã¯ã¯ä»¥äžã®ããã«ãªããŸã*:
//Assume this method does the relevant UI update...
private void UpdateProgress(...) {
//whatever "update progress" means - change a counter, text, etc
...
NotifyPropertyChanged("ProgressValue");//Assuming something is bound to this
}
//Our method that needs to update the UI
public void DoWork(...) {
...
//this code is running synchronously but on which thread?
//check UI access...
if (FrameworkApplication.Current.Dispatcher.CheckAccess()) {
//this means we are on the UI thread already
UpdateProgress(...);//no invoke necessary
}
else {
//this means we are on another thread (usually the MCT)
//we must invoke progress via the Dispatcher
FrameworkApplication.Current.Dispatcher.BeginInvoke(
(Action)(() => UpdateProgress(...))); //Fire and forget...
}
...
*"é²è¡ç¶æ³" ããŠãŒã¶ãŒã«ã·ã³ãã«ã«äŒéããããã«ãprogressor ã¯ã©ã¹ã¯ãå ¬ç§°åšæ³¢æ°ã§é²è¡ç¶æ³ãæŽæ°ããããã®çŽç²ãªã³ãŒã«ãã㯠ã¡ã«ããºã ãæäŸããŸãã
ãã®äŸã¯ BeginInvoke (éåæ) ã䜿çšããŠããããããåŸ
æ©ããŠããªãããšã«æ³šæããŠãã ãããBeginInvoke ã䜿çšããããã®æ§æã¯ãããè€éã«ãªãå¯èœæ§ããããŸãã äž»ãªåé¡ã¯ãéåžžãå¿åããªã²ãŒãã®ã¿ãé€ããªãŒããŒããŒãã䜿çšããå Žå (ã€ãŸãã "() => ..." ã©ã ã) ã§ãã"Action" ãšå
¥åããã«ã¯ãã©ã ãã®æ瀺çãªãã£ã¹ããæäŸããå¿
èŠããããŸããããããªããšãã³ãŒããã³ã³ãã€ã«ãããŸããããã£ã¹ãã¯æ¬¡ã®ããã«ãªããŸã: (Action)(... lambda going here ...)
ãã㯠(Action)(() => UpdateProgress(...))
ã®æçµçãªæ§æã«ã€ãªãããŸãã
ã¢ãã€ã³ã¯ãUI ã®æŽæ°ããžãã¯ããŠãŒãã£ãªã㣠ã¡ãœããã«çµ±åããããšãã§ãã次ã®ããã«æ§æãåçŽåã§ãããšããå©ç¹ããããŸã:
//Utility method to consolidate UI update logic
public void RunOnUIThread(Action action) {
if (FrameworkApplication.Current.Dispatcher.CheckAccess())
action();//No invoke needed
else
//We are not on the UI
FrameworkApplication.Current.Dispatcher.BeginInvoke(action);
}
//Assume this method does the relevant UI update...
private void UpdateProgress(...) { ... }
//Our method that needs to update the UI
public void DoWork(...) {
...
//Use the utility method...
Module1.Current.RunOnUIThread(() => UpdateProgess(50, "working..."));
...
*ãã¹ãŠã® WPF ã® UI ã³ã³ãã³ãã¯ãDispatcherObject ãã掟çããŠããŸãã DispatcherObjects ã«ã¯ãããããäœæãããã¹ã¬ããããã®ã¿ã¢ã¯ã»ã¹ã§ããŸããå€ãã®å Žåãææã¹ã¬ãããšåŒã°ããŸããã»ãšãã©ã®å ŽåãUI ã³ã³ãã³ãã®ææã¹ã¬ãã㯠UI ã¹ã¬ããã§ãã
ArcGIS Pro APIã®ãã¿ãŒã³ã«åŸããšãã«ã¹ã¿ã ã® "select "ã¡ãœããã«éåæã®ä»£æ¿æ段ãæäŸããUI ã¹ã¬ããããå®å šã«åŒã³åºããããã«ããããšãã§ããŸããããã«ãããUI ã¹ã¬ããããå®å šã«åŒã³åºãããšãã§ããŸããéåæã¡ãœããã®èšèšã§ã¯ãåæã¡ãœããã QueuedTask ã§ã©ããããè¿ãããåã "Task "ã«å€æŽããŸããã¡ãœããåã« "Async "ãšãããµãã£ãã¯ã¹ãä»ããŠãåæã¡ãœãããšã®å·®å¥åãå³ã£ãŠããŸãããããã¯å¿ é ã§ã¯ãªãããããŸã§ãæ £äŸã§ãããã®éåæã¡ãœããã¯æ¬¡ã®ããã«å®è£ ãããŠããŸã:
//recall: synchronous method signature
public SelectionSet SelectFeaturesWithViewExtent(
MapView mv, double ratio, bool asRatio) { ... }
//Asynchronous alternative that wraps the synchronous method
public Task<SelectionSet> SelectFeaturesWithViewExtentAsync(
MapView mv, double ratio, bool asRatio)
{
//Wrap the synchronous method in a QueuedTask
return QueuedTask.Run(() => SelectFeaturesWithViewExtent(mv, ratio, asRatio));
}
ã«ã¹ã¿ã ã¢ãã€ã³ã®ã³ãŒããäž»ã«åæã¡ãœãããšããŠèšè¿°ããããšã§ãQueuedTask.Run ã§åæã¡ãœãããã©ããããã ãã§ãå¿ èŠã«å¿ããŠéåæã®ä»£æ¿æ段ãç°¡åã«æäŸã§ããŸããSelectFeaturesWithViewExtentAsync ã UI ã¹ã¬ããããå®å šã«åŒã³åºãããšãã§ããããã«ãªããŸããã
ArcGIS Pro ã®ã»ãšãã©ãã¹ãŠã® GIS æäœã¯ãQueuedTask ã§å®è¡ãããŸãããã ããæäœãããã¯ã°ã©ãŠã³ã ã¹ã¬ããäžã§ãã³ã¢ãŒãã«ã«å®è¡ããããšãé©ããŠããå Žåãããã€ããããŸã (COM ã³ã³ããŒãã³ãã®äœ¿çšãšäºææ§ããããŸã)ããããã®æäœã¯é·æéå®è¡ãããå¯èœæ§ããããã¢ãã€ã³éçºè ã¯ãã®é QueuedTask ãææããããšãé¿ããArcGIS Pro ã® UI ãæå¹ã«ãããŸãŸã«ããŠãããããšèããŠããŸããBackgroundTask ã¯ãArcGIS Pro ã®ããã¯ã°ã©ãŠã³ã ã¹ã¬ãã ããŒã«ã§åŠçãå®è¡ããŸãããããã¯ããŒãžã§ã³ 2.6 以åã¯ãããªã㯠API ã§ã¯å©çšã§ããŸããã§ããã
ArcGIS Pro ã®ããã¯ã°ã©ãŠã³ã ã¹ã¬ãã ããŒã«ã¯ãéåžžã®éšåãšåªå
床ã®é«ãéšåã«åããããŸããéåžžã®åªå
床ã®ããŒã«ã¯ãã»ãšãã©ã®æäœã察象ãšããŠãããäžçšåºŠããé·æéå®è¡ãããæäœã«é©ããŠããŸããåªå
床ã®é«ãããŒã«ã¯ãI/O æäœã䌎ããªãèšç®æ©çãªæ§è³ªãæã€çæéã®æäœã®ããã«å³å¯ã«ç¢ºä¿ãããŠããŸããç¹å®ã®ãªã¯ãšã¹ããçããé·ããã確å®ã«å€æã§ããªãå Žåã¯ãé·ããšä»®å®ããŠéåžžã®åªå
床ããŒã«ã䜿çšããŠãã ãããããã¯ã°ã©ãŠã³ãæäœã®åªå
床ã¯ãBackgroundTask.Run() ã®ç¬¬äžåŒæ°ãšããŠæž¡ããã ArcGIS.Core.Threading.Tasks.TaskPriority
ãã©ã¡ãŒã¿ãŒã§æå®ããŸãã
æäœã QueuedTask ã§ã¯ãªã BackgroundTask ã§ã®äœ¿çšã«é©ããŠãããã©ãããå€æããã«ã¯ã次ã®åºæºãæºããå¿ èŠããããŸã:
- æäœã¯ ArcGIS Pro ã®ç¶æ ã«ã¢ã¯ã»ã¹ãããå€æŽãããããŸãã (ã€ãŸããCIM ã®ç¶æ ããªããããã¬ã€ã€ãŒãããããã¹ã¿ã€ã«ãã¬ã€ã¢ãŠãããŸãã¯ãã®ä»ã®ãããžã§ã¯ãã³ã³ãã³ãã®èªã¿åã/æžã蟌ã¿ã¯ãããŸãã)ãæäœã®æåã®æ®µéã§ç¶æ ã èªã¿åã ããšã¯å®å šã§ãããããã¯ã°ã©ãŠã³ãæäœã§ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ç¶æ ãäžæŠéå§ãããåŸãéçãªãŸãŸã§ãããšä»®å®ããããšã¯ã§ããŸãã(QueuedTask å ã®å Žåãšåæ§)ã
- ãã®æäœã¯ãå®è¡äžã«ãŠãŒã¶ãŒããã®å ¥åãå¿ èŠãšããŸãã (ãã ããããã¯ã°ã©ãŠã³ã ã¿ã¹ã¯ã¯ãUI ãéããŠãŠãŒã¶ãŒã« é²è¡ç¶æ³ ãäŒããããšãã§ããŸã)ã
BackgroundTask 㯠QueuedTask ãšã¯å€§ããç°ãªããŸããQueuedTask ã¯æäœã åäžã®ã¹ã¬ãã (MCT) ã«ãã¥ãŒãè¿œå ããéä¿¡ãããæäœã FIFO é ã«å®è¡ããŸãããBackgroundTask ã¯ãArcGIS Pro ã®ããã¯ã°ã©ãŠã³ã ã¹ã¬ãã ããŒã«ã§äœ¿çšå¯èœãªã¹ã¬ããã䜿çšããŠãéä¿¡ãããæäœã åæã« å®è¡ããŸããåæã«å®è¡ãããããã¯ã°ã©ãŠã³ã ã¿ã¹ã¯éã§å ±æãããã¢ãã€ã³ã®ç¶æ ã¯ãç Žæãé²ãããã«ãé©åãªããã¯ããã¥ãŒããã¯ã¹ãªã©ã§ä¿è·ããå¿ èŠããããŸããããã¯ã°ã©ãŠã³ãæäœãè¡ãããŠããéããŠãŒã¶ãŒã¯èªç±ã«çŸåšã®ãããžã§ã¯ããã¢ã³ããŒãããããã¢ã¯ãã£ããªãã¥ãŒãå€æŽããããã¢ããªã±ãŒã·ã§ã³ã«å€æŽãå ãããããããšãã§ããŸããããã¯ã°ã©ãŠã³ãæäœã¯ããã®ãããªå Žåã«ã察å¿ã§ããããã«å ç¢ã§ããå¿ èŠããããŸãããã¹ãããã BackgroundTask ã¯ããããã®ãããªããšããã£ããšããŠããã€ã³ã©ã€ã³åãããŸããããããããã¹ãããã QueuedTasks ã (åäžã®ã¹ã¬ããã«) ã€ã³ã©ã€ã³åãããåæé¢æ°ã§ãããã®ããã«å®è¡ããã QueuedTask ãšæ¯èŒããŠãã ããã
BackgroundTask ã¯ãã¹ã¬ãã ã¢ãã£ããã£ãæã€ãªããžã§ã¯ãã§ã®äœ¿çšãæ³å®ããŠããŸããããBackgroundTask.Run ã§ã©ã ããåŒã³åºãéã« TaskPriority.single
åã® TaskPriority ã䜿çšããããšã§ãã¹ã¬ãã ã¢ãã£ããã£ãæäŸããäžè²«ããã¹ã¬ããã§ã¿ã¹ã¯ãå®è¡ã§ããŸã:
//Use TaskPriority.single for background operations with thread affinity
await ArcGIS.Core.Threading.Tasks.BackgroundTask.Run(
TaskPriority.single, () => {
//Do Work
...
}, BackgroundProgressor.None);
以äžã®äŸã§ã¯ããŠãŒãã£ãªã㣠ã¡ãœããã BackgroundTask ã䜿çšããŠãStyleItems ã®ã³ã¬ã¯ã·ã§ã³ããäžé£ã®ãã¬ãã¥ãŒç»åãçæããŠããŸããã¢ããªã±ãŒã·ã§ã³ã®ç¶æ
ã¯å€æŽãããŠããªããããQueuedTask ããã®æäœãšçµã³ä»ããå¿
èŠã¯ãããŸããã ããã«ããã¬ãã¥ãŒç»åã®çæã«ã¯ã³ã¹ãããããå¯èœæ§ããããããBackgroundTask ã¯ããã©ã«ãã®åªå
床ã§ãã TaskPriority.normal
ã䜿çšããŠå®è¡ãããŠããŸã:
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ArcGIS.Core.Threading.Tasks;
...
//Generate preview images for the input style items
internal Task<List<ImageSource>> GeneratePreviewImagesAsync(IList<StyleItem> styleItems) {
//Application state is not affected so let's use a background thread...
//This is potentially long running so leave the priority at 'normal'
return BackgroundTask.Run(() => {
//make each image a little bigger (just for purposes of visibility)
var scale = new ScaleTransform() {
ScaleX = 2,
ScaleY = 2
};
var preview_images = new List<ImageSource>();
foreach (var style_item in styleItems) {
//If PreviewImage is null, a PreviewImage will be generated
var bmsource = (BitmapSource)style_item.PreviewImage;
//scale up the image and save it
preview_images.Add(new TransformedBitmap(bmsource, scale));
}
return preview_images;
}, BackgroundProgressor.None);
}
//Usage...
var styles = Project.Current.GetItems<StyleProjectItem>().ToList();
var theStyle = styles.First(style => style.Name == "ArcGIS 2D");
//Get all the point symbols from the 2D style
var point_symbols = await QueuedTask.Run(() => theStyle.SearchSymbols(StyleItemType.PointSymbol, ""));
//Generate preview images for each in the background
var preview_images = await GeneratePreviewImagesAsync(point_symbols);