如何使用 C++/winRT 为 UWP NavigationView 的菜单动态定义 Headers 和设置?

How to dynamically define Headers and Settings for UWP NavigationView's menu, using C++/winRT?

我正在用 C++ 开发一个 cross-platform 项目,动态生成 UI,我在使用 C++/winRT UWP NavigationView 时遇到两个问题:

  1. 定义 NavigationViewItemHeader 时,生成的 header 标题不会显示在导航菜单中,space 保持为空,
  2. 尝试更新导航菜单的 SettingsItem 时,设置导航项的值为 SettingsItem() 返回的 nullptr。

这是我编写的代码,用于从独立于主机管理的项目列表中生成菜单(例如 Windows):

bool
CANavigationView::UpdateHostView( void )
{
    TNavigationItemPtr item;
    TIndex index;

    if( _hostViewUpdateNeeded == false )
        return false;

    Windows::UI::Xaml::Controls::NavigationViewItemBase hItem( nullptr );
    Windows::UI::Xaml::Controls::TextBlock hText( nullptr );
    winrt::hstring hTag;

    // Remove all navigation items from the current host view:
    _hostNavigationView.MenuItems().Clear();
    _hostNavigationView.IsSettingsVisible( false );

    // Build the navigation menu items:
    for( index = 0; index < _navigationItems.CountOfItems(); index++ )
    {
        item = * _navigationItems.GetItemAtIndex( index );

        if( item->identifier == kSettingsItem )
        {
            _hostNavigationView.IsSettingsVisible( true );
            hText = Windows::UI::Xaml::Controls::TextBlock();
            CSString::ConvertToUIString( item->title->GetString( gAppLanguageCode ), & hTag );
            hText.Text( hTag );
//          Issue #1 : cannot access to the Settings item
//          _hostNavigationView.SettingsItem().as< Windows::UI::Xaml::Controls::NavigationViewItem >().Content( hText );
//          SettingsItem() returns nullptr...
        }
        else
        {
            switch( item->type )
            {
                case eNavigationHeader:
                    hItem = Windows::UI::Xaml::Controls::NavigationViewItemHeader();
                    hText = Windows::UI::Xaml::Controls::TextBlock();
                    CSString::ConvertToUIString( item->title->GetString( gAppLanguageCode ), & hTag );
                    hText.Text( hTag );
//                  Issue #2: The header's title is not displayed
                    hItem.Content( hText );
                    _hostNavigationView.MenuItems().Append( hItem );
                    break;

                case eNavigationSeparator:
                    hItem = Windows::UI::Xaml::Controls::NavigationViewItemSeparator();
                    _hostNavigationView.MenuItems().Append( hItem );
                    break;

                case eNavigationItem:
                    hItem = Windows::UI::Xaml::Controls::NavigationViewItem();
                    CSString::ConvertToUIString( CAUIElement::GetStringFromUIIdentifier( item->identifier ), & hTag );
                    hItem.Tag( winrt::box_value( hTag ) );
                    hText = Windows::UI::Xaml::Controls::TextBlock();
                    CSString::ConvertToUIString( item->title->GetString( gAppLanguageCode ), & hTag );
                    hText.Text( hTag );
                    hItem.Content( hText );
                    hItem.as< Windows::UI::Xaml::Controls::NavigationViewItem>().Icon( GetHostIcon( item->icon ) );
                    _hostNavigationView.MenuItems().Append( hItem );
                    break;

                default:
                    break;
            }
        }
    }

    _hostViewUpdateNeeded = false;

    return true;
}

由于我使用自己的字符串格式(我坚持使用旧的 C++ 标准...)和 I18N 支持,我需要先将 UTF8 字符串转换为主机(此处 Windows)在设置文本块的值之前,使用 hstring 类型的 hTag 变量。在调试模式下,文本很好地转码为hstring格式...

令我困惑的是,NavigationSeparator 和 NavigationItem 案例都工作正常,符合 Microsoft 官方文档(包括用于菜单事件处理的 Tag 和 NavigationViewItem 的图标设置)。

我知道这不是处理 UWP 用户界面的“主流 XAML 方式”,但到目前为止,该方法在其他 UI 元素上运行良好。

这是导航视图的屏幕截图,其中 headers 为空 spaces:

此外,在上面的示例中,我在主机导航视图 (_hostNavigationView.MenuItems().Size()) 中记录了菜单项的数量,结果是 7,这是正确的...

最后,这是我在 DEBUG 模式下生成的详细日志:

DBG-[000002686A230710]CANavigationView::UpdateDisplayedLanguage() {
    DBG-[000002686A230710]CANavigationView::UpdateHostView() {
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 0, type 2
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Header case: Reference Library
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 1, type 1
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 2, type 1
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 3, type 1
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 4, type 3
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Separator case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 5, type 2
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Header case: Project Library
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 6, type 1
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Navigation item 7, type 1
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Settings case
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Value of SettingsItem(): 0000000000000000
        DBG-[000002686A230710]CANavigationView::UpdateHostView() Count of menu items for the navigation view: 7 (8)
    DBG-}
DBG-}

非常感谢您帮助解决这两个问题!

此致,

阿尔诺

动态 Headers/Footers 在报告中启用不同的分组选项,例如“按位置”或“按系统按位置”:请注意上面圈出了“报告定义”一词。虽然报表最多可以有三个动态 Headers/Footers,但有些报表只有一个或两个动态组。

根据 Roy 的评论,没有必要使用 TextBlock 来设置 NavigationViewItemHeader 和 NavigationViewItem 的值。相反,这只是将字符串值装箱到 IInspectable object:

中的情况
hItem.Content( winrt::box_value( hTag ) );

现在,我有了正确的导航菜单显示和行为:

谢谢罗伊!

更新: 我还设法更改了 SettingsItem 的标题。根据文档,自定义导航菜单的好时机是在加载视图时。

因此,我 subscribed/registered 到 Loaded() 事件并从那里执行自定义。在导航视图生命周期的那个阶段,SettingsItem() returns 一个有效的 NavigationViewItem 允许我使用相同的字符串装箱方法更改标题。

现在两个问题都解决了!