为 Acumatica 创建 TreeView
Creating TreeView for Acumatica
在 Acumatica 中,我想为 Material 屏幕的 Bill 创建一个 TreeView。 This is what the screen currently looks like 我需要帮助填充网格左侧的树。我希望树的最高值是 BOM 项目,然后构成该项目的材料将在每个项目的下方打开。到目前为止,这是我的代码。
<DataTrees>
<px:PXTreeDataMember TreeKeys="BOMID" TreeView="Nodes" />
<px:PXTreeDataMember TreeView="_AMBOMTree_Tree_" TreeKeys="OberNbr" />
<px:PXTreeDataMember TreeKeys="Key" TreeView="CacheTree" />
<px:PXTreeDataMember TreeKeys="Key" TreeView="EntityItems" />
</DataTrees>
这是针对数据树本身的。
<px:PXTreeSelector CommitChanges="True" SuppressLabel="False" ID="edGraphType" runat="server" DataField="GraphType" PopulateOnDemand="True"
ShowRootNode="False" TreeDataSourceID="ds" TreeDataMember="CacheTree" InitialExpandLevel="0" MinDropWidth="297" MaxDropWidth="500"
TextField="Name" Size="XL">
<Images>
<ParentImages Normal="tree@Folder" Selected="tree@FolderS" />
<LeafImages Normal="tree@Screen" Selected="tree@Screen" />
</Images>
<DataBindings>
<px:PXTreeItemBinding DataMember="CacheTree" TextField="Name" ValueField="SubKey" ImageUrlField="Icon" />
</DataBindings>
</px:PXTreeSelector>
这里是 TreeSelector。现在我从一个不同的屏幕上拉下来,想知道这一切是如何工作的。因为我希望项目显示在顶层,所以转到构成该项目的 Materials。如果选择了一个项目,我希望网格能够填充。我知道 DataField = GraphType 不正确,因为这是从不同的屏幕中提取的,但我不知道应该在此处的值。
<px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="500px" PopulateOnDemand="True" ShowRootNode="False"
ExpandDepth="1" AutoRepaint="true" Caption="Tree" AllowCollapse="true" PreserveExpanded="true">
<ToolBarItems>
<px:PXToolBarButton Tooltip="Reload Tree" ImageKey="Refresh">
<AutoCallBack Target="tree" Command="Refresh" />
</px:PXToolBarButton>
</ToolBarItems>
<AutoCallBack Target="grid" Command="Refresh" ActiveBehavior="True">
<Behavior RepaintControlsIDs="gridMatl" />
<Behavior RepaintControlsIDs="gridStep" />
<Behavior RepaintControlsIDs="gridTool" />
<Behavior RepaintControlsIDs="gridOvhd" />
</AutoCallBack>
<AutoSize Enabled="True" MinHeight="300" />
<DataBindings>
<px:PXTreeItemBinding DataMember="Nodes" TextField="Name" ValueField="AssignmentRouteID" ImageUrlField="Icon" />
</DataBindings>
</px:PXTreeView>
那么这就是树视图本身。
如果有人可以帮助我编写这棵树的代码,那就太好了,或者至少为我指明了正确的方向。
要在 Acumatica 中添加数据树,需要做 3 件事:
在 ASPX 页面中,在 PXDataSource 中声明 PXDataTreeMember:
<px:PXDataSource ID="ds" runat="server" Visible="True" PrimaryView="Document" SuspendUnloading="False" TypeName="PX.TreeDemo.TreeEntry">
<CallbackCommands>
<px:PXDSCallbackCommand CommitChanges="True" Name="MyAction" />
<px:PXDSCallbackCommand CommitChanges="True" Name="MyOtherAction" Visible="False" />
<px:PXDSCallbackCommand CommitChanges="True" Name="SomeOtherOtherAction" Visible="False" />
</CallbackCommands>
<DataTrees>
<px:PXTreeDataMember TreeView="Nodes" TreeKeys="NodeID" />
</DataTrees>
</px:PXDataSource>
添加 PXTreeView(通常与 PXSplitContainer 一起使用)和将显示所选记录的链接 FormView。
<px:PXSplitContainer runat="server" ID="sp1" SplitterPosition="300">
<AutoSize Enabled="true" Container="Window" />
<Template1>
<px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="180px"
ShowRootNode="False" AllowCollapse="False" Caption="Tree Demo" AutoRepaint="True"
SyncPosition="True" ExpandDepth="4" DataMember="Nodes" KeepPosition="True"
SyncPositionWithGraph="True" PreserveExpanded="True" PopulateOnDemand="true" SelectFirstNode="True">
<ToolBarItems>
<px:PXToolBarButton Text="Add Tree Node" Tooltip="Add Tree Node">
<AutoCallBack Command="AddNode" Enabled="True" Target="ds" />
<Images Normal="main@AddNew" />
</px:PXToolBarButton>
<px:PXToolBarButton Text="Delete Tree Node" Tooltip="Delete Tree Node">
<AutoCallBack Command="DeleteNode" Enabled="True" Target="ds" />
<Images Normal="main@Remove" />
</px:PXToolBarButton>
</ToolBarItems>
<AutoCallBack Target="formTree" Command="Refresh" Enabled="True" />
<DataBindings>
<px:PXTreeItemBinding DataMember="Nodes" TextField="DisplayName" ValueField="NodeID" />
</DataBindings>
<AutoSize Enabled="True" />
</px:PXTreeView>
</Template1>
<Template2>
<px:PXFormView ID="formTree" runat="server" DataSourceID="ds" DataMember="CurrentNode"
Caption="Node Info" Width="100%" >
<Template>
<px:PXLayoutRule ID="PXLayoutRule1" runat="server" StartColumn="True" LabelsWidth="S" ControlSize="SM" />
<px:PXSelector ID="edNodeID" runat="server" DataField="NodeID" CommitChanges="True" AutoRefresh="True" />
</Template>
</px:PXFormView>
</Template2>
</px:PXSplitContainer>
创建数据视图、数据视图委托和树操作。
public PXSelect<TreeNode> Nodes;
public PXSelect<TreeNode,
Where<TreeNode.parentNodeID,
Equal<Optional<TreeNode.nodeID>>>> ChildNodes;
public PXSelect<TreeNode,
Where<TreeNode.nodeID,
Equal<Current<TreeNode.nodeID>>>> CurrentNode;
#endregion
#region Delegates
protected virtual IEnumerable nodes(
[PXInt]
int? nodeID
)
{
if (nodeID == null)
{
yield return new TreeNode()
{
ParentNodeID = 0,
NodeID = 0
};
}
else
{
foreach (TreeNode node in ChildNodes.Select(nodeID))
{
yield return node;
}
}
}
protected virtual IEnumerable currentNode()
{
if (Nodes.Current != null)
{
var isNotRoot = Nodes.Current.NodeID != 0;
//Situation where we would want to limit the recursion to one
AddNode.SetEnabled(!isNotRoot);
DeleteNode.SetEnabled(isNotRoot);
Caches[typeof(TreeNode)].AllowInsert = isNotRoot;
Caches[typeof(TreeNode)].AllowDelete = isNotRoot;
Caches[typeof(TreeNode)].AllowUpdate = isNotRoot;
foreach (TreeNode item in PXSelect<TreeNode,
Where<TreeNode.nodeID,
Equal<Required<TreeNode.nodeID>>>>.
Select(this, Nodes.Current.NodeID))
{
yield return item;
}
}
}
public PXAction<NodeSetup> AddNode;
[PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)]
[PXButton()]
public virtual IEnumerable addNode(PXAdapter adapter)
{
var selectedNode = Nodes.Current;
if (selectedNode.ParentNodeID == 0)
{
var inserted = (TreeNode)Caches[typeof(TreeNode)].Insert(new TreeNode
{
ParentNodeID = Nodes.Current.NodeID
});
inserted.TempChildID = inserted.NodeID;
inserted.TempParentID = inserted.ParentNodeID;
Nodes.Cache.ActiveRow = inserted;
}
return adapter.Get();
}
public PXAction<NodeSetup> DeleteNode;
[PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)]
[PXButton()]
public virtual IEnumerable deleteNode(PXAdapter adapter)
{
var selectedNode = Nodes.Current;
if(selectedNode.NodeID != 0)
{
if(selectedNode.ParentNodeID == 0)
{
var childrenNodes = ChildNodes
.Select(selectedNode.NodeID)
.Select(br => (TreeNode)br).ToList();
if (childrenNodes.Any())
{
if (Document.Ask(Messages.ValidationDeleteChildren, MessageButtons.YesNo) == WebDialogResult.Yes)
{
foreach(var childrenNode in childrenNodes)
{
Caches[typeof(TreeNode)].Delete(childrenNode);
}
Caches[typeof(TreeNode)].Delete(selectedNode);
}
}
else
{
Caches[typeof(TreeNode)].Delete(selectedNode);
}
}
else
{
Caches[typeof(TreeNode)].Delete(selectedNode);
}
}
return adapter.Get();
}
解决该问题的最简单方法通常是使用单个 ID。因为您的 objects 不会完全相同(BOM/MATL/ETC),您需要能够从 ID 中找出它。我建议的解决方案是格式化 NodeID 以包含获取所需记录所需的所有信息 (E.I.BOM-BM00001_MATL-INVIDANDOTHERKEYFIELD)。这样你就会知道你是什么级别和return正确的children(在Dataview委托中nodes(int? nodeID)
)。
在 Acumatica 中,我想为 Material 屏幕的 Bill 创建一个 TreeView。 This is what the screen currently looks like 我需要帮助填充网格左侧的树。我希望树的最高值是 BOM 项目,然后构成该项目的材料将在每个项目的下方打开。到目前为止,这是我的代码。
<DataTrees>
<px:PXTreeDataMember TreeKeys="BOMID" TreeView="Nodes" />
<px:PXTreeDataMember TreeView="_AMBOMTree_Tree_" TreeKeys="OberNbr" />
<px:PXTreeDataMember TreeKeys="Key" TreeView="CacheTree" />
<px:PXTreeDataMember TreeKeys="Key" TreeView="EntityItems" />
</DataTrees>
这是针对数据树本身的。
<px:PXTreeSelector CommitChanges="True" SuppressLabel="False" ID="edGraphType" runat="server" DataField="GraphType" PopulateOnDemand="True"
ShowRootNode="False" TreeDataSourceID="ds" TreeDataMember="CacheTree" InitialExpandLevel="0" MinDropWidth="297" MaxDropWidth="500"
TextField="Name" Size="XL">
<Images>
<ParentImages Normal="tree@Folder" Selected="tree@FolderS" />
<LeafImages Normal="tree@Screen" Selected="tree@Screen" />
</Images>
<DataBindings>
<px:PXTreeItemBinding DataMember="CacheTree" TextField="Name" ValueField="SubKey" ImageUrlField="Icon" />
</DataBindings>
</px:PXTreeSelector>
这里是 TreeSelector。现在我从一个不同的屏幕上拉下来,想知道这一切是如何工作的。因为我希望项目显示在顶层,所以转到构成该项目的 Materials。如果选择了一个项目,我希望网格能够填充。我知道 DataField = GraphType 不正确,因为这是从不同的屏幕中提取的,但我不知道应该在此处的值。
<px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="500px" PopulateOnDemand="True" ShowRootNode="False"
ExpandDepth="1" AutoRepaint="true" Caption="Tree" AllowCollapse="true" PreserveExpanded="true">
<ToolBarItems>
<px:PXToolBarButton Tooltip="Reload Tree" ImageKey="Refresh">
<AutoCallBack Target="tree" Command="Refresh" />
</px:PXToolBarButton>
</ToolBarItems>
<AutoCallBack Target="grid" Command="Refresh" ActiveBehavior="True">
<Behavior RepaintControlsIDs="gridMatl" />
<Behavior RepaintControlsIDs="gridStep" />
<Behavior RepaintControlsIDs="gridTool" />
<Behavior RepaintControlsIDs="gridOvhd" />
</AutoCallBack>
<AutoSize Enabled="True" MinHeight="300" />
<DataBindings>
<px:PXTreeItemBinding DataMember="Nodes" TextField="Name" ValueField="AssignmentRouteID" ImageUrlField="Icon" />
</DataBindings>
</px:PXTreeView>
那么这就是树视图本身。
如果有人可以帮助我编写这棵树的代码,那就太好了,或者至少为我指明了正确的方向。
要在 Acumatica 中添加数据树,需要做 3 件事:
在 ASPX 页面中,在 PXDataSource 中声明 PXDataTreeMember:
<px:PXDataSource ID="ds" runat="server" Visible="True" PrimaryView="Document" SuspendUnloading="False" TypeName="PX.TreeDemo.TreeEntry"> <CallbackCommands> <px:PXDSCallbackCommand CommitChanges="True" Name="MyAction" /> <px:PXDSCallbackCommand CommitChanges="True" Name="MyOtherAction" Visible="False" /> <px:PXDSCallbackCommand CommitChanges="True" Name="SomeOtherOtherAction" Visible="False" /> </CallbackCommands> <DataTrees> <px:PXTreeDataMember TreeView="Nodes" TreeKeys="NodeID" /> </DataTrees> </px:PXDataSource>
添加 PXTreeView(通常与 PXSplitContainer 一起使用)和将显示所选记录的链接 FormView。
<px:PXSplitContainer runat="server" ID="sp1" SplitterPosition="300"> <AutoSize Enabled="true" Container="Window" /> <Template1> <px:PXTreeView ID="tree" runat="server" DataSourceID="ds" Height="180px" ShowRootNode="False" AllowCollapse="False" Caption="Tree Demo" AutoRepaint="True" SyncPosition="True" ExpandDepth="4" DataMember="Nodes" KeepPosition="True" SyncPositionWithGraph="True" PreserveExpanded="True" PopulateOnDemand="true" SelectFirstNode="True"> <ToolBarItems> <px:PXToolBarButton Text="Add Tree Node" Tooltip="Add Tree Node"> <AutoCallBack Command="AddNode" Enabled="True" Target="ds" /> <Images Normal="main@AddNew" /> </px:PXToolBarButton> <px:PXToolBarButton Text="Delete Tree Node" Tooltip="Delete Tree Node"> <AutoCallBack Command="DeleteNode" Enabled="True" Target="ds" /> <Images Normal="main@Remove" /> </px:PXToolBarButton> </ToolBarItems> <AutoCallBack Target="formTree" Command="Refresh" Enabled="True" /> <DataBindings> <px:PXTreeItemBinding DataMember="Nodes" TextField="DisplayName" ValueField="NodeID" /> </DataBindings> <AutoSize Enabled="True" /> </px:PXTreeView> </Template1> <Template2> <px:PXFormView ID="formTree" runat="server" DataSourceID="ds" DataMember="CurrentNode" Caption="Node Info" Width="100%" > <Template> <px:PXLayoutRule ID="PXLayoutRule1" runat="server" StartColumn="True" LabelsWidth="S" ControlSize="SM" /> <px:PXSelector ID="edNodeID" runat="server" DataField="NodeID" CommitChanges="True" AutoRefresh="True" /> </Template> </px:PXFormView> </Template2> </px:PXSplitContainer>
创建数据视图、数据视图委托和树操作。
public PXSelect<TreeNode> Nodes; public PXSelect<TreeNode, Where<TreeNode.parentNodeID, Equal<Optional<TreeNode.nodeID>>>> ChildNodes; public PXSelect<TreeNode, Where<TreeNode.nodeID, Equal<Current<TreeNode.nodeID>>>> CurrentNode; #endregion #region Delegates protected virtual IEnumerable nodes( [PXInt] int? nodeID ) { if (nodeID == null) { yield return new TreeNode() { ParentNodeID = 0, NodeID = 0 }; } else { foreach (TreeNode node in ChildNodes.Select(nodeID)) { yield return node; } } } protected virtual IEnumerable currentNode() { if (Nodes.Current != null) { var isNotRoot = Nodes.Current.NodeID != 0; //Situation where we would want to limit the recursion to one AddNode.SetEnabled(!isNotRoot); DeleteNode.SetEnabled(isNotRoot); Caches[typeof(TreeNode)].AllowInsert = isNotRoot; Caches[typeof(TreeNode)].AllowDelete = isNotRoot; Caches[typeof(TreeNode)].AllowUpdate = isNotRoot; foreach (TreeNode item in PXSelect<TreeNode, Where<TreeNode.nodeID, Equal<Required<TreeNode.nodeID>>>>. Select(this, Nodes.Current.NodeID)) { yield return item; } } } public PXAction<NodeSetup> AddNode; [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)] [PXButton()] public virtual IEnumerable addNode(PXAdapter adapter) { var selectedNode = Nodes.Current; if (selectedNode.ParentNodeID == 0) { var inserted = (TreeNode)Caches[typeof(TreeNode)].Insert(new TreeNode { ParentNodeID = Nodes.Current.NodeID }); inserted.TempChildID = inserted.NodeID; inserted.TempParentID = inserted.ParentNodeID; Nodes.Cache.ActiveRow = inserted; } return adapter.Get(); } public PXAction<NodeSetup> DeleteNode; [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Enabled = true)] [PXButton()] public virtual IEnumerable deleteNode(PXAdapter adapter) { var selectedNode = Nodes.Current; if(selectedNode.NodeID != 0) { if(selectedNode.ParentNodeID == 0) { var childrenNodes = ChildNodes .Select(selectedNode.NodeID) .Select(br => (TreeNode)br).ToList(); if (childrenNodes.Any()) { if (Document.Ask(Messages.ValidationDeleteChildren, MessageButtons.YesNo) == WebDialogResult.Yes) { foreach(var childrenNode in childrenNodes) { Caches[typeof(TreeNode)].Delete(childrenNode); } Caches[typeof(TreeNode)].Delete(selectedNode); } } else { Caches[typeof(TreeNode)].Delete(selectedNode); } } else { Caches[typeof(TreeNode)].Delete(selectedNode); } } return adapter.Get(); }
解决该问题的最简单方法通常是使用单个 ID。因为您的 objects 不会完全相同(BOM/MATL/ETC),您需要能够从 ID 中找出它。我建议的解决方案是格式化 NodeID 以包含获取所需记录所需的所有信息 (E.I.BOM-BM00001_MATL-INVIDANDOTHERKEYFIELD)。这样你就会知道你是什么级别和return正确的children(在Dataview委托中nodes(int? nodeID)
)。