嵌套视图中的3级列表

时间:2013-12-12 08:31:24

标签: c# asp.net

我有这些表PagesPageVersionsElementsOnPageVersions

我希望在一个可折叠的视图中同时在一个页面上显示它。

像这样:

  • Page 1

    • 第1版
    • 第2版
    • 第3版
    • 第4版
      • 元素1
      • 元素2
      • 元素3
  • 第2页

    • 第1版
    • 第2版
    • 第3版
    • 第4版
      • 元素1
      • 元素2
  • Page 3

    • 版本1
      • 元素1
  • 第4页

    • 版本1
      • 元素1

我不确定检索数据的最佳方法是什么,并且很容易在collapsaple布局中显示它。

我会做这样的事情:

  1. 一次性获取所有物品。 “select * from ElementsOnPageVersion e inner join PageVersions v on e.PageVersion = v.id inner join pages p on v.PageId = p.id”
  2. 遍历所有内容,并构建排序列表的层次结构,使其看起来像collapsaple布局。 PageLists [PagesObject],PagesObject有一个PageVersionObjects的排序列表,它有一个ElementObjects的排序列表。
  3. 遍历最终的PagesList列表构建页面。在这个迭代的页面转换列表中显示页面的版本,并在每个版本内部迭代遍历elementslist。
  4. 这就是我现在这样做的方式。但它似乎太沉重了,并且需要进行大量迭代。

    最好的方法是什么?

3 个答案:

答案 0 :(得分:1)

我考虑将您的三个不同项目投影到一个自引用列表中。每个项目都需要Id,Description和ParentId。我为此目的制作了一个视图模型。

public class TreeItem {
    public int Id {get; set;}
    public string Description {get; set;}
    public int ParentId {get; set;}
}

如果您使用的是MVC,那么您可以在webforms中使用asp:TreeView或者使用任何jQuery树/树视图。

答案 1 :(得分:0)

