使用 GoJS 创建端口的问题
Issue in creating ports with GoJS
我正在测试 Javascript 的 GoJS 库。
我希望在组的两侧创建端口。
我附上了程序的可执行文件。
当我从调色板创建一个新对象时,右键单击该对象,然后 select "Add left port",调试消息显示变量 "arr" 已声明 "undefined" 在执行时。 (程序第 111 和 112 行)。
感谢您的帮助。
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dynamic Ports</title>
<meta name="description" content="Nodes with varying lists of ports on each of four sides." />
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="../release/go-debug.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<!-- LL <script>goCode("diagramEvents", 600, 200)</script>-->
<script src="/Users/leonlevy/gojs/site/extensionsTS/ResizeMultipleScript.js"></script>
<span id="diagramEventsMsg" style="color: red"></span>
<span id="changeMethodsMsg" style="color: red"></span>
<span id="BackgroundDoubleClicked" style="color: red"></span>
<p>
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; //for conciseness in defining node templates
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowDrop: true, // from Palette
// what to do when a drag-drop occurs in the Diagram's background
mouseDrop: function(e) { finishDrop(e, null); },
// layout: null, // Diagram has no layout
/* $(go.GridLayout,
{ wrappingWidth: Infinity, alignment: go.GridLayout.Position, cellSize: new go.Size(1, 1) }),*/
initialContentAlignment: go.Spot.Center,
"commandHandler.archetypeGroupData": { isGroup: true, category: "OfGroups" },
"undoManager.isEnabled": true
});
// LL
function showMessage(s) {
document.getElementById("diagramEventsMsg").textContent = s;
}
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener("Modified", function(e) {
var button = document.getElementById("SaveButton");
if (button) button.disabled = !myDiagram.isModified;
var idx = document.title.indexOf("*");
if (myDiagram.isModified) {
if (idx < 0) document.title += "*";
} else {
if (idx >= 0) document.title = document.title.substr(0, idx);
}
});
myDiagram.addDiagramListener("ObjectSingleClicked",
function(e) {
var part = e.subject.part;
var heightPart = part.actualBounds.height;
var widthPart = part.actualBounds.width;
var xPart = part.actualBounds.position.x;
var yPart = part.actualBounds.position.y;
var xMouseClic = myDiagram.lastInput.documentPoint.x;
var yMouseClic = myDiagram.lastInput.documentPoint.y;
var xPartMiddle = xPart+ widthPart/2;
var side = xMouseClic<= xPartMiddle ? "Left" : "Right";
if (!(part instanceof go.Link)) {console.log("hello leon");}
});
myDiagram.addDiagramListener("BackgroundSingleClicked",
function(e) {
showMessage("Clicked on background " + myDiagram.lastInput.documentPoint);
//console.log("hello leon");}
});
myDiagram.addDiagramListener("BackgroundDoubleClicked", // LL KO
function(e) {
showMessage("Double-clicked at " );//+ e.myDiagram.lastInput.documentPoint);
//showMessage("Clicked on " + myDiagram.lastInput.documentPoint);
});
myDiagram.addDiagramListener("ClipboardPasted",
function(e) { showMessage("Pasted " + e.myDiagram.selection.count + " parts"); });
// LL end
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
return $("ContextMenuButton",
$(go.TextBlock, text),
{ click: action },
//{showMessage( "button: " + myDiagram.lastInput.button);
// don't bother with binding GraphObject.visible if there's no predicate
visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.myDiagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
//}
}
var nodeMenu = // context menu for each Node
$(go.Adornment, "Horizontal",
makeButton("Add left port",
function (e, obj) { addPort("left"); }),
makeButton("Add right port",
function (e, obj) { addPort("right"); }),
); //Leon end nodeM
// Add a port to the specified side of the selected nodes.
function addPort(side) {
myDiagram.startTransaction("addPort");
myDiagram.selection.each(function(node) {
// skip any selected Links
if (!(node instanceof go.Node)) return;
// compute the next available index number for the side
var i = 0;
while (node.findPort(side + i.toString()) !== node) i++;
// now this new port name is unique within the whole Node because of the side prefix
var name = side + i.toString();
// get the Array of port data to be modified
var arr = node.data[side + "Array"];
showMessage ("node: " + node + "; name: " + name + ";arr: " + arr + ";node.data: " + node.data);
if (arr) {
// create a new port data object
var newportdata = {
portId: name,
portColor: go.Brush.randomColor()
// if you add port data properties here, you should copy them in copyPortData above
};
// and add it to the Array of port data
myDiagram.model.insertArrayItem(arr, -1, newportdata);
}
});
myDiagram.commitTransaction("addPort");
}
var portSize = new go.Size(8, 8);
// this function is used to highlight a Group that the selection may be dropped into
function highlightGroup(e, grp, show) {
if (!grp) return; showMessage("ok")
e.handled = true;
if (show) {
// cannot depend on the grp.diagram.selection in the case of external drag-and-drops;
// instead depend on the DraggingTool.draggedParts or .copiedParts
var tool = grp.diagram.toolManager.draggingTool;
var map = tool.draggedParts || tool.copiedParts; // this is a Map
// now we can check to see if the Group will accept membership of the dragged Parts
if (grp.canAddMembers(map.toKeySet())) {
grp.isHighlighted = true;
return;
}
}
grp.isHighlighted = false;
}
// Upon a drop onto a Group, we try to add the selection as members of the Group.
// Upon a drop onto the background, or onto a top-level Node, make selection top-level.
// If this is OK, we're done; otherwise we cancel the operation to rollback everything.
function finishDrop(e, grp) {
var ok = (grp !== null
? grp.addMembers(grp.diagram.selection, true)
: e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true));
if (!ok) e.diagram.currentTool.doCancel();
}
var portMenu = // context menu for each port
$(go.Adornment, "Vertical",
makeButton("Remove port",
// in the click event handler, the obj.part is the Adornment;
// its adornedObject is the port
function (e, obj) { removePort(obj.part.adornedObject); }),
makeButton("Change color",
function (e, obj) { changeColor(obj.part.adornedObject); }),
makeButton("Remove side ports",
function (e, obj) { removeAll(obj.part.adornedObject); })
);
// support double-clicking in the background to add a copy of this data as a node
myDiagram.toolManager.clickCreatingTool.archetypeNodeData = {
name: "Unit",
leftArray: [],
rightArray: [],
topArray: [],
bottomArray: []
};
myDiagram.groupTemplateMap.add("OfGroups",
$(go.Group, "Table",
{ locationObjectName: "BODY",
locationSpot: go.Spot.Center,
selectionObjectName: "BODY",
contextMenu: nodeMenu
},
{
background: "transparent",
resizable: true,
// highlight when dragging into the Group
mouseDragEnter: function(e, grp, prev) { highlightGroup(e, grp, true); },
mouseDragLeave: function(e, grp, next) { highlightGroup(e, grp, false); },
//computesBoundsAfterDrag: true,
mouseDrop: finishDrop,
resizable: true, resizeObjectName: "SHAPE"
//, minSize: new go.Size(36, 46)
},
new go.Binding("background", "isHighlighted", function(h) { return h ? "rgba(0,0,255,0.)" : "transparent"; }).ofObject(),
// save the modified size in the model node data
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Rectangle",
{ fill: null, stroke: "rgba(0,50,255,1)", strokeWidth: 3, name: "SHAPE"}),
$(go.Panel, "Vertical", {alignment: go.Spot.Top},// title above Placeholder
$(go.Panel, "Horizontal", // button next to TextBlock
{ stretch: go.GraphObject.Horizontal, background: "rgba(0,0,255,0.1)" },
$("SubGraphExpanderButton",
{ alignment: go.Spot.Right, margin: 5 }),
$(go.TextBlock,
{ //text: "verticalAlignment: Top", verticalAlignment: go.Spot.Top,
alignment: go.Spot.Left,
editable: true,
margin: 5,
font: "bold 18px sans-serif",
opacity: 0.75,
stroke: "#404040"
},
new go.Binding("text", "text").makeTwoWay())
), // end Horizontal Panel
$(go.Placeholder,
{ padding: 5, alignment: go.Spot.TopLeft })
), // end Vertical Panel
// the Panel holding the left port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.leftArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "leftArray"),
{ row: 1, column: 0,
itemTemplate:
$(go.Panel,
{ _side: "left", // internal property to make it easier to tell which side it's on
fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu },
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{ stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1,0) },
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
// the Panel holding the right port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.rightArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "rightArray"),
{ row: 1, column: 2,
itemTemplate:
$(go.Panel,
{ _side: "right",
fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu },
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{ stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0) },
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
)
); // end groupTemplateMap.add("OfGroups"
// initialize the Palette and its contents
myPalette =
$(go.Palette, "myPaletteDiv",
{
groupTemplateMap: myDiagram.groupTemplateMap,
layout: $(go.GridLayout, { wrappingColumn: 1, alignment: go.GridLayout.Position })
});
myPalette.nodeTemplate =
$(go.Node, "Horizontal",
$(go.Shape,
{ width: 10, height: 10, fill: "white" },
new go.Binding("fill", "color")),
$(go.TextBlock,
new go.Binding("text", "color"))
);
myPalette.model = new go.GraphLinksModel([
{ key: 101, text: 'UNIT', isGroup: true, isSubProcess: true,
category: "OfGroups", isAdHoc: true, loc: '0 0' },
]);
}
</script>
</head>
<body onload="init()">
<!-- LL -->
<body onload="goIntro()">
<div id="container" class="container-fluid">
<div id="content">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 100px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 480px; border: solid 1px black"></div>
</div>
<p>
See the <a href="../intro/ports.html">Ports Intro page</a> for an explanation of GoJS ports.
</p>
<textarea id="mySavedModel" style="width:100%;height:250px">
</textarea>
</div>
</div>
</body>
</html>
您需要确保节点绑定了正确的数据。特别是,数据对象应该有一个 leftArray
属性 ,它是一个包含端口描述符对象的数组。 (而且它似乎也期望 rightArray
属性。)您可以通过查看源 属性 名称来确定每个 Node 的预期属性节点模板中的每个 Binding。
您可以通过查看 Panel.itemTemplate 在其数据 [=16] 中使用的属性来确定任一数据数组中每个端口描述符的预期属性=]绑定s.
我正在测试 Javascript 的 GoJS 库。 我希望在组的两侧创建端口。 我附上了程序的可执行文件。
当我从调色板创建一个新对象时,右键单击该对象,然后 select "Add left port",调试消息显示变量 "arr" 已声明 "undefined" 在执行时。 (程序第 111 和 112 行)。
感谢您的帮助。
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dynamic Ports</title>
<meta name="description" content="Nodes with varying lists of ports on each of four sides." />
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="../release/go-debug.js"></script>
<script src="../assets/js/goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<!-- LL <script>goCode("diagramEvents", 600, 200)</script>-->
<script src="/Users/leonlevy/gojs/site/extensionsTS/ResizeMultipleScript.js"></script>
<span id="diagramEventsMsg" style="color: red"></span>
<span id="changeMethodsMsg" style="color: red"></span>
<span id="BackgroundDoubleClicked" style="color: red"></span>
<p>
<script id="code">
function init() {
if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this
var $ = go.GraphObject.make; //for conciseness in defining node templates
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
allowDrop: true, // from Palette
// what to do when a drag-drop occurs in the Diagram's background
mouseDrop: function(e) { finishDrop(e, null); },
// layout: null, // Diagram has no layout
/* $(go.GridLayout,
{ wrappingWidth: Infinity, alignment: go.GridLayout.Position, cellSize: new go.Size(1, 1) }),*/
initialContentAlignment: go.Spot.Center,
"commandHandler.archetypeGroupData": { isGroup: true, category: "OfGroups" },
"undoManager.isEnabled": true
});
// LL
function showMessage(s) {
document.getElementById("diagramEventsMsg").textContent = s;
}
// when the document is modified, add a "*" to the title and enable the "Save" button
myDiagram.addDiagramListener("Modified", function(e) {
var button = document.getElementById("SaveButton");
if (button) button.disabled = !myDiagram.isModified;
var idx = document.title.indexOf("*");
if (myDiagram.isModified) {
if (idx < 0) document.title += "*";
} else {
if (idx >= 0) document.title = document.title.substr(0, idx);
}
});
myDiagram.addDiagramListener("ObjectSingleClicked",
function(e) {
var part = e.subject.part;
var heightPart = part.actualBounds.height;
var widthPart = part.actualBounds.width;
var xPart = part.actualBounds.position.x;
var yPart = part.actualBounds.position.y;
var xMouseClic = myDiagram.lastInput.documentPoint.x;
var yMouseClic = myDiagram.lastInput.documentPoint.y;
var xPartMiddle = xPart+ widthPart/2;
var side = xMouseClic<= xPartMiddle ? "Left" : "Right";
if (!(part instanceof go.Link)) {console.log("hello leon");}
});
myDiagram.addDiagramListener("BackgroundSingleClicked",
function(e) {
showMessage("Clicked on background " + myDiagram.lastInput.documentPoint);
//console.log("hello leon");}
});
myDiagram.addDiagramListener("BackgroundDoubleClicked", // LL KO
function(e) {
showMessage("Double-clicked at " );//+ e.myDiagram.lastInput.documentPoint);
//showMessage("Clicked on " + myDiagram.lastInput.documentPoint);
});
myDiagram.addDiagramListener("ClipboardPasted",
function(e) { showMessage("Pasted " + e.myDiagram.selection.count + " parts"); });
// LL end
// To simplify this code we define a function for creating a context menu button:
function makeButton(text, action, visiblePredicate) {
return $("ContextMenuButton",
$(go.TextBlock, text),
{ click: action },
//{showMessage( "button: " + myDiagram.lastInput.button);
// don't bother with binding GraphObject.visible if there's no predicate
visiblePredicate ? new go.Binding("visible", "", function(o, e) { return o.myDiagram ? visiblePredicate(o, e) : false; }).ofObject() : {});
//}
}
var nodeMenu = // context menu for each Node
$(go.Adornment, "Horizontal",
makeButton("Add left port",
function (e, obj) { addPort("left"); }),
makeButton("Add right port",
function (e, obj) { addPort("right"); }),
); //Leon end nodeM
// Add a port to the specified side of the selected nodes.
function addPort(side) {
myDiagram.startTransaction("addPort");
myDiagram.selection.each(function(node) {
// skip any selected Links
if (!(node instanceof go.Node)) return;
// compute the next available index number for the side
var i = 0;
while (node.findPort(side + i.toString()) !== node) i++;
// now this new port name is unique within the whole Node because of the side prefix
var name = side + i.toString();
// get the Array of port data to be modified
var arr = node.data[side + "Array"];
showMessage ("node: " + node + "; name: " + name + ";arr: " + arr + ";node.data: " + node.data);
if (arr) {
// create a new port data object
var newportdata = {
portId: name,
portColor: go.Brush.randomColor()
// if you add port data properties here, you should copy them in copyPortData above
};
// and add it to the Array of port data
myDiagram.model.insertArrayItem(arr, -1, newportdata);
}
});
myDiagram.commitTransaction("addPort");
}
var portSize = new go.Size(8, 8);
// this function is used to highlight a Group that the selection may be dropped into
function highlightGroup(e, grp, show) {
if (!grp) return; showMessage("ok")
e.handled = true;
if (show) {
// cannot depend on the grp.diagram.selection in the case of external drag-and-drops;
// instead depend on the DraggingTool.draggedParts or .copiedParts
var tool = grp.diagram.toolManager.draggingTool;
var map = tool.draggedParts || tool.copiedParts; // this is a Map
// now we can check to see if the Group will accept membership of the dragged Parts
if (grp.canAddMembers(map.toKeySet())) {
grp.isHighlighted = true;
return;
}
}
grp.isHighlighted = false;
}
// Upon a drop onto a Group, we try to add the selection as members of the Group.
// Upon a drop onto the background, or onto a top-level Node, make selection top-level.
// If this is OK, we're done; otherwise we cancel the operation to rollback everything.
function finishDrop(e, grp) {
var ok = (grp !== null
? grp.addMembers(grp.diagram.selection, true)
: e.diagram.commandHandler.addTopLevelParts(e.diagram.selection, true));
if (!ok) e.diagram.currentTool.doCancel();
}
var portMenu = // context menu for each port
$(go.Adornment, "Vertical",
makeButton("Remove port",
// in the click event handler, the obj.part is the Adornment;
// its adornedObject is the port
function (e, obj) { removePort(obj.part.adornedObject); }),
makeButton("Change color",
function (e, obj) { changeColor(obj.part.adornedObject); }),
makeButton("Remove side ports",
function (e, obj) { removeAll(obj.part.adornedObject); })
);
// support double-clicking in the background to add a copy of this data as a node
myDiagram.toolManager.clickCreatingTool.archetypeNodeData = {
name: "Unit",
leftArray: [],
rightArray: [],
topArray: [],
bottomArray: []
};
myDiagram.groupTemplateMap.add("OfGroups",
$(go.Group, "Table",
{ locationObjectName: "BODY",
locationSpot: go.Spot.Center,
selectionObjectName: "BODY",
contextMenu: nodeMenu
},
{
background: "transparent",
resizable: true,
// highlight when dragging into the Group
mouseDragEnter: function(e, grp, prev) { highlightGroup(e, grp, true); },
mouseDragLeave: function(e, grp, next) { highlightGroup(e, grp, false); },
//computesBoundsAfterDrag: true,
mouseDrop: finishDrop,
resizable: true, resizeObjectName: "SHAPE"
//, minSize: new go.Size(36, 46)
},
new go.Binding("background", "isHighlighted", function(h) { return h ? "rgba(0,0,255,0.)" : "transparent"; }).ofObject(),
// save the modified size in the model node data
new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Rectangle",
{ fill: null, stroke: "rgba(0,50,255,1)", strokeWidth: 3, name: "SHAPE"}),
$(go.Panel, "Vertical", {alignment: go.Spot.Top},// title above Placeholder
$(go.Panel, "Horizontal", // button next to TextBlock
{ stretch: go.GraphObject.Horizontal, background: "rgba(0,0,255,0.1)" },
$("SubGraphExpanderButton",
{ alignment: go.Spot.Right, margin: 5 }),
$(go.TextBlock,
{ //text: "verticalAlignment: Top", verticalAlignment: go.Spot.Top,
alignment: go.Spot.Left,
editable: true,
margin: 5,
font: "bold 18px sans-serif",
opacity: 0.75,
stroke: "#404040"
},
new go.Binding("text", "text").makeTwoWay())
), // end Horizontal Panel
$(go.Placeholder,
{ padding: 5, alignment: go.Spot.TopLeft })
), // end Vertical Panel
// the Panel holding the left port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.leftArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "leftArray"),
{ row: 1, column: 0,
itemTemplate:
$(go.Panel,
{ _side: "left", // internal property to make it easier to tell which side it's on
fromSpot: go.Spot.Left, toSpot: go.Spot.Left,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu },
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{ stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1,0) },
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
// the Panel holding the right port elements, which are themselves Panels,
// created for each item in the itemArray, bound to data.rightArray
$(go.Panel, "Vertical",
new go.Binding("itemArray", "rightArray"),
{ row: 1, column: 2,
itemTemplate:
$(go.Panel,
{ _side: "right",
fromSpot: go.Spot.Right, toSpot: go.Spot.Right,
fromLinkable: true, toLinkable: true, cursor: "pointer",
contextMenu: portMenu },
new go.Binding("portId", "portId"),
$(go.Shape, "Rectangle",
{ stroke: null, strokeWidth: 0,
desiredSize: portSize,
margin: new go.Margin(1, 0) },
new go.Binding("fill", "portColor"))
) // end itemTemplate
}
), // end Vertical Panel
)
); // end groupTemplateMap.add("OfGroups"
// initialize the Palette and its contents
myPalette =
$(go.Palette, "myPaletteDiv",
{
groupTemplateMap: myDiagram.groupTemplateMap,
layout: $(go.GridLayout, { wrappingColumn: 1, alignment: go.GridLayout.Position })
});
myPalette.nodeTemplate =
$(go.Node, "Horizontal",
$(go.Shape,
{ width: 10, height: 10, fill: "white" },
new go.Binding("fill", "color")),
$(go.TextBlock,
new go.Binding("text", "color"))
);
myPalette.model = new go.GraphLinksModel([
{ key: 101, text: 'UNIT', isGroup: true, isSubProcess: true,
category: "OfGroups", isAdHoc: true, loc: '0 0' },
]);
}
</script>
</head>
<body onload="init()">
<!-- LL -->
<body onload="goIntro()">
<div id="container" class="container-fluid">
<div id="content">
<div id="sample">
<div style="width: 100%; display: flex; justify-content: space-between">
<div id="myPaletteDiv" style="width: 100px; margin-right: 2px; background-color: whitesmoke; border: solid 1px black"></div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 480px; border: solid 1px black"></div>
</div>
<p>
See the <a href="../intro/ports.html">Ports Intro page</a> for an explanation of GoJS ports.
</p>
<textarea id="mySavedModel" style="width:100%;height:250px">
</textarea>
</div>
</div>
</body>
</html>
您需要确保节点绑定了正确的数据。特别是,数据对象应该有一个 leftArray
属性 ,它是一个包含端口描述符对象的数组。 (而且它似乎也期望 rightArray
属性。)您可以通过查看源 属性 名称来确定每个 Node 的预期属性节点模板中的每个 Binding。
您可以通过查看 Panel.itemTemplate 在其数据 [=16] 中使用的属性来确定任一数据数组中每个端口描述符的预期属性=]绑定s.