基于插件技术的GIS应用框架(C# + ArcEngine9.3)(十一)
---------------------------其他一些关键的问题
至此,已将框架部分核心的东西做了一个大概的介绍,当然还有一些大家可能在开发过程中碰到的其他的一些问题,我也同样如此,这一节,就这些零散的问题跟进行一下探讨。
一、关于MapControl和PageLayoutControl的同步问题
这个问题其实是老生常谈了,几乎用ArcEngine做应用,不管你使用框架与否,都会碰到这个问题;网上很多代码贴出来用一个名为: ControlsSynchronizer 类来解决这个问题,前提是还得去重写esriControls里的OpenMxdDocument命令,这样一来,觉得有些繁琐,此外,即便这样,假如我用AddData命令来添加的地图,而不是通过OpenMxdDocument来触发MapReplace事件,那么又该如何同步呢?我使用了好几种办法,个人觉得进行硬拷贝相对来说比较可靠,当然,这纯属个人感觉。
private void CopyAndOverwriteMap()
{
IObjectCopy pObjectCopy;
IMap pMap, pToCopyMap;
object pCopiedMap, pToOverwriteMap;
pObjectCopy = new ObjectCopy() as IObjectCopy;
pToCopyMap = axMapControl.Map;
pCopiedMap = pObjectCopy.Copy(pToCopyMap);
pMap = axPageLayoutControl.ActiveView.FocusMap;
pToOverwriteMap = pMap;
pObjectCopy.Overwrite(pCopiedMap, ref pToOverwriteMap);
pMap = axPageLayoutControl.ActiveView.FocusMap;
(pMap as IActiveView).Extent = axMapControl.Extent;
axPageLayoutControl.Refresh();
}
private void tabMain_SelectedPageChanged(object sender,
DevExpress.XtraTab.TabPageChangedEventArgs e)
{
IObjectCopy pObjectCopy;
IMap PC, pOverMap, pToCopyMap, pMap;
object pToOverwritenMap, pIntover, pCopiedMap, pInt;
if (tabMain.SelectedTabPage == tsMap)
{
pObjectCopy = new ObjectCopy() as IObjectCopy;
pToCopyMap = axPageLayoutControl.ActiveView.FocusMap;
pToCopyMap.IsFramed = false;
pInt = pObjectCopy.Copy(pToCopyMap);
pCopiedMap = pInt as IMap;
pMap = axMapControl.ActiveView.FocusMap;
pIntover = pMap;
pObjectCopy.Overwrite(pCopiedMap, ref pIntover);
axMapControl.Refresh(esriViewDrawPhase.esriViewGeography, Type.Missing, Type.Missing);
}
else if (tabMain.SelectedTabPage == tsLayout)
{
pObjectCopy = new ObjectCopy() as IObjectCopy;
pToCopyMap = axMapControl.ActiveView.FocusMap;
pToCopyMap.IsFramed = true;
pCopiedMap = pObjectCopy.Copy(pToCopyMap);
pMap = axPageLayoutControl.ActiveView.FocusMap;
pToOverwritenMap = pMap;
pObjectCopy.Overwrite(pCopiedMap, ref pToOverwritenMap);
axPageLayoutControl.Refresh(esriViewDrawPhase.esriViewGeography, Type.Missing, Type.Missing);
}
}
二、插件的保存
实际上这个问题很好办,通过遍历框架上的所有UI对象,读取其Tag属性中保存的插件对象的命名空间,以及它的UI对象的各个属性,即可完整保存。这里,贴上一段代码,演示如何将子菜单递归保存。
private void SaveItemRecursion(BarSubItem item, XmlDocument xmldoc, XmlNode xmlnode)
{
int i, ilinkcnt;
ilinkcnt = item.ItemLinks.Count;
for (i = 0; i < ilinkcnt; i++)
{
BarItem subitem = item.ItemLinks[i].Item;
string name = subitem.Name;
string caption = item.ItemLinks[i].Caption;// subitem.Caption;
string tag = "";
string itemtype = "";
string sVisible = "1";
bool begingroup = item.ItemLinks[i].BeginGroup;
if (subitem.Tag != null)
{
tag = subitem.Tag.ToString();
}
if (tag == "")
{
continue;
}
if (subitem is BarSubItem) //如果是具有子菜单的菜单项插件
{
itemtype = "subitem";
switch ((subitem as BarSubItem).Visibility)
{
case BarItemVisibility.Always: sVisible = "1"; break;
case BarItemVisibility.OnlyInCustomizing: sVisible = "0"; break;
case BarItemVisibility.Never: sVisible = "-1"; break;
}
}
else if (subitem is BarButtonItem) //如果是一般按钮插件
{
itemtype = "command";
}
//如果是编辑器插件(DevExpress将编辑器进行了分类,本文论述的
//框架中,只使用了其中的Edit、Combobox和ColorEdit三种)
else if (subitem is BarEditItem)
{
BarEditItem edititem = (BarEditItem)subitem;
if (edititem.Edit.EditorTypeName == "TextEdit")
{
itemtype = "edit";
}
else if (edititem.Edit.EditorTypeName == "ComboBoxEdit")
{
itemtype = "combobox";
}
else if (edititem.Edit.EditorTypeName == "ColorEdit")
{
itemtype = "colorpicker";
}
else
{
itemtype = "undefine";
}
}
XmlElement subitemele = xmldoc.createElement_x_x("item");
subitemele.SetAttribute("type", itemtype);
subitemele.SetAttribute("namespace", tag);
subitemele.SetAttribute("caption", caption);
if (begingroup)
{
subitemele.SetAttribute("begingroup", "1");
}
subitemele.SetAttribute("visible", sVisible);
xmlnode.AppendChild(subitemele);
if (subitem is BarSubItem)
{
XmlNode xmlsubnode = subitemele as XmlNode;
BarSubItem subsubitem = (BarSubItem)subitem;
SaveItemRecursion(subsubitem, xmldoc, xmlsubnode); //递归保存
}
}
}
三、事件代理
1. IActiveViewEvents
前面的论述中,基本上都没有提及,想必很多人对这个问题是比较关心的,下面,我们就这个问题进行一下专门的论述。
在本文论述的框架中,有两类事件需要进行通知,一个是IActiveViewEvents,另外一个就是IWorkspaceEvents。C#提供事件代理机制来处理COM事件,在ArcEngine中也提供了相应的接口,关键是如何让框架进行回调呢?通过以下代码,我们可以对插件如何响应框架中的两类事件有所了解。
((esriCarto.IActiveViewEvents_Event)hook.MapControl.ActiveView).ItemAdded += new
esriCarto.IActiveViewEvents_ItemAddedEventHandler(Custom_ItemAdded);
((esriCarto.IActiveViewEvents_Event)hook.MapControl.ActiveView).ContentsCleared += new
esriCarto.IActiveViewEvents_ContentsClearedEventHandler(Custom_ContentsCleared);
((esriCarto.IActiveViewEvents_Event)hook.MapControl.ActiveView).ViewRefreshed += new
esriCarto.IActiveViewEvents_ViewRefreshedEventHandler(Custom_ViewRefreshed);
如上代码所示,我们要响应MapControl中的ItemAdded 、ContentsCleared 、ViewRefreshed 事件,只需要通过上述方式,直接把你自己的事件处理句柄交给事件代理(如上:Custom_ItemAdded、ContentsCleared、Custom_ViewRefreshed)去处理就OK了,需要注意的是,你自己定义的事件句柄,其参数必须与相应的事件接口中的参数定义保持一致。
2.IWorkspaceEvents
对于工作空间事件,处理相对来说就需要进行一下特别的处理,本文中因为没有使用IEngineEditor接口中的EditWorkspace,所以对于当前工作空间的事件处理,也需要自己来编写事件代理来进行。下面的接口定义了工作空间事件及其相应的事件代理。
namespace AxeMap.AxeMapEngine.Interface
{
public interface IAxWorkspaceEvents
{
/// <summary>
/// OnStartEditing
/// </summary>
event AxWorkspaceStartEditingEventsHandler OnStartEditing;
/// <summary>
/// OnStartEditing
/// </summary>
event AxWorkspaceStartEditOperationEventsHandler OnStartEditOperation;
/// <summary>
/// OnStartEditing
/// </summary>
event AxWorkspaceStopEditingEventsHandler OnStopEditing;
/// <summary>
/// OnStopEditOperation
/// </summary>
event AxWorkspaceStopEditOperationEventsHandler OnStopEditOperation;
/// <summary>
/// OnAbortEditOperation
/// </summary>
event AxWorkspaceAbortEditOperationEventsHandler OnAbortEditOperation;
/// <summary>
/// OnUndoEditOperation
/// </summary>
event AxWorkspaceUndoEditOperationEventsHandler OnUndoEditOperation;
/// <summary>
/// OnRedoEditOperation
/// </summary>
event AxWorkspaceRedoEditOperationEventsHandler OnRedoEditOperation;
void FireEvents_OnStartEditing();
void FireEvents_OnStartEditOperation();
void FireEvents_OnStopEditing(bool blSaveEdit);
void FireEvents_OnStopEditOperation(string sAction);
void FireEvents_OnAbortEditOperation();
void FireEvents_OnUndoEditOperation();
void FireEvents_OnRedoEditOperation();
}
}
IAxWorkspaceEvents的实现类
namespace AxeMap.AxeMapEngine.Implement
{
//定义事件代理
public delegate void AxWorkspaceStartEditingEventsHandler();
public delegate void AxWorkspaceStartEditOperationEventsHandler();
public delegate void AxWorkspaceStopEditingEventsHandler(bool blSaveEdit);
public delegate void AxWorkspaceStopEditOperationEventsHandler(string sAction);
public delegate void AxWorkspaceAbortEditOperationEventsHandler();
public delegate void AxWorkspaceUndoEditOperationEventsHandler();
public delegate void AxWorkspaceRedoEditOperationEventsHandler();
public class AxWorkspaceEvents : IAxWorkspaceEvents
{
public event AxWorkspaceStartEditingEventsHandler OnStartEditing;
public event AxWorkspaceStartEditOperationEventsHandler OnStartEditOperation;
public event AxWorkspaceStopEditingEventsHandler OnStopEditing;
public event AxWorkspaceStopEditOperationEventsHandler OnStopEditOperation;
public event AxWorkspaceAbortEditOperationEventsHandler OnAbortEditOperation;
public event AxWorkspaceUndoEditOperationEventsHandler OnUndoEditOperation;
public event AxWorkspaceRedoEditOperationEventsHandler OnRedoEditOperation;
public AxWorkspaceEvents()
{
}
public void FireEvents_OnStartEditing()
{
OnStartEditing();
}
public void FireEvents_OnStartEditOperation()
{
OnStartEditOperation();
}
public void FireEvents_OnStopEditing(bool blSaveEdit)
{
OnStopEditing(blSaveEdit);
}
public void FireEvents_OnStopEditOperation(string sAction)
{
OnStopEditOperation(sAction);
}
public void FireEvents_OnAbortEditOperation()
{
OnAbortEditOperation();
}
public void FireEvents_OnUndoEditOperation()
{
OnUndoEditOperation();
}
public void FireEvents_OnRedoEditOperation()
{
OnRedoEditOperation();
}
}
}