热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AxControlTool部分介绍

1.典型的插件式架构1.1.什么是插件式架构插件式架构设计中主要包括三个重要部分,宿主、插件协议以及插件实现。宿主是指使用插件的部分,该模块可以是一个类,也可以是多个接口和类组成的

1.典型的插件式架构

1.1.什么是插件式架构

插件式架构设计中主要包括三个重要部分,宿主、插件协议以及插件实现。宿主是指使用插件的部分,该模块可以是一个类,也可以是多个接口和类组成的模块。插件协议是指宿主与插件之间的协议,宿主根据这个协议去调用插件的功能,插件根据这个协议去实现宿主需要的功能。插件实现就是基于插件协议实现的一个个具体插件。插件协议一般用接口体现。


1.2.AO中的Command-Tool插件架构

要梳理AO中的Command-Tool插件架构,就要找到对应的宿主、插件协议以及具体的插件实现。

我们先通过ICommand的定义,找下宿主是什么。

当命令被创建的时候,会传一个名为hook的参数进来,该参数为object类型,名称翻译过来叫钩子。这个名称挺形象的,相当于这个小小的插件钩住了一个很庞大宿主对象,这样在这个插件中,就可以使用宿主对象的任何资源了。

其关系如下图所示。

 

我们开发的时候,常用的宿主就是IMapControl、IPageLayoutControl,分别可以有AxMapControl.Object和AxPageLayoutControl.Object获取。AO API已经为我们实现了上百个命令或者工具。例如我们最常用的地图放大工具、地图全图命令等。这些工具和命令我们都可以直接使用。

下面代码展示了如何设置当前的地图工具为放大工具。

ESRI.ArcGIS.SystemUI.ITool myZoomInTool=new ControlsMapZoomInToolClass();
(myZoomInTool
as ICommand).OnCreate(myAxMapControl.Object);
(myAxMapControl.Object
as IMapControl2). CurrentTool = myZoomInTool;

调用AO API自带的全图工具的代码如下。

ESRI.ArcGIS.SystemUI.ICommand myFullCommand=new ControlsMapFullExtentCommandClass ();
myFullCommand.OnCreate(myAxMapControl.Object);
myFullCommand. OnClick();

2.ICommnad接口

ICommand接口是插件协议之一,继承该接口的类都可以成为命令。即点击一下执行,不主动与宿主发生鼠标和键盘交互。该接口包含的重要成员如下表所示。





















































序号


名称


类型


描述


1


Bitmap


Int


命令上显示的图标


2


Caption


String


命令上显示的文字


3


Checked


Bool


命令是否处于选中状态


4


Enabled


Bool


命令当前是否可用


5


OnClick


函数


点击命令时,触发执行的函数


6


OnCreate


函数


创建该命令时,调用的函数


7


Tooltip


String


鼠标放到命令上要显示的文字


这些属性和函数的名字都比较容易理解,一看就知道其作用。如果我们自己继承ICommand实现一个弹出当前地图包含几个图层的命令,命名为LayerCountCommand。那么只要在OnClick函数中,获取OnCreate函数传进来的宿主对象,从宿主对象中获取当前加载的地图,进而获取其包含的几个图层,使用消息对话框弹出即可。

我们实现命令时候,只需要关注宿主对象就可以了,和其他Command不会发生直接关联。如果需要和其他Command关联,那么这些关联都通过宿主进行。例如我们刚才实现的LayerCountCommand在地图图层为0的时候,其处于不可用的状态,也就是 Enabled属性等于False。按照正常思维的话,我们会在OpenMapCommand里面,判断一下当前图层个数是否为0,从而设置设置LayerCountCommand实例的Enabled属性。那RemoveLayerCommand、AddLayerCommand、NewMapCommand等怎么办呢?难道每个命令里面都要判断LayerCountCommand是都可用?

当然不会是这样,我们会为宿主添加MapChanged和LayersChanged事件,无论用OpenMapCommand还是其他地方,在系统中打开一个新地图后,宿主对象就会触发MapChanged事件。而我们实现的LayerCountCommand会监测该事件,当该事件触发后,LayerCountCommand会判断当前地图有几个图层,从而设置自己是否是可用。

