1

私が作成したカスタム タイプ (Menu) のオブジェクトがあります。これには、同じタイプの子要素を含めることができます。これには当然、子要素などを含めることができます。

これらのオブジェクトからメニューとサブメニューを生成したいのですが、すべてのループをハードコーディングする必要がないように、それらを再帰的にトラバースする方法を考えています。私は再帰に慣れていません.Menuオブジェクトとその下にあるすべてのオブジェクトをトラバースする方法に光を当てることができる人はいますか?

コード例:

public class Menu {
    public int MenuID { get; set; }
    public int Name { get; set; }
    public Menu[] _ChildMenus { get; set; }
}
4

3 に答える 3

3

1 つのオプションを次に示します。

private void TraverseMenu(Menu menu)
{
    Output(string.Format("Now reading menu #{0}, named {1}", menu.MenuID, menu.Name));
    if (menu._ChildMenus != null)
    {
        foreach (Menu child in menu._ChildMenus)
        {
            TraverseMenu(child);
        }
    }
}

各メニューがどの程度「深く」ネストされているかを知る必要がある場合は、レベル パラメータを追加できます。

private void TraverseMenu(Menu menu, int level)
{
    Output(string.Format("Now reading menu #{0}, named {1} in level {2}", menu.MenuID, menu.Name, level));
    if (menu._ChildMenus != null)
    {
        foreach (Menu child in menu._ChildMenus)
        {
            TraverseMenu(child, level + 1);
        }
    }
}

private void TraverseMenu(Menu menu)
{
    TraverseMenu(menu, 0);
}
于 2013-02-11T12:21:26.490 に答える
0

通常、メニューの各レベルには、それに関連付けられた異なるループが必要です。たとえば、最初のレベル (例: ホーム、製品、about_us) は上部に水平に表示され、2 番目のレベルは関連する親メニュー項目の下のドロップダウンに表示されます。 、第 2 レベルのメニュー項目の下に第 3 レベルが表示されます。

したがって、ループについては、理論的にはレベルごとに分割したくありませんが、親メニュー項目へのリンクを維持して表示できるようにします。もう 1 つのより一般的なことは、ツリーの最上位のメニューが他のメニュー項目のコンテナー要素として無視されることが多いことです。これにより、次のような複数のトップレベル メニューを使用できます。

編集: コードを修正しました..編集履歴を元に戻して、私がいかに天才ではないかを確認してください:p

string html;
int lastLevel = 0;
void placeMenu( Menu menu, int level)
{
    // if level hasn't changed close last menu-item
    if( lastLevel == level ) html += "</li>";

    // if we're deeper, open a new <UL>
    else if( lastLevel < level ) html += "<ul>";

    // if we're less deep, close the last <UL> and it's parent menu-item
    else if( lastLevel > level ) html += "</ul></li>";

    // add current menu item without closing it's <LI> so the next itteration of the loop can add a submenu if needed
    html += "<li><a href='http://link/to/page'>" + menu.Name + "</a>";

    lastLevel = level;
}

void setupMenu( Menu menu, int level )
{
    foreach( var currentMenu in menu._ChildMenus )
    {
        // place current menu
        placeMenu( currentMenu, level + 1 );

        // place submenus
        setupMenu( currentMenu, level + 1 );
    }
}

string setupWholeMenu( Menu menu )
{
    setupMenu( menu, 0 );

    // close any elements left open by the recursive loop
    html = html + "</li></ul>";

    return html;
}

このコードは、メニュー構造から通常の html の順序付けされていないレベルのリストを作成するように設計されています。これは、HTML でメニューを追加してスタイルを設定する標準的な方法です。ここで HTML 順不同リストを使用する理由は、正しく実装されていれば、Javascript と CSS が無効になっていても (GSM と弱視者向けのスクリーン リーダー)、このリスト構造が引き続き表示され、Javascript が無効になっていてもすべての IE6+ で機能するためです。

正直に言うと、MVC では通常、HTML の順序付けられていないリストとして宣言的な方法でメニューを直接設定するだけで簡単に割り当てられます。部分的な共有ページに配置するか、定義することでどこにでも表示すると、複数の方法でスタイルを設定できます。あなたのレイアウトページでそれを。このアプローチは、上記のように動的なメニュー構造がある場合でも機能しますが、Razor を使用してリストを作成する方がおそらく簡単です。

もう 1 つの注意点は、この種の再帰関数では、文字列を連結するよりもはるかに効率的なStringBuilderを使用する必要があることです。ただし、メニュー構造 (それぞれが 2 ~ 3 の連結で構築された最大 30 個の項目を含む) の場合、これによって顕著な遅延が発生することはありません。将来のために留意する必要があります。

于 2013-02-11T12:48:36.143 に答える
0

レンダリング目的で、部分ビューを使用できます。

-- MenuView.cshtml

@model IEnumerable<Menu>

@if (Model != null && Model.Any())
{
    <ul>
        @foreach(var menu in Model)
        {
            <li>
                <a href='some/path/to/menu?id=@menu.MenuID'>@menu.Name</a>
                @Html.RenderPartial("MenuView", menu._ChildMenus)
            </li>
        }
    <ul>
}

編集:「C# での再帰」から MVC に関連するものからトピックが変更されたため、これは少し無関係に思えるかもしれません。

于 2013-02-11T12:27:16.640 に答える