事件(Events)
GoJS涵盖了三种基本事件:DiagramEvents(图表事件)、InputEvents(输入事件)以及ChangedEvents(变更事件)。这一页我们讨论前两种事件;至于最后一种事件请见 Changed Events。
1、图表事件(Diagram Events)
DiagramEvent(图表事件)表示一般用户发起的对图表的改变。你可以通过调用Diagram.addDiagramListener注册图表事件处理程序。各个图表事件以名字区分。
目前定义了的图表事件名称包括:
-
"AnimationStarting", 一个动画将要开始
-
"AnimationFinished", 一个动画刚刚结束.
-
"BackgroundSingleClicked", 当我们用鼠标左键单机一个Diagram的背景而不是一个Part时
-
"BackgroundDoubleClicked", 当我们用鼠标左键双击一个Diagram的背景而不是一个Part时
-
"BackgroundContextClicked", 当我们用鼠标右键单机一个Diagram的背景而不是一个Part时
-
"ChangingSelection", 在Diagram.selection集合改变之前
-
"ChangedSelection", 在Diagram.selection集合改变之后
-
"ClipboardChanged", 当CommandHandler.copySelection改变了剪贴板(但是Diagram没有改变)
-
"ClipboardPasted", 当CommandHandler.pasteSelection从剪贴板复制了Part到Diagram中;新的Part被放在了Diagram.selection集合中
-
"DocumentBoundsChanged", 当Diagram.documentBounds改变了值
-
"ExternalObjectsDropped", 当DraggingTool 从另一个Diagram复制了Part到当前的Diagram; 新的Part被放在了Diagram.selection集合中
-
"InitialLayoutCompleted", 在对图表进行重大改动后第一个diagram布局被完成时
-
"LayoutCompleted", 当图表布局完成时
-
"LinkDrawn", 当LinkingTool完成创建一个新的Link(连接线时), 也就是DiagramEvent.subject
-
"LinkRelinked", 当RelinkingTool完成重新连接已存在的Link时, 也就是DiagramEvent.subject
-
"LinkReshaped", 当LinkReshapingTool完成改变一个Link的路线时, 也就是DiagramEvent.subject
-
"Modified", Diagram.isModified属性被设置为一个新的值时,不要在事件监听里修改Diagram或者它的Model
-
"PartCreated", 当ClickCreatingTool插入了一个新的Part, 也就是DiagramEvent.subject
-
"PartResized", 当ResizingTool调整了一个GraphObject的尺寸后, 也就是DiagramEvent.subject
-
"PartRotated", 当RotatingTool旋转了一个GraphObject后, 也就是DiagramEvent.subject
-
"ObjectSingleClicked", 当鼠标左键单机一个GraphObject时, 也就是DiagramEvent.subject
-
"ObjectDoubleClicked", 当鼠标左键双击一个GraphObject时, 也就是DiagramEvent.subject
-
"ObjectContextClicked", 当鼠标右键单击一个GraphObject时, 也就是DiagramEvent.subject
-
"SelectionMoved", 当DraggingTool移动了某些Parts; 被移动了的Parts就存在于Diagram.selection集合中
-
"SelectionCopied", 当DraggingTool复制了某些Parts; 被复制了的Parts就存在于Diagram.selection集合中
-
"SelectionDeleting", 在CommandHandler.deleteSelection删除Diagram.selection 的Parts之前
-
"SelectionDeleted", 在CommandHandler.deleteSelection删除了Diagram.selection后, Diagram.selection变空了
-
"SelectionGrouped", 在CommandHandler.groupSelection在Diagram.selection中创建了一个新的Group, Diagram.selection现在含有这个新的Group之后
-
"SelectionUngrouped", 在CommandHandler.ungroupSelection移除了选中的Group但保留了它的Parts作为新的Diagram.selection之后
-
"SubGraphCollapsed", 当CommandHandler.collapseSubGraph收缩了选中的Groups, 使它们的Parts不可见后
-
"SubGraphExpanded", 当CommandHandler.expandSubGraph扩大了选中的Groups, 使它们的Parts可见之后
-
"TextEdited", 当TextEditingTool完成了文本的就地编辑后;被修改了的TextBlock就是DiagramEvent.subject
-
"TreeCollapsed", 当CommandHandler.collapseTree用subtrees(子树)收缩了选中的节点, 使得子树的节点和连接线不可见之后
-
"TreeExpanded", 当CommandHandler.expandTree用subtrees(子树)展开了被选中的节点, 使得子树的节点和连接线可见之后
-
"ViewportBoundsChanged", 当Diagram.viewportBounds的值改变了之后
更多详细内容,请见文档DiagramEvent。
图表事件并不一定与鼠标/键盘或者触摸事件相对应。它们也不一定与图表模型的变化相对应——要跟踪这些变化,使用Model.addChangedListener或者Diagram.addModelChangedListener。当用户做了什么或者间接做了什么时,图表事件才会产生。
除了图表事件监听器之外,还有一些情况是,当发现一些变动足以表明一些属性是事件处理程序。由于这些事件并不一定需要与任何特殊的输入或者图表事件对应,座椅这些事件处理程序就会有特定于情况的自定义参数。
一个非常常见的事件属性是GraphObject.click,它指的是无论何时用户点击一个对象时调用的一个非空的函数。它最常用于为按钮指定行为,但是它与其他的点击事件属性,双击和右键点击,对于任何GraphObject都是有用的。
另外一个常见的事件属性是Part.selectionChanged,它指的是当Part.isSelected改变时调用的一个非空函数。在下面的示例中,事件处理函数有一个参数,该Part。由于函数能够检查Part.isSelected的当前值从而决定要做什么,所以不需要其余的参数。
模型的ChangedEvent事件比基于DiagramEvent更完成可靠。例如,当在代码里为图表增加一条连接线时,"LinkDrawn" 图表事件不会被调用。图表事件只有在用户用 LinkingTool 添加一条新的连接线时才被调用。另外,连接线没有被改变路线,因此Link.points不会被计算。事实上,创建一条新的连接线可能会使一个Layout失效,因此,所有的节点都可能会在不久后被移动。
有时候用户对图标做了一些改动,你需要更新数据库。 通常,你会想要执行一个Model ChangedEvent监听器, 通过调用Model.addChangedListener或者Diagram.addModelChangedListener, 然后把这些改变通知给模型层,再决定把哪些记录到数据库里。请查看关于Changed Events和Update Demo的讨论.
这个示例展示了几个图表事件:"ObjectSingleClicked"(单机对象),"BackgroundDoubleClicked"(双击背景), 和"ClipboardPasted"(剪贴板复制)。
例子:
function showMessage(s) {
document.getElementById("diagramEventsMsg").textContent = s;
}
// 为diagram添加一个“对象单机”事件
diagram.addDiagramListener("ObjectSingleClicked",
function(e) {
var part = e.subject.part;
//如果鼠标单机的不是连接线,则显示信息:"Clicked on" + 鼠标点击的目标的key值
if (!(part instanceof go.Link)) showMessage("Clicked on " + part.data.key);
}
);
//为diagram添加“背景双击”事件
diagram.addDiagramListener("BackgroundDoubleClicked",
function(e) {
//显示信息: “Double-clicked at” + 点击的点的坐标值
showMessage("Double-clicked at " + e.diagram.lastInput.documentPoint);
}
);
//为diagram添加“剪贴板复制粘贴”事件
diagram.addDiagramListener("ClipboardPasted",
function(e) {
//复制某个节点Part后,粘贴时,显示信息:“Pasted” + 复制粘贴的节点数量
showMessage("Pasted " + e.diagram.selection.count + " parts");
}
);
var nodeDataArray = [
{ key: "Alpha" },
{ key: "Beta", group: "Omega" },
{ key: "Gamma", group: "Omega" },
{ key: "Omega", isGroup: true },
{ key: "Delta" }
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" }, // 从Group外指向Group内
{ from: "Beta", to: "Gamma" }, // 这条连接线是Group内部的
{ from: "Omega", to: "Delta" } // 从Group指向一个节点
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
2、输入事件
当发生低级HTML DOM事件时,GoJS会将键盘/鼠标/触摸事件信息规范化转换为一个能够被传递给各种事件处理方法且为后面的检查而保存的新的 InputEvent(输入事件)。
一个输入事件,用InputEvent.key存放键盘事件, 用InputEvent.button存放鼠标事件, 用InputEvent.viewPoint存放鼠标和触摸事件, 用InputEvent.modifiers存放键盘和鼠标事件。
图表的事件处理程序同时也记录InputEvent.documentPoint,也就是鼠标事件发生时的文档坐标 InputEvent.viewPoint ,以及事件发生时的以毫秒为单位的时间戳 InputEvent.timestamp。
输入事件类还未特定类型的事件提供了很多很便利的属性。比如,InputEvent.control (如果按下控制键)和InputEvent.left (如果按下左/主鼠标按钮)。
一些工具在鼠标点击的点可以找到当前的GraphObject(图形对象)。这些图形对象就被记为InputEvent.targetObject。
3、高级输入事件
一些工具能检测一系列的输入事件以组成一些更抽象的用户事件。比如说,“点击”(鼠标迅速的按下并放开)和“悬停”(鼠标静止不动一段时间)。这些工具会为当前鼠标所在的点对应的GrapgObject调用一个事件处理程序(如果有的话)。这个事件处理程序被当做对象的一个属性值。它还会沿着 GraphObject.panel的链向上冒泡知道这个Part结束。这允许了一个“点击”事件在一个Panel上被申明并应用,即使实际的点击是发生在Panel内部的元素上的。如果鼠标所在的点没有对象,那时间就会发生在最外层的图表上。
类似的点击事件属性包括 GraphObject.click, GraphObject.doubleClick, 和 GraphObject.contextClick。即便没有GraphObject时,这些事件也会发生——事件会发生在图表的背景上发生:Diagram.click, Diagram.doubleClick, 和 Diagram.contextClick。这些都是你可以设置为事件处理程序的函数。这些事件是由鼠标事件和触摸事件引起的。
类似的鼠标移入事件属性包括GraphObject.mouseEnter, GraphObject.mouseOver和GraphObject.mouseLeave。但是只有Diagram.mouseOver会被应用到图表上。
类似的鼠标悬停事件属性包括 GraphObject.mouseHover和GraphObject.mouseHold。等价的图表属性是Diagram.mouseHover和Diagram.mouseHold.
拖拽操作也有对应的事件属性:GraphObject.mouseDragEnter, GraphObject.mouseDragLeave, 和GraphObject.mouseDrop. 他们适用于静止的对象,而不是被拖动中的对象。同时,他们也适用于触摸事件的拖拽,而不仅仅是鼠标事件的拖拽。
下面的示例展示了三个高级输入事件:点击节点、进入/离开group(组)。
function showMessage(s) {
document.getElementById("inputEventsMsg").textContent = s;
}
diagram.nodeTemplate =
$(go.Node, "Auto",
$(go.Shape, "Ellipse", { fill: "white" }),
$(go.TextBlock,
new go.Binding("text", "key")),
{ click: function(e, obj) { showMessage("Clicked on " + obj.part.data.key); } }
);
diagram.groupTemplate =
$(go.Group, "Vertical",
$(go.TextBlock,
{ alignment: go.Spot.Left, font: "Bold 12pt Sans-Serif" },
new go.Binding("text", "key")),
$(go.Panel, "Auto",
$(go.Shape, "RoundedRectangle",
{ name: "SHAPE",
parameter1: 14,
fill: "rgba(128,128,128,0.33)" }),
$(go.Placeholder, { padding: 5 })
),
{ mouseEnter: function(e, obj, prev) { // 改变group的背景画笔
var shape = obj.part.findObject("SHAPE");
if (shape) shape.fill = "red";
},
mouseLeave: function(e, obj, next) { // 回复原始的画笔
var shape = obj.part.findObject("SHAPE");
if (shape) shape.fill = "rgba(128,128,128,0.33)";
} });
var nodeDataArray = [
{ key: "Alpha" },
{ key: "Beta", group: "Omega" },
{ key: "Gamma", group: "Omega" },
{ key: "Omega", isGroup: true },
{ key: "Delta" }
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" }, // 从Group外指向Group内
{ from: "Beta", to: "Gamma" }, // 这条连接线是Group内部的
{ from: "Omega", to: "Delta" } // 从Group指向一个节点
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
3、点击与选中
这个示例同时展示了“点击”和“selectionChanged”事件:
function showMessage(s) {
document.getElementById("changeMethodsMsg").textContent = s;
}
diagram.nodeTemplate =
$(go.Node, "Auto",
{ selectionAdorned: false },
$(go.Shape, "Ellipse", { fill: "white" }),
$(go.TextBlock,
new go.Binding("text", "key")),
{
click: function(e, obj) { showMessage("Clicked on " + obj.part.data.key); },
selectionChanged: function(part) {
var shape = part.elt(0);
shape.fill = part.isSelected ? "red" : "white";
}
}
);
var nodeDataArray = [
{ key: "Alpha" }, { key: "Beta" }, { key: "Gamma" }
];
var linkDataArray = [
{ from: "Alpha", to: "Beta" },
{ from: "Beta", to: "Gamma" }
];
diagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
试着按下“Ctrl-A”可以全选所有的对象。注意 GraphObject.click事件属性与Part.selectionChanged事件属性的区别。当节点上发生什么事情时,两个方法都能被调用。GraphObject.click事件会在用户点击节点时发生,选中了节点。但是Part.selectionChanged事件在没有点击事件(或者任何鼠标事件)时就会发生——这是由于节点的一个属性值变了