【ArcGIS Pro二次开发】(45):手搓一个工具执行进度框

我在之前做的工具中,UI这部分基本没怎么深入,都是直接用现成的控件。

其中有一个问题比较突出,就是没有工具执行的进度框提示。曾经也用过系统自带的信息提示框和进度条,但太简陋,确实不好用。于是就想抄一个进度框来用,自然而然想到了ArcGIS中地理工具执行时显示的进度框:

这里做一个简单版的,够用就行(其实是目前只会这些)。


一、要实现的功能

进度框无法单独使用,这里结合之前做的一个【面要素拓扑】工具一起使用。

如上图所示,选择一个面要素,右键点击,在弹出的列表中点击【面要素拓扑】按钮,即可对所选要素进行处理,同时弹出一个进度提示框,实时显示目前的进程和用时。

可以看出,已经和ArcGIS中地理工具的进度框有几分相似。

其实,显示用时这一点不仅对用户有帮助,对开发、测试同样是很有用的,有利于不断优化你的代码,提高代码执行效率。毕竟一个简单的功能老是显示耗时几分钟,你也很难受。


二、实现流程

1、控件选择

首先,需要确定进度框用什么来做,可以用Form,但我最终还是选择了ArcGIS ProWindow控件,毕竟已经给我们集成了更多基础性功能,用起来应该更方便。

新建一个ArcGIS ProWindow,主要的控件就2个,【ProgressBar】进度条和【RichTextBox】富文本框。

【ProgressBar】用来显示当前执行进度,【RichTextBox】用来显示提示信息,包括正常流程信息和错误信息。这里使用【RichTextBox】而不是更常用的【TextBox】是考虑到显示内容需要不同字体和颜色,才能显示更丰富的文本内容,【TextBox】是无法做到的。

至于打开窗口的方法,其实【ArcGIS ProWindow】已经给我们写好了,正常创建【ArcGIS ProWindow】控件的时候,就会一起创建一个【Show*****.cs】,里面就有打开窗口的代码:

        private ProcessWindow _processwindow = null;

        protected override void OnClick()
        {
            if (_processwindow != null)
                return;
            _processwindow = new ProcessWindow();
            _processwindow.Owner = FrameworkApplication.Current.MainWindow;
            _processwindow.Closed += (o, e) => { _processwindow = null; };
            _processwindow.Show();
        }

把这部分代码抄到工具执行代码段里就行了。

2、【ArcGIS ProWindow】方法

因为上面的信息都是在【ArcGIS ProWindow】控件里显示的,所以方法都得写在ProcessWindow.xaml.cs里:

这里主要需要定义3个方法:【更新进度条、添加信息文本和添加耗时文本】。

1)更新进度条

        // 变更进度条的进度【100%】
        public void AddProcess(int percent)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                pb.Value += percent;
            });
        }

这个比较简单,只要修改进度条控件的Value值即可。不过需要注意的是,操作都要在UI线程上执行,不能放在主线程里,所以需要在【Dispatcher.Invoke】下执行。

2)添加信息文本

添加信息文本需要考虑到文本的颜色和字体,所以除了文本,还添加了颜色和字体2个参数,并且设置了默认值,大部分情况下其实用默认值就行,有需要才修改参数:

        // 添加信息框文字
        public void AddMessage(string add_text, SolidColorBrush solidColorBrush = null)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                if (solidColorBrush == null)
                {
                    solidColorBrush = Brushes.Black;
                }
                // 创建一个新的TextRange对象,范围为新添加的文字
                TextRange newRange = new TextRange(tb_message.Document.ContentEnd, tb_message.Document.ContentEnd)
                {
                    Text = add_text
                };
                // 设置新添加文字的颜色
                newRange.ApplyPropertyValue(TextElement.ForegroundProperty, solidColorBrush);
                // 设置新添加文字的样式
                newRange.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Normal);
            });
        }

3)添加耗时文本