这样LayerCountCommand只关系宿主的信息,间接的和OpenMapCommand产生了关联。而LayerCountCommand自己可以决定自己是否可用,这就形成了高内聚,低耦合的设计。


3.ITool接口

ITool接口也是插件协议之一,实现该接口的类我们成为工具,这些工具是可以和地图显示控件进行鼠标、键盘交互的。该接口包含的重要成员如下表所示。



























































序号


名称


类型


描述


1


Cursor


Int


鼠标在地图显示控件上的样式


2


Deactivate


函数


工具失活的时候触发的函数


3


OnMouseDown


函数


鼠标按下执行的函数


4


OnMouseMove


函数


鼠标移动执行的函数


5


OnMouseUp


函数


鼠标谈起执行的函数


6


OnDblClick


函数


鼠标双击地图显示控件执行的函数


7


OnKeyDown


函数


键盘按键按下执行的函数


8


OnKeyUp


函数


键盘按键弹起执行的函数


每个宿主对象都有CurrentTool属性,如果点击MapZoomInTool工具,那么该宿主的CurrentTool就是该工具了。当鼠标在宿主对象上按下的时候,会自动调用宿主对象CurrentTool属性值的OnMouseDown,也就是MapZoomInTool工具的OnMouseDown函数。当鼠标在宿主对象上移动的时候,会自动调用MapZoomInTool的OnMouseMove函数。其他动作一次类推,并且宿主对象读取CurrentTool的Cursor作为当前的鼠标样式。

要做一个放大工具,需要考虑拉框放大、点击放大等。但有了宿主程序,就可以把这些代码完整的封装到MapZoomInTool类中。而实现地图缩小功能MapZoomOutTool以及地图平移的MapPanTool,完全不需要知道其他工具的存在,也不关心其他Tool都做了什么。

在ArcMap中,地图放大、地图缩小、地图平移、地图全图地图即放大、地方即缩小、上一视图和下一视图这几个Tool和Command关系非常密切。如下图所示。

但通过这种插件式架构,他们不光代码之间无直接联系,而且还可以做到随意组合使用。例如我们开发的系统可以使用放大、缩小和平移三个工具,也可以加上全图,或者再加上其他工具,都可以正常运行。这种模式非常值得我们思考和借鉴。


4.我们能借鉴到什么

ArcMap就是通过这种插件式架构把各种Command和Tool组合起来的,而这些Comamnd和Tool的背后是一个个功能点。不光使得ArcMap可以持续集成那么多功能,甚至可以开放接口,让开发人员直接在ArcMap上扩展。

那这种模式是不是可以借鉴到我们的软件开发中?答案是肯定的,下面我们就来设计自己的App-Command框架。


5.我们自己的App-Command框架

5.1.为什么再设计一套App-Command框架

为什么我们要自己再设计一套App-Command框架,而不直接使用AO API中的AxControl-ICommand这套已经非常好的框架呢?

1、宿主不同。我们系统的宿主对象除了可能要包含MapControl等地图显示控件外,还可能会包含我们业务系统特有的信息。例如当前登录用户,在一些Command中,可能需要根据当前登录用户的觉得来判断功能是否可用等。

2、AO中的ICmmand和ITool已经和UI绑定到一起了,我们并不想直接用AO中定义的ToolBar,这样会和我们的系统风格不一致。还有ICommand中定义的Bitmap以及ITool中定义的Cursor都是int类型,这并不符合我们的使用习惯。如果我们使用传统菜单+工具条的模式,使用的都是16*16的图标,如果我们采用Office的Ribbon风格,那么可能会出现很多32*32的图标,这个如何兼容?

3、我们想让我们定义的工具适应更多的UI。例如定义的Command和Tool和绑定到WPF自带的按钮上,也可以绑定到第三方库例如DEV定义的按钮上。这就需要多UI进行抽象。


5.2.基于AO设计的App-Command框架

我们参考借鉴AO,定义我们自己的App-Command框架如下,定义的时候,主要是解决上述的几个问题。我们定义的框架如下。

 

 

