如何在用户控件中引用父DataContext [C++/winrt]
How to reference parent DataContext in user control [C++/winrt]
编辑:通过空白用户控件解决,而不是自定义控件(参见下面的解决方案)
我基本上是在尝试将复杂的 XAML 内容从 MainPage 模块化到用户控件中,因此我需要弄清楚如何将 MainPage.Xaml 中的本地 DataContext 注入到我的用户控件中。
按照 MSDN 文档的自定义用户控件教程,我的 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 中,我尝试通过本地绑定访问 BasketItem 的数据,并通过我传入的 DependencyProperty (MyBasketItem),
<!-- \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 传递时,我收到“MyBasketItem 在 Windows 通用项目中不受支持”
我知道对于 Name(),我可以简单地将其作为 DependencyProperty 传入;我的目标是引用另一个变量 BasketItemNutri(),它是 BasketItem 的子 ViewModel 变量。所以我需要找到一种方法,将 BasketItem 或 BasketItemNutri 从 MainPage 传递到自定义用户控件中。
如有任何帮助,我们将不胜感激。我该怎么办?
So I need to figure out a way to either pass in BasketItem, or BasketItemNutri, from the MainPage into custom user control.
你把NutriDetailsControl
放在了DataTemplate
里,所以你不能直接在后面的代码里用x:Bind
绑定属性。对于这种情况,更好的方法是使用绑定元素名称然后找到它 DataContext
MyBasketItem="{Binding ElementName=ItemList,Path=DataContext.MyBasketItem}"
请注意,您需要特定的当前页面作为页面的 DataContext。
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
更多细节请参考这个案例 .
已解决
我无法让 Nico 的解决方案为我工作,因为我仍然需要以某种方式在 NutriDetailsControl.xaml 内绑定到 MyBasketItem,而且我尝试的任何排列似乎都不起作用。
阅读 Data Binding in Depth 之后,我想也许我应该忘记整个自定义控件的情况,而只使用一个名为 NutriControl 的空白用户控件。
我通过添加
在NutriControl.idl(空白用户控件)中创建了一个依赖项属性
static Windows.UI.Xaml.DependencyProperty MyBasketItemProperty{ get; };
BasketItem MyBasketItem;
(注意 MyBasketItem属性 和 MyBasketItem; 必须相同)。通过 NutriControl.idl 文件“公开”的用户控件的 Dependency属性 基本上就像一个盒子,您可以将数据从其他 Xaml 文件(例如 MainPage.xaml ).数据可以是文本、class 对象(包括您的视图模型)等
// 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 传递给用户控件。
编辑:通过空白用户控件解决,而不是自定义控件(参见下面的解决方案) 我基本上是在尝试将复杂的 XAML 内容从 MainPage 模块化到用户控件中,因此我需要弄清楚如何将 MainPage.Xaml 中的本地 DataContext 注入到我的用户控件中。
按照 MSDN 文档的自定义用户控件教程,我的 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 中,我尝试通过本地绑定访问 BasketItem 的数据,并通过我传入的 DependencyProperty (MyBasketItem),
<!-- \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 传递时,我收到“MyBasketItem 在 Windows 通用项目中不受支持”
我知道对于 Name(),我可以简单地将其作为 DependencyProperty 传入;我的目标是引用另一个变量 BasketItemNutri(),它是 BasketItem 的子 ViewModel 变量。所以我需要找到一种方法,将 BasketItem 或 BasketItemNutri 从 MainPage 传递到自定义用户控件中。
如有任何帮助,我们将不胜感激。我该怎么办?
So I need to figure out a way to either pass in BasketItem, or BasketItemNutri, from the MainPage into custom user control.
你把NutriDetailsControl
放在了DataTemplate
里,所以你不能直接在后面的代码里用x:Bind
绑定属性。对于这种情况,更好的方法是使用绑定元素名称然后找到它 DataContext
MyBasketItem="{Binding ElementName=ItemList,Path=DataContext.MyBasketItem}"
请注意,您需要特定的当前页面作为页面的 DataContext。
public MainPage()
{
this.InitializeComponent();
DataContext = this;
}
更多细节请参考这个案例
已解决
我无法让 Nico 的解决方案为我工作,因为我仍然需要以某种方式在 NutriDetailsControl.xaml 内绑定到 MyBasketItem,而且我尝试的任何排列似乎都不起作用。
阅读 Data Binding in Depth 之后,我想也许我应该忘记整个自定义控件的情况,而只使用一个名为 NutriControl 的空白用户控件。
我通过添加
在NutriControl.idl(空白用户控件)中创建了一个依赖项属性static Windows.UI.Xaml.DependencyProperty MyBasketItemProperty{ get; };
BasketItem MyBasketItem;
(注意 MyBasketItem属性 和 MyBasketItem; 必须相同)。通过 NutriControl.idl 文件“公开”的用户控件的 Dependency属性 基本上就像一个盒子,您可以将数据从其他 Xaml 文件(例如 MainPage.xaml ).数据可以是文本、class 对象(包括您的视图模型)等
// 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 传递给用户控件。