耗时信息需要计算,在工具开始执行的时候在保存一下开始时间【time_base】,然后将其作为输入参数参与计算。将新的当前时间减去【time_base】即为耗时,然后作为为文字信息写入文本框,文本颜色设为灰色,文本字体设为斜体,这部分就直接固定,不再给参数。

        // 添加信息框文字_时间
        public void AddTime(DateTime time_base)
        {
            System.Windows.Application.Current.Dispatcher.Invoke(() =>
            {
                DateTime time_now = DateTime.Now;
                TimeSpan time_span = time_now - time_base;
                string time_total = time_span.ToString()[..time_span.ToString().LastIndexOf(".")];
                string add_text = "………………用时" + time_total + "\r";

                // 创建一个新的TextRange对象,范围为新添加的文字
                TextRange newRange = new TextRange(tb_message.Document.ContentEnd, tb_message.Document.ContentEnd)
                {
                    Text = add_text
                };
                // 设置新添加文字的颜色为灰色
                newRange.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Gray);
                // 设置新添加文字的样式为斜体
                newRange.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Italic);
            });
        }

4)组合方法

上述三个主要方法写完,其实还可以更进一步,因为正常情况下,在某一个节点,通常都不是只调用一个方法,而是多个一起调用,比如在添加文本信息的时候,同时更新进度条和上一个步骤的耗时。所以这里可以做一个方法的组合,调用的时候就可以只调用这个方法:

        // 综合显示进度【AddTime+AddMessage+AddProcess】
        public void AddProcessMessage(int percent, DateTime time_base, string add_text, SolidColorBrush solidColorBrush = null)
        {
            AddProcess(percent);
            AddTime(time_base);
            AddMessage(add_text, solidColorBrush);
        }

        // 综合显示进度【AddMessage+AddProcess】
        public void AddProcessMessage(int percent, string add_text, SolidColorBrush solidColorBrush = null)
        {
            AddProcess(percent);
            AddMessage(add_text, solidColorBrush);
        }
3、在主程序中调用【ArcGIS ProWindow】方法

