【问题标题】:How to reference parent DataContext in user control [C++/winrt]如何在用户控件中引用父 DataContext [C++/winrt]
【发布时间】:2021-06-28 04:05:59
【问题描述】:

编辑:通过空白用户控件解决,而不是自定义控件(参见下面的解决方案) 我基本上是在尝试将复杂的 XAML 内容从 MainPage 模块化到用户控件中,因此我需要弄清楚如何将 MainPage.Xaml 中的本地 DataContext 注入到我的用户控件中。

按照 MSDN Doc 的自定义用户控件教程,我的 MainPage.xaml 文件中有一个自定义控件,称为 NutriDetailsControl。

// MainPage.xaml


...<ListBox x:Name="ItemList" ItemsSource="{x:Bind Basket}" RelativePanel.Below="AddDelCol"
      <ItemsControl.ItemTemplate>
         <DataTemplate x:DataType="local:BasketItem"> 
            <StackPanel GotFocus="ItemGotFocus" Orientation="Horizontal">
               <Button Content="Delete" Click="DeleteItemClickHandler"/>
               <TextBlock VerticalAlignment="Center" Text="{x:Bind Name, Mode=OneWay}"/>
HERE------->   <local:NutriDetailsControl Background="Red" MyItem="Hello, World!" MyBasketItem="x:Bind ???"/>
            ...
         ...
      ...
   ...
...</ListBox>

MyItem 是一个字符串,我可以在自定义用户控件中访问它,但我无法访问 MyBasketItem,它是 BasketItem 类型的 DependencyProperty(本地 DataContext 中的 ViewModel 变量)。在 Generic.Xaml 中,我尝试通过本地绑定以及通过传入的 DependencyProperty (MyBasketItem) 来访问 BasketItem 的数据,

<!-- \Themes\Generic.xaml -->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:NutritionBasket">

 <Style TargetType="local:NutriDetailsControl" >
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:NutriDetailsControl">
                    <Grid Width="30" Height="30" Background="{TemplateBinding Background}">
(THIS WORKS)------>     <!--<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{TemplateBinding MyItem}"/>-->
                        <Button HorizontalAlignment="Right" VerticalAlignment="Center" Content=">">
                            <Button.Flyout>
                                <Flyout>
                                    <Flyout.FlyoutPresenterStyle>
                                        <Style TargetType="FlyoutPresenter">
                                            <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled"/>
                                            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
                                            <Setter Property="IsTabStop" Value="True"/>
                                            <Setter Property="TabNavigation" Value="Cycle"/>
                                        </Style>
                                    </Flyout.FlyoutPresenterStyle>
                                    <StackPanel>
    LOOK HERE ---------------->          <TextBlock Text="{x:Bind Name}"/>
     AND HERE ---------------->          <TextBlock Text="{TemplateBinding MyBasketItem.Name}"/>
                                    </StackPanel>
                                </Flyout>
                            </Button.Flyout>
                        </Button>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

对于第一行,我试图通过本地 DataContext 访问 BasketItem->Name()。我收到错误消息“此 Xaml 文件必须有代码隐藏才能使用 {x:Bind}”。

对于第二行,当我在 NutriDetailsControl.h 中将 BasketItem 作为 DependencyProperty 传递时,我收到“Windows Universal 项目不支持 MyBasketItem”

我知道对于 Name(),我可以简单地将其作为 DependencyProperty 传入;我的目标是引用另一个变量 BasketItemNutri(),它是 BasketItem 的子 ViewModel 变量。所以我需要想办法将 BasketItem 或 BasketItemNutri 从 MainPage 传递到自定义用户控件。

任何帮助将不胜感激。我该怎么办?