IApplication、ICommand、IMapTool以及ICmmandUI四个接口以及MapApplication类是整个框架的核心部分。除了上图中体现出来的内容外,框架还包含Command、MapTool以及 ViewSynchronizer等基类和辅助类。


5.3.ITool接口是怎么交互的

我们在MapApplication类中封装了宿主与ITool接口的交互。封装代码如下。

this.AxMapControl.OnMouseDown += (x, y) =>
{
this._CrruteTool.OnMouseDown(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnMouseMove += (x, y) =>
{
this._CrruteTool.OnMouseMove(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnMouseUp += (x, y) =>
{
this._CrruteTool.OnMouseUp(y.button, y.shift, y.x, y.y);
};
this.AxMapControl.OnDoubleClick += (x, y) =>
{
this._CrruteTool.OnDblClick();
};
this.AxMapControl.OnKeyDown += (x, y) =>
{
this._CrruteTool.OnKeyDown(y.keyCode, y.shift);
};
this.AxMapControl.OnKeyUp += (x, y) =>
{
this._CrruteTool.OnKeyUp(y.keyCode, y.shift);
};

对于宿主来说,并不关心当前使用的到底是哪个Tool,只管在触发动作的时候,去调用当前工具对应的函数即可。

工具切换的代码如下。

public IMapTool CrruteTool
{
get
{
return this._CrruteTool;
}
set
{
this._CrruteTool.OnDeActivate();
this._CrruteTool.IsChecked = false;
this._CrruteTool = value;
if (this._CrruteTool == null)
{
this._CrruteTool = new NullMapTool(this);
}
this._CrruteTool.OnActive();
this._CrruteTool.IsChecked = true;
}
}

切换工具的时候,首先要调用工具的OnDeActivate函数,把当前工具的使用痕迹清理掉。设置新工具,调用新工具的OnActive函数,激活该工具。


5.4.封装AO已有的Command

AO本身为我们提供了很多已经实现好的工具和命令,如何把这些命令融合到我们自己的框架中呢?

Command以全图命令为例。

public class MapFullExtentCommand : MapCommand
{
private ESRI.ArcGIS.SystemUI.ICommand _EsriCommand = null;
public MapFullExtentCommand(MapApplication pMapApplication)
:
base(pMapApplication)
{
this._EsriCommand = new ControlsMapFullExtentCommandClass();
this._EsriCommand.OnCreate(pMapApplication.MapControl);
this.SetIcon(CommandIconSize.IconSize16, "MapTools/Res/MapFullExtent16.png");
}
public override void OnClick()
{
base.OnClick();
this._EsriCommand.OnClick();
}
}

我们初始化了一个AO定义的ControlsMapFullExtentCommandClass类,并与我们定义的MapApplication中的MapControl绑定。实现命令点击函数的时候,直接调用AO中定义的全图类的OnClick函数即可。

Tool以地图放大工具为例。

public class MapZoomInTool : MapTool
{
private readonly ESRI.ArcGIS.SystemUI.ITool _EsriTool = null;
public MapZoomInTool(MapApplication pMapApplication)
:
base(pMapApplication)
{
this._EsriTool = new ControlsMapZoomInToolClass();
this.SetIcon(CommandIconSize.IconSize16, "MapTools/Res/MapZoomIn16.png");
}
public override void OnActive()
{
base.OnActive();
(
this._EsriTool as ESRI.ArcGIS.SystemUI.ICommand).OnCreate(this.MapApplication.ActiveControl);
this.MapApplication.ActiveControl.CurrentTool = this._EsriTool;
}
public override void OnDeActivate()
{
base.OnDeActivate();
this.MapApplication.ActiveControl.CurrentTool = null;
}
}

我们初始化了一个AO定义的ControlsMapZoomInToolClass类,在激活该工具的时候和当前激活的Control绑定,如果是数据模式,会绑定MapControl,如果是布局模式,会绑定PageLayoutControl。并把定义的工具赋值给当前激活的Control的CurrentTool属性。失活的时候,把当前激活Control的CurrentTool设置为null。

这样我们就可以充分利用AO已经实现的各类命令和工具了。


5.5.扩展AO已有的Command

如果我们想在已有工具的基础上做些其他事情呢?例如在出图的时候,选择一个元素,在右侧显示该元素的属性面板。正常思路下,我们会点击PageLayoutControl,根据坐标去判断是否选中的Element,如果选中了,则把Element显示为选中状态,并获取该对象,在右侧显示其属性面板。

那是不是有更简单的方法?AO是有选择Element工具的,类名称为ControlsSelectToolClass,使用该工具可以使用鼠标进行点选、框选、删除、移动以及调整元素大小等操作,这些功能如何我们自己去写代码实现,将有非常大的工作量。如果能用这个工具,那就再好不过了。但我们需要解决两个问题。

1、AO中定义的选择类,在选择元素后,我们要捕捉到该动作,并获取选中的元素,显示元素的面板;

2、AO中定义的选择类,选择元素后,按下Delete键,会删除元素,这个逻辑我们需要控制,禁止删除MapFarme,并且删除其他元素的时候,要弹出提示是否确定删除对话框,确定后,再删除。

代码定义如下。

public class SelectTool : MapTool
{
private readonly LayoutDesignApplication _LayoutDesignAplication = null;
private readonly ControlsSelectToolClass _EsriTool = null;
public SelectTool(LayoutDesignApplication pLayoutDesignAplication)
:
base(pLayoutDesignAplication)
{
this._LayoutDesignAplication = pLayoutDesignAplication;
this._EsriTool = new ControlsSelectToolClass();
this.Caption = "Select";
this.Tooltip = "Select";
this.SetIcon(CommandIconSize.IconSize16, "Designs/Res/Select16.png");
}
public override void OnActive()
{
base.OnActive();
this._EsriTool.OnCreate(this._LayoutDesignAplication.ActiveControl);
}
public override void OnKeyDown(int keyCode, int shift)
{
if (keyCode == (int)ConsoleKey.Delete)
{
IGraphicsContainerSelect myGraphicsContainerSelect
= this._LayoutDesignAplication.PageLayoutControl.GraphicsContainer as IGraphicsContainerSelect;
IElement mySelectElement
= myGraphicsContainerSelect.DominantElement;
if (mySelectElement is IMapFrame == true)
{
return;
}
MessageBoxResult myMessageBoxResult
= MessageBox.Show("Is it determined to remove?", "Info", MessageBoxButton.YesNo);
if (myMessageBoxResult != MessageBoxResult.Yes)
{
return;
}
base.OnKeyDown(keyCode, shift);
this._EsriTool.OnKeyDown(keyCode, shift);
}
else
{
base.OnKeyDown(keyCode, shift);
}
}
public override void OnMouseDown(int button, int shift, int x, int y)
{
//如果是中键
if (button == 4)
{
this._LayoutDesignAplication.AxControlPan();
}
else
{
this._EsriTool.OnMouseDown(button, shift, x, y);
}
}
public override void OnMouseUp(int button, int shift, int x, int y)
{
base.OnMouseUp(button, shift, x, y);
this._EsriTool.OnMouseUp(button, shift, x, y);
IPageLayoutControl myPageLayoutControl
= this._LayoutDesignAplication.PageLayoutControl;
IPageLayout myPageLayout
= myPageLayoutControl.PageLayout;
IGraphicsContainerSelect myGraphicsContainerSelect
= myPageLayout as IGraphicsContainerSelect;
IElement myDominantElement
= myGraphicsContainerSelect.DominantElement;
}
public override void OnMouseMove(int button, int shift, int x, int y)
{
base.OnMouseMove(button, shift, x, y);
this._EsriTool.OnMouseMove(button, shift, x, y);
}
}

5.6. 完全自定义Command

完全自定义的Command就比较简单些了。例如我们定义异常当前选中的图层命令,定义如下。

public class LayerRemoveCommand : MapCommand
{
public LayerRemoveCommand(MapApplication pMapApplication)
:
base(pMapApplication)
{
this.Caption = "Remove";
this.IsEnabled = false;
this.MapApplication.OnActiveStateChanged += (x, y) =>
{
this.UpdateIsEnableState();
};
this.MapApplication.OnSelectTocObjectChanged += (x, y) =>
{
this.UpdateIsEnableState();
};
}
public override void OnClick()
{
base.OnClick();
ILayer myLayer
= this.MapApplication.SelectTocObject as ILayer;
if (myLayer == null)
{
MessageBox.Show(
"Please Select A Layer。");
return;
}
if (MessageBox.Show("Are You Sure Remove The Layer?", "Info", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
this.MapApplication.MapControl.ActiveView.FocusMap.DeleteLayer(myLayer);
this.MapApplication.TOCControl.Update();
}
}
private void UpdateIsEnableState()
{
if (this.MapApplication.ActivePattern == MapActivePattern.None)
{
this.IsEnabled = false;
return;
}
ILayer myLayer
= this.MapApplication.SelectTocObject as ILayer;
this.IsEnabled = (myLayer != null);
}
}

该定义就可以添加到图层的右键菜单上,用来移除当前选中的图层。我们不光可以通过调用宿主的属性和事件来控制自己是否可用,还可以加入很多逻辑判断。例如如果没有选择任何图层,则提示用户请选择一个图层。在移除的时候,可以提示用户是否确定移除等。

自定义的工具如下所示。

public class PointTextTool : MapTool
{
private LayoutDesignApplication _PLAplication = null;
public PointTextTool(LayoutDesignApplication pPLAplication)
:
base(pPLAplication)
{
this._PLAplication = pPLAplication;
this.Caption = "Insert Text";
this.Tooltip = "Insert Text";
this.SetIcon(CommandIconSize.IconSize16, "Designs/Res/Text16.png");
}
public override void OnMouseDown(int button, int shift, int x, int y)
{
base.OnMouseDown(button, shift, x, y);
IPoint myPagePoint
= this._PLAplication.PageLayoutControl.ToPagePoint(x, y);
IPageLayout myPageLayout
= this._PLAplication.PageLayoutControl.PageLayout;
IGraphicsContainerSelect myGraphicsContainerSelect
= myPageLayout as IGraphicsContainerSelect;
myGraphicsContainerSelect.UnselectAllElements();
PointTextItem myPointTextItem
= new PointTextItem();
MapFrameItem myMapFrameItem
= this._PLAplication.LayoutDesign.MapFrameItem;
myPointTextItem.X
= myPagePoint.X;
myPointTextItem.Y
= myPagePoint.Y;
this._PLAplication.LayoutDesign.PageLayoutItemList.Add(myPointTextItem);
myPointTextItem.Apply(
this._PLAplication);
(myPageLayout
as IActiveView).PartialRefresh(esriViewDrawPhase.esriViewGraphics, null, null);
this._PLAplication.CrruteTool = this._PLAplication.SelectTool;
}
}

实现工具的OnMouseDown函数,获取当前点击位置的坐标,实例化一个文本元素,添加到该位置。然后马上把工具切换到系统定义好的SelectTool上,这样会避免不小心点击两次,添加了两个文本元素,提高用户体验。

切换到SelectTool后,再次点击刚添加文本元素就可以选中该元素,这样右侧该元素的信息面板就展示出来了,完成了一个非常自然的操作过程。

通过命令和工具通过自己控制自己的状态、行为等,可以做到很细微的逻辑控制,并且这些操作会很好的封装在自己的代码中。这样系统功能可以通过实现各类Command和Tool不断扩展系统功能,但又不会影响系统的整体结构。


6.AO中已经实现的Command和Tool

AO中实现了二三百个命令和工具,我们常用的大概有几十个。可以在帮助中通过查看ICommand接口,查看到底有哪些实现。

也可以通过ArcMap查找,出现在ArcMap工具条上的按钮,大部分都能在此找到对应的类。



推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • C# WPF自定义按钮的方法
    本文介绍了在C# WPF中实现自定义按钮的方法,包括使用图片作为按钮背景、自定义鼠标进入效果、自定义按压效果和自定义禁用效果。通过创建CustomButton.cs类和ButtonStyles.xaml资源文件,设计按钮的Style并添加所需的依赖属性,可以实现自定义按钮的效果。示例代码在ButtonStyles.xaml中给出。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
author-avatar
遗忘的睡骨
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有