为Acumatica创建TreeView

3
在Acumatica中,我希望为物料清单屏幕创建一个TreeView。这是当前屏幕的样子,我需要帮助填充左侧网格下方的树形结构。我希望树形结构的顶部值为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。现在我从另一个屏幕上把它拉出来,想知道它的工作原理。因为我希望Item显示在顶层,然后进入组成该Item的材料。如果选择了一个Item,我希望网格填充数据。我知道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>

下面是树形视图本身。

如果有人能帮我编写这棵树,那太好了,或者至少指点一下方向。


1
值得一提的是,这不是标准的Acumatica页面。 - Brendan
真的,这不是一个标准页面。它是一个自定义页面。我只是不确定在Acumatica中TreeView如何工作,或者至少在选择时如何填充数据。 - Dane
1个回答

4
为了在Acumatica中添加数据树,需要完成以下三个步骤:
  1. In the ASPX Page, declare the PXDataTreeMember in the PXDataSource:

    <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. Add the PXTreeView (Usually with a PXSplitContainer) with the linked FormView that will display the selected record.

    <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. Create the DataViews, DataViews delegate and Tree Actions.

    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。由于您的对象不会全部相同(BOM / MATL / ETC),因此需要能够从ID中找出相关信息。我建议的解决方案是将NodeID格式化为包含获取所需记录的所有信息(例如,BOM-BM00001_MATL-INVIDANDOTHERKEYFIELD)。这样,您就知道自己处在哪个级别并返回正确的子项(在Dataview委托nodes(int? nodeID)中)。


作为单个 ID,您指的是节点 ID 还是 BOM ID? - Dane
我将添加一个新的未绑定的 DAC,它将是通用的“BomTreeNode”。该DAC的关键字段将是所有其他相关关键字段的可解析聚合(例如:要查找MATL,您需要有BOMID + MATLID,因此使用单个字符串,您需要具有创建指向MATL的选择查询所需的所有信息)。 - Philippe
TreeNode被引用到哪里了?它与我的代码中的WebControls冲突,即使删除并添加PX.SM,它仍然显示相同的内容。 - Dane
受保护的虚拟 IEnumerable currentNode() { if (Nodes.Current != null) { var isNotRoot = Nodes.Current.BOMID != "0"; foreach (AMBomItem item in PXSelect>>>. Select(this, Nodes.Current.BOMID)) { yield return item; } } } - Dane
上面的代码说需要一个对象引用,但我不确定应该放在哪里。 - Dane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接