直接先上完整代码:

        // 定义一个进度框
        private ProcessWindow processwindow = null;
        string tool_name = "面要素拓扑检查";

        protected override async void OnClick()
        {
			try
			{
                // 打开进度框
                ProcessWindow pw = ToolManager.OpenProcessWindow(processwindow, tool_name);
                DateTime time_base = DateTime.Now;
                pw.AddMessage("开始执行" + tool_name + "工具…………" + time_base + "\r", Brushes.Green);

                var map = MapView.Active.Map;
                // 获取默认数据库
                var gdb = Project.Current.DefaultGeodatabasePath;
                // 获取工程默认文件夹位置
                var def_path = Project.Current.HomeFolderPath;
                // 获取图层
                FeatureLayer ly = MapView.Active.GetSelectedLayers().FirstOrDefault() as FeatureLayer;
                // 如果选择的不是面要素或是无选择,则返回
                if (ly.ShapeType != esriGeometryType.esriGeometryPolygon || ly == null)
                {
                    pw.AddMessage("错误!请选择一个面要素!", Brushes.Red);
                    return;
                }
                
                string db_name = "Top2Check";    // 要素数据集名
                string fc_name = "top_fc";        // 要素名
                string top_name = "Topology";       // TOP名
                string db_path = gdb + "\\" + db_name;    // 要素数据集路径
                string fc_path = db_path + "\\" + fc_name;         // 要素路径
                string top_path = db_path + "\\" + top_name;         // TOP路径

                string err_fc = @"检查结果";
                string err_field = @"错误说明";

                await QueuedTask.Run(() =>
                {
                    pw.AddProcessMessage(10, "创建检查用的数据库和拓扑");

                    //获取图层的坐标系
                    var sr = ly.GetSpatialReference();
                    //在数据库中创建要素数据集
                    Arcpy.CreateFeatureDataset(gdb, db_name, sr);
                    // 将所选要素复制到创建的要素数据集中
                    Arcpy.CopyFeatures(ly.Name, fc_path);
                    // 新建拓扑
                    Arcpy.CreateTopology(db_path, top_name);
                    // 向拓扑中添加要素
                    Arcpy.AddFeatureClassToTopology(top_path, fc_path);
                    // 添加拓扑规则【重叠】
                    Arcpy.AddRuleToTopology(top_path, "Must Not Overlap (Area)", fc_path);
                    // 添加拓扑规则【空隙】
                    Arcpy.AddRuleToTopology(top_path, "Must Not Have Gaps (Area)", fc_path);

                    pw.AddProcessMessage(20, time_base, "生成重叠错误");

                    // 验证拓扑
                    Arcpy.ValidateTopology(top_path);
                    // 输出TOP错误
                    Arcpy.ExportTopologyErrors(top_path, gdb, "TopErr");

                    pw.AddProcessMessage(20, time_base, "生成空隙错误");

                    // 生成空隙
                    ToolManager.GetCave(fc_path, gdb + @"\" + err_fc);
                    // 添加说明字段
                    Arcpy.AddField(gdb + @"\" + err_fc, err_field, "TEXT");
                    // 空隙错误说明赋值
                    Arcpy.CalculateField(gdb + @"\" + err_fc, err_field, "'存在空隙'");
                    // 合并错误
                    Arcpy.Append(gdb + @"\TopErr_poly", gdb + @"\" + err_fc);
                    // 加载错误面图层
                    ToolManager.AddFeatureLayerToMap(gdb + @"\" + err_fc);

                    pw.AddProcessMessage(20, time_base, "生成错误标记");

                    // 重叠错误说明赋值
                    FeatureLayer init_layer = map.FindLayers(err_fc)[0] as FeatureLayer;
                    using (ArcGIS.Core.Data.Table table = init_layer.GetTable())
                    {
                        using (RowCursor rowCursor = table.Search(null, false))
                        {
                            TableDefinition tableDefinition = table.GetDefinition();
                            while (rowCursor.MoveNext())
                            {
                                using (Row row = rowCursor.Current)
                                {
                                    // 获取value
                                    var va = row[err_field];
                                    // 赋值
                                    if (va is null)
                                    {
                                        row[err_field] = "存在重叠面";
                                    }
                                    row.Store();
                                }
                            }
                        }
                    }
                    // 删除多余字段
                    Arcpy.DeleteField(err_fc, "ORIG_FID");

                    pw.AddProcessMessage(20, time_base, "应用错误图层的显示符号");

                    // 复制图层符号
                    string copy_lyrx = def_path + @"\检查结果.lyrx";
                    ToolManager.CopyResourceFile(@"CCTool.Data.Layers." + @"检查结果.lyrx", copy_lyrx);
                    // 应用图层符号
                    Arcpy.ApplySymbologyFromLayer(err_fc, copy_lyrx);

                    // 删除中间要素
                    List<string> list_del = new List<string>() { "TopErr_point", "TopErr_line", "TopErr_poly" };
                    foreach (var fc in list_del)
                    {
                        Arcpy.Delect(gdb + @"\" + fc);
                    }
                    // 删除数据集和符号图层
                    Arcpy.Delect(db_path);
                    File.Delete(copy_lyrx);

                    pw.AddProcessMessage(20, time_base, "工具运行完成!!!", Brushes.Blue);
                });
            }
			catch (Exception ee)
			{
                MessageBox.Show(ee.Message + ee.StackTrace);
				throw;
			}
        }

首先按上面【控件选择】那一节中的代码,先打开提示框。

在工具刚开始执行的时候,先生成一个初始时间,并把开始执行的信息和初始时间都添加到文本框里。

// 打开进度框
ProcessWindow pw = ToolManager.OpenProcessWindow(processwindow, tool_name);
DateTime time_base = DateTime.Now;
pw.AddMessage("开始执行" + tool_name + "工具…………" + time_base + "\r", Brushes.Green);

然后在每一个你认为需要添加信息的节点,直接调用上面写好的方法即可,如:

pw.AddProcessMessage(20, time_base, "生成重叠错误");

需要注意进度条的满值是100,这个需要自己协调好。


三、工程文件分享

 最后,放上工程文件的链接:

PrcessingFramehttps://pan.baidu.com/s/12b74CXMIs9aBJaFq_QB1fg?pwd=qz8t

PS:可以直接点击...bin\Debug\net6.0-windows\下的.esriAddinX文件直接安装。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

规划GIS会

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值