我实际上构建了一些非常相似的东西。我可以向你提供我的HTML源代码,但我需要修改.NET代码以在公开发布之前删除公司特定的专有信息(并将其转换为C#)。

不幸的是,我要根据你的需要修改我的,这几乎可以在我能够到达之前的一个月(由于我们休假前的一周忙碌)。但您可以查看我的HTML并查看可以从中重复使用的内容。根据我对您的请求的理解,您将抓住我的一半JavaScript及其功能。但如果我在你的鞋子里,我会很感激这个代码用作启动器并废弃我不想要的东西并且可能添加一些东西。或者您可能只是认为我的代码是垃圾而根本不使用它。 ; P

所以这是我的HTML& JavaScript,以便您可以使用它。它确实使用ul和ol列表来处理节点的折叠和扩展。

<!DOCTYPE HTML>
<html>
 <head><title>Oz-Med-Hist-VB</title>
  <meta charset='utf-8' />
  <meta http-equiv='X-UA-Compatible' content='IE=EmulateIE8' />
  <style type='text/css'>
    body { background: #CCC; margin-left: 1.5em; }
    li { list-style-type: none; text-indent: -38px; }

    .emptyGrayBox8 {
        background-color: #bbb; /* checked and unchecked background color */
        border: 1px solid #777; /* checked and unchecked border width & style & color */
        padding: 5.5px; display: inline-block; position: relative; margin: 0 3px; margin-top: 3px;
    }

    .checkInsideGrayBox:after {
        content: 'X';
        font-size: 13px;
        position: absolute;
        top: -2px;
        left: 1.5px;
        color: #555;
    }

    .checkInsideGrayBoxInsideLI:after {
        content: 'X';
        font-size: 13px;
        position: absolute;
        top: -2px;
        left: 39.5px;
        color: #555;
    }
  </style>
  <script>

    if (navigator.userAgent.indexOf('MSIE 8.') < 0)
    {
        document.write(
        '  <style>\n' +
        '   .checkInsideGrayBox               { left:  0.5px; }\n' +
        '   .checkInsideGrayBoxInsideLI:after { left: 38.5px; }\n' +
        '  </style>');
    }

var dta =
{   'checkedSubNodeCount--1'   : 0
,   'checkedSubNodeCount--19'  : 0
,   'checkedSubNodeCount--19-a': 0
,   'checkedSubNodeCount--19-b': 0
,   'checkedSubNodeCount--19-c': 0
,   'checkedSubNodeCount--22'  : 0
,   'checkedSubNodeCount--24'  : 0
,   'checkedSubNodeCount--25'  : 0
,   'checkedSubNodeCount--144' : 0
,   'checkedSubNodeCount--1728': 0
};

function chkBox_click(id) // this gets called when user clicks on checkbox or text to right of checkbox.
{ // when checkbox becomes checked, then associated group also becomes shown.
    var chkE = document.getElementById('chkBox--' + id); // CHecKBOX HTML element
    var grpE = document.getElementById('grpBox--' + id); // GRouPbox HTML element (UL element)
    var isChecked = chkE.checked;
    if (isChecked)
    {   // if user just checked the checkbox, and ...
        if (grpE != null) // if an associated (sub)group exists for this checkbox, ...
        { grpE.style.display = ''; } // then expand/show the group element.
    }
    setLabelHtm(id);
    var pid = getParentID(id); // Parent ID
    if (id == null) { return; } // if parent id doesn't exist then we're done here.

    // now 'pid' is parent ID of 'id'. for instance:
    // when id == '19-a'    then pid = '19'
    // when id == '19-a-1-' then pid = '19-a'

    var h = '';
    var maxLoopCount = 12; // infinite loop protection :P
    while (pid != null && --maxLoopCount >= 0)
    {
        chkE = document.getElementById('chkBox--' + pid); // CHecKBOX element *of parent*
        var pKey = 'checkedSubNodeCount--' + pid; // Key for this Parent ID
        if (isChecked) { ++dta[pKey]; } else { --dta[pKey]; }
        setLabelHtm(pid);
        if (h.length > 0) { h += '\n\n'; }
        h += 'id = ' + id + '   isChecked = ' + isChecked
        +   '\npid = ' + pid + '   chkE = ' + chkE
        +   '\ndta[\'' + pKey + '\'] = ' + dta[pKey];

        pid = getParentID(pid);
    }
//  alert(h);
} // function chkBox_click(id)

function chkBox_click8(id)
{
    var chkE = document.getElementById('chkBox--' + id); // CHecKBOX element
    var lblE = document.getElementById('chkLab--' + id); // CHecKbox LABel (HTML 'label' element for the checkbox)
    if (chkE == null || lblE == null) { return; }
    var isChecked = chkE.checked;
    var g = Number(chkE.tag);
    if (isChecked == false) { g = 3; chkE.tag = g; }
    if (isChecked == true ) { g = 2; chkE.tag = g; }
    alert(id + '\nisChecked = ' + isChecked + '\n.tag = ' + g);
} // function chkBox_click8(id)

function chkBox_clickIt(id)
{   var chkE = document.getElementById('chkBox--' + id); // CHecKBOX HTML element
    if (chkE != null) { chkE.click(); }
} // function chkBox_clickIt(id)

function getParentID(id)
{
    var pid = String(id); if (pid.length < 1) { return null; }
    if (pid[pid.length - 1] == '-') { pid = pid.substr(0, pid.length - 1); }
    var x = pid.lastIndexOf('-'); if (x < 0) { return null; }
    pid = pid.substr(0, x); // now pid is id of parent
    return pid;
} // function getParentID(id)

function hdrLab_click(id) // this will switch whether the associated group is hidden or shown
{// var chkE = document.getElementById('chkBox--' + id); // CHecKBOX HTML element
    var grpE = document.getElementById('grpBox--' + id); // GRouPBOX HTML element (UL element)
    if (grpE != null)
    {   if (grpE.style.display == '')
        {   grpE.style.display = 'none'; }
        else
        {   grpE.style.display = ''; }
    }
    setLabelHtm(id);
} // function hdrLab_click(id)

function setLabelHtm(id)
{   var grpE = document.getElementById('grpBox--' + id); // GRouPBOX HTML element (UL element)
    var lblE = document.getElementById('hdrLab--' + id);
    if (lblE == null) { return; }
    var h = null; var suffix = '.';
    if (grpE == null) { h = '<span style="visibility: hidden">+</span>'; }
    else
    {   var grpIsOpen = grpE.style.display == '';
        h = '<b>' + (grpIsOpen ? '−' : '+') + '</b>';
    }
    var s = String(id);
    if (s.length > 0 && s.substr(s.length - 1) == '-') { s = '&bull;'; suffix = ''; }
    else
    {
        var x = s.lastIndexOf('-') + 1;
        if (x > 0) { s = s.substr(x); }
    }
    h += '&nbsp;' + s + suffix;

    var cnt = dta['checkedSubNodeCount--' + id]; // CouNT of checked sub-nodes
    if (cnt != null) // if this node is a parent node
    {   var chkE = document.getElementById('chkBox--' + id); // CHecKBOX HTML element
        if (chkE != null) { chkE.style.display = (cnt > 0) ? 'none' : ''; }
        var hBeg = '<span class="emptyGrayBox8">';
        var hEnd = '</span>';
        if (cnt > 0) { h += hBeg + '<span class="checkInsideGrayBoxInsideLI"></span>' + hEnd; }
        else if (chkE == null) { h += hBeg + hEnd; }
    }
    lblE.innerHTML = h;
} // function setLabelHtm(id)

function init()
{   //alert('yes');
    hdrLab_click('1');
    hdrLab_click('1-a');
    hdrLab_click('19');
    hdrLab_click('19-a');
    hdrLab_click('19-a-1-');
    hdrLab_click('19-a-2-');
    hdrLab_click('19-b');
    hdrLab_click('19-b-1-');
    hdrLab_click('19-b-2-');
    hdrLab_click('19-b-3-');
    hdrLab_click('19-c');
    hdrLab_click('19-c-1-');
    hdrLab_click('19-c-2-');
    hdrLab_click('19-c-3-');
    hdrLab_click('22');
    hdrLab_click('22-a');
    hdrLab_click('22-b');
    hdrLab_click('23');
    hdrLab_click('24');
    hdrLab_click('24-a');
    hdrLab_click('24-b');
    hdrLab_click('24-c');
    hdrLab_click('25');
    hdrLab_click('25-a-');
    hdrLab_click('144');
    hdrLab_click('144-a');
    hdrLab_click('1728');
    hdrLab_click('1728-a');
//  alert(dta['checkedSubNodeCount--19-a']);
}
    window.onload = init;
  </script>
 </head>
 <body>
<ul style='list-style-type: none; margin-left: -1em'>
    <li><label id='hdrLab--1' onclick='hdrLab_click("1")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--1' onclick='hdrLab_click("1")')>&nbsp;Test for short number '1':</label></li>
    <ul id='grpBox--1'>
        <li><label id='hdrLab--1-a' onclick='hdrLab_click("1-a")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--1-a' onclick='chkBox_click("1-a");' /><label id='txtLab--1-a' onclick='chkBox_clickIt("1-a")')>Test for short number '1' subitem:</label></li>
    </ul>
    <li><label id='hdrLab--19' onclick='hdrLab_click("19")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--19' onclick='hdrLab_click("19")')>&nbsp;Which fruit do you prefer?</label></li>
    <ul id='grpBox--19'>
        <li><label id='hdrLab--19-a' onclick='hdrLab_click("19-a")' tabindex='0' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-a' onclick='chkBox_click("19-a");' /><label id='txtLab--19-a' onclick='chkBox_clickIt("19-a")')>Apples:</label></li>
        <ul id='grpBox--19-a'>
            <li><label id='hdrLab--19-a-1-' onclick='hdrLab_click("19-a-1-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-a-1-' onclick='chkBox_click("19-a-1-");' /><label id='txtLab--19-a-1-' onclick='chkBox_clickIt("19-a-1-")')>Red delicious</label></li>
            <li><label id='hdrLab--19-a-2-' onclick='hdrLab_click("19-a-2-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-a-2-' onclick='chkBox_click("19-a-2-");' /><label id='txtLab--19-a-2-' onclick='chkBox_clickIt("19-a-2-")')>Granny smith</label></li>
        </ul>
        <li><label id='hdrLab--19-b' onclick='hdrLab_click("19-b")' tabindex='0' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-b' onclick='chkBox_click("19-b");' /><label id='txtLab--19-b' onclick='chkBox_clickIt("19-b")')>Bananas:</label></li>
        <ul id='grpBox--19-b'>
            <li><label id='hdrLab--19-b-1-' onclick='hdrLab_click("19-b-1-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-b-1-' onclick='chkBox_click("19-b-1-");' /><label id='txtLab--19-b-1-' onclick='chkBox_clickIt("19-b-1-")')>Green</label></li>
            <li><label id='hdrLab--19-b-2-' onclick='hdrLab_click("19-b-2-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-b-2-' onclick='chkBox_click("19-b-2-");' /><label id='txtLab--19-b-2-' onclick='chkBox_clickIt("19-b-2-")')>Yellow (ripe but not too ripe)</label></li>
            <li><label id='hdrLab--19-b-3-' onclick='hdrLab_click("19-b-3-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-b-3-' onclick='chkBox_click("19-b-3-");' /><label id='txtLab--19-b-3-' onclick='chkBox_clickIt("19-b-3-")')>Brown (<i>very</i> ripe)</label></li>
        </ul>
        <li><label id='hdrLab--19-c' onclick='hdrLab_click("19-c")' tabindex='0' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-c' onclick='chkBox_click("19-c");' /><label id='txtLab--19-c' onclick='chkBox_clickIt("19-c")')>Juice</label></li>
        <ul id='grpBox--19-c'>
            <li><label id='hdrLab--19-c-1-' onclick='hdrLab_click("19-c-1-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-c-1-' onclick='chkBox_click("19-c-1-");' /><label id='txtLab--19-c-1-' onclick='chkBox_clickIt("19-c-1-")')>Orange juice</label></li>
            <li><label id='hdrLab--19-c-2-' onclick='hdrLab_click("19-c-2-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-c-2-' onclick='chkBox_click("19-c-2-");' /><label id='txtLab--19-c-2-' onclick='chkBox_clickIt("19-c-2-")')>Grape juice</label></li>
            <li><label id='hdrLab--19-c-3-' onclick='hdrLab_click("19-c-3-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--19-c-3-' onclick='chkBox_click("19-c-3-");' /><label id='txtLab--19-c-3-' onclick='chkBox_clickIt("19-c-3-")')>Tomato juice</label></li>
        </ul>
    </ul>
    <li><label id='hdrLab--22' onclick='hdrLab_click("22")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--22' onclick='hdrLab_click("22")')>&nbsp;Which juice do you prefer?</label></li>
    <ul id='grpBox--22'>
        <li><label id='hdrLab--22-a' onclick='hdrLab_click("22-a")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--22-a' onclick='chkBox_click("22-a");' /><label id='txtLab--22-a' onclick='chkBox_clickIt("22-a")')>Apple juice</label></li>
        <li><label id='hdrLab--22-b' onclick='hdrLab_click("22-b")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--22-b' onclick='chkBox_click("22-b");' /><label id='txtLab--22-b' onclick='chkBox_clickIt("22-b")')>Orange juice</label></li>
    </ul>
    <li><label id='hdrLab--23' onclick='hdrLab_click("23")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--23' onclick='chkBox_click("23");' /><label id='txtLab--23' onclick='chkBox_clickIt("23")')>Single checkmark question with no subnodes</label></li>
    <li><label id='hdrLab--24' onclick='hdrLab_click("24")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--24' onclick='hdrLab_click("24")')>&nbsp;Best OS?</label></li>
    <ul id='grpBox--24'>
        <li><label id='hdrLab--24-a' onclick='hdrLab_click("24-a")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--24-a' onclick='chkBox_click("24-a");' /><label id='txtLab--24-a' onclick='chkBox_clickIt("24-a")') style='color:green'>Android!</label></li>
        <li><label id='hdrLab--24-b' onclick='hdrLab_click("24-b")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--24-b' onclick='chkBox_click("24-b");' /><label id='txtLab--24-b' onclick='chkBox_clickIt("24-b")') style='color:brown'>Apple</label></li>
        <li><label id='hdrLab--24-c' onclick='hdrLab_click("24-c")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--24-c' onclick='chkBox_click("24-c");' /><label id='txtLab--24-c' onclick='chkBox_clickIt("24-c")')>Linux</label></li>
    </ul>
    <li><label id='hdrLab--25' onclick='hdrLab_click("25")' tabindex='0' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--25' onclick='chkBox_click("25");' /><label id='txtLab--25' onclick='chkBox_clickIt("25")')>Check question with subnode check too.</label></li>
    <ul id='grpBox--25'>
        <li><label id='hdrLab--25-a-' onclick='hdrLab_click("25-a-")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--25-a-' onclick='chkBox_click("25-a-");' /><label id='txtLab--25-a-' onclick='chkBox_clickIt("25-a-")')>Sub-node check question</label></li>
    </ul>
    <li><label id='hdrLab--144' onclick='hdrLab_click("144")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--144' onclick='hdrLab_click("144")')>&nbsp;Test for 3-digit number:</label></li>
    <ul id='grpBox--144'>
        <li><label id='hdrLab--144-a' onclick='hdrLab_click("144-a")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--144-a' onclick='chkBox_click("144-a");' /><label id='txtLab--144-a' onclick='chkBox_clickIt("144-a")')>Test for 3-digit number subitem:</label></li>
    </ul>
    <li><label id='hdrLab--1728' onclick='hdrLab_click("1728")' tabindex='0' onkeypress='this.click(); return false;'></label><label id='txtLab--1728' onclick='hdrLab_click("1728")')>&nbsp;Test for 4-digit number:</label></li>
    <ul id='grpBox--1728'>
        <li><label id='hdrLab--1728-a' onclick='hdrLab_click("1728-a")' onkeypress='this.click(); return false;'></label><input type='checkbox' id='chkBox--1728-a' onclick='chkBox_click("1728-a");' /><label id='txtLab--1728-a' onclick='chkBox_clickIt("1728-a")')>Test for 4-digit number subitem:</label></li>
    </ul>