【问题讨论】:

    标签: c++-winrt


    【解决方案1】:

    已解决

    我无法让 Nico 的解决方案为我工作,因为我仍然需要在 NutriDetailsControl.xaml 中以某种方式绑定到 MyBasketItem,而且我尝试的任何排列似乎都不起作用。

    在阅读了Data Binding in Depth 之后,我想也许我应该忘记整个自定义控件的情况,只使用一个名为 NutriControl 的空白用户控件。

    我通过添加在 NutriControl.idl(空白用户控件)中创建了一个 DependencyProperty

    static Windows.UI.Xaml.DependencyProperty MyBasketItemProperty{ get; };
            BasketItem MyBasketItem;
    

    (注意 MyBasketItemProperty 和 MyBasketItem; 必须相同)。通过 NutriControl.idl 文件“公开”的用户控件的 DependencyProperty 基本上就像一个框,您可以将数据从其他 Xaml 文件(如 MainPage.xaml)放入其中。数据可以是文本、类对象(包括您的视图模型)等。

    // Mainpage.Xaml
    <local:NutriControl MyBasketItem="{x:Bind}"/>
    

    “x:Bind”绑定到本地DataContext(DataType??),这里是BasketItem,所以我不需要指定其他任何东西。

    // NutriControl.h
    ...other stuff...
    struct NutriControl : NutriControlT<NutriControl>
        {
            NutriControl();
    
            NutritionBasket::BasketItem MyBasketItem()
            {
                return winrt::unbox_value<NutritionBasket::MyBasketItem>(GetValue(m_myBasketItemProperty));
            }
    
            void MyBasketItem(NutritionBasket::MyBasketItem const& value)
            {
                SetValue(m_myBasketItemProperty, winrt::box_value(value));
            }
    
            static Windows::UI::Xaml::DependencyProperty MyBasketItemProperty() { return m_myBasketItemProperty; }
    
            static void OnMyBasketItemChanged(Windows::UI::Xaml::DependencyObject const&, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const&);
    
        private:
            static Windows::UI::Xaml::DependencyProperty m_myBasketItemProperty;
        };
    }
    ...other stuff...
    
    // NutriControl.c
        NutriControl::NutriControl() {
            InitializeComponent();
        }
    
        Windows::UI::Xaml::DependencyProperty NutriControl::m_myBasketItemProperty =
            Windows::UI::Xaml::DependencyProperty::Register(
                L"MyBasketItem",
                winrt::xaml_typename<NutritionBasket::BasketItem>(),
                winrt::xaml_typename<NutritionBasket::NutriControl>(),
                Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(L"default label"), Windows::UI::Xaml::PropertyChangedCallback{ &NutriControl::OnMyBasketItemChanged } }
        );
    
        void NutriControl::OnMyBasketItemChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& /* e */)
        {
           // still unimplemented
        }
    
    // NutriControl.Xaml
        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Button HorizontalAlignment="Right" VerticalAlignment="Center" Content=">">
                <Button.Flyout>
                    <Flyout>
                        <Flyout.FlyoutPresenterStyle>
                            <Style TargetType="FlyoutPresenter">
                                <Setter Property="ScrollViewer.HorizontalScrollMode" Value="Disabled"/>
                                <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
                                <Setter Property="IsTabStop" Value="True"/>
                                <Setter Property="TabNavigation" Value="Cycle"/>
                            </Style>
                        </Flyout.FlyoutPresenterStyle>
                        <StackPanel>
     see here-------->      <TextBlock Text="{x:Bind MyBasketItem.Nutrition.Amount, Mode=OneWay}"></TextBlock>
     and here-------->      <ListBox ItemsSource="{x:Bind MyBasketItem.Nutrition.Elems}">
                                <ItemsControl.ItemTemplate>
                                    <DataTemplate x:DataType="local:BasketItemNutriElem">
                                        <StackPanel Orientation="Horizontal">
                                            <TextBlock VerticalAlignment="Center" Text="{x:Bind Nutrient, Mode=OneWay}"/>
                                            <TextBlock VerticalAlignment="Center" Text="{x:Bind Amount, Mode=OneWay}"/>
                                        </StackPanel>
                                    </DataTemplate>
                                </ItemsControl.ItemTemplate>
                            </ListBox>
                        </StackPanel>
                    </Flyout>
                </Button.Flyout>
            </Button>
        </StackPanel>
    

    这行得通。 MyBasketItem (-> Nutrition) 和 (->Elems->BasketItemNutriElem) 都是 BasketItem 类型的子视图模型,因此我已成功地将视图模型的本地实例从 MainPage.Xaml 传递到用户控件。

    【讨论】:

      【解决方案2】:

      所以我需要想办法将 BasketItem 或 BasketItemNutri 从 MainPage 传递到自定义用户控件。

      你把NutriDetailsControl放在了DataTemplate里面,所以后面的代码不能直接用x:Bind绑定属性。对于这种情况,更好的方法是使用绑定元素名称然后找到它是DataContext

      MyBasketItem="{Binding ElementName=ItemList,Path=DataContext.MyBasketItem}"
      

      请注意,您需要特定的当前页面作为页面的 DataContext。

      public MainPage()
      {
         
          this.InitializeComponent();
          DataContext = this;
      }
      

      更多详情请参考本案例reply

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-11-25
        • 2015-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-03
        • 1970-01-01
        相关资源
        最近更新 更多