为 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 件事:

  1. 在 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>
    
  2. 添加 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>
    
  3. 创建数据视图、数据视图委托和树操作。

    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))。