</ul>
<div style='display: none'>
<script>
    var ua = navigator.userAgent;
    var ieVersion = null;
    var ix = 0;
    for (var v = 7; v <= 99; v++)
    {
        document.write('<!--[if IE ' + v + ']><hr />According to the conditional comment this is IE ' + v + '.<![endif]-->');
        if ((ix = ua.indexOf('MSIE ' + v + '.')) >= 0)
        {   ieVersion = '';
            ix += 5;
            while (ix < ua.length && '1234567890.'.indexOf(ua.charAt(ix)) >= 0)
            {   ieVersion += ua.charAt(ix++);
            }   document.write('<hr />According to JavaScript, this is IE ' + ieVersion);
        }
    }
    if (ieVersion == null)
    {   document.write('<hr />According to JavaScript, this IE version could not be determined.');
    }   document.write('<hr />navigator.userAgent = ' + navigator.userAgent);
</script><hr />
    <button id='webBtn1'>Fire an event</button>
    <button id='btnHi' onclick='window.external.sayHelloFromJavaScript("Hello from JavaScript!", 5);'>Say Hello</button>
</div>
 </body>
</html>

我还注意到我的项目使用了为IE构建的WebControl,它需要像WinXP一样工作。所以我可以使用它因为它必须适用于IE 7而受限制,但好的是它可以用于回到WinXP的最新工作版IE。我也关心使用符合标准的代码,因此据我所知,我的JavaScript也适用于任何符合标准的浏览器。我在最新版本的Chrome和FF以及IE 10,IE 8和IE 7中进行了测试。

在HTML页面中生成数据所需的代码仍然很多,但这可以解决一半的请求。

在我的.NET代码中,我必须构建那些JavaScript和HTML列表,我可以在几周内完成。

答案 2 :(得分:0)

您可以使用Dictionary对象填充分层数据。此代码示例可能会有所帮助:

Dictionary<string, Dictionary<string, List<string>>> dictPage = new Dictionary<string, Dictionary<string, List<string>>>();

foreach (DataRow row in dt.Rows)
{
    string sPageID = row["PageName"].ToString();
    string sVersionID = row["VersionName"].ToString();
    string sElementID = row["ElementName"].ToString();

    if (!dictPage.ContainsKey(sPageID))
        dictPage.Add(sPageID, new Dictionary<string, List<string>>());

    if (!dictPage[sPageID].ContainsKey(sVersionID))
        dictPage[sPageID].Add(sVersionID, new List<string>());

    dictPage[sPageID][sVersionID].Add(sElementID);
}

我在此示例代码中使用了DataTable,但您可以将其与SqlDataReader一起使用。

要创建HTML,您可以执行以下操作:

StringBuilder sbHtmlUL = new StringBuilder();

sbHtmlUL.Append("<ul>");

foreach (var page in dictPage)
{
    sbHtmlUL.Append("<li>");
    sbHtmlUL.Append(page.Key);

    sbHtmlUL.Append("<ul>");

    foreach (var version in page.Value)
    {
        sbHtmlUL.Append("<li>");
        sbHtmlUL.Append(version.Key);

        sbHtmlUL.Append("<ul>");

        foreach (var element in version.Value)
        {
            sbHtmlUL.Append("<li>");
            sbHtmlUL.Append(element);
            sbHtmlUL.Append("</li>");
        }

        sbHtmlUL.Append("</ul>");
        sbHtmlUL.Append("</li>");
    }

    sbHtmlUL.Append("</ul>");
    sbHtmlUL.Append("</li>");
}

sbHtmlUL.Append("</ul>");

在此过程中,您还可以应用CSS类,ID或简单JavaScript来添加折叠/展开行为。

请注意,我没有将此代码的性能与其他方法进行比较,因此我对此并不十分肯定,但在一台普通计算机上测试需要8毫秒才能处理8000条记录。