将非项目值传递给 ItemTemplate 中的属性
Passing Non-Item Values to Properties in ItemTemplate
今天我在将值从父控件向下传递到列表中子控件的属性时遇到问题。
我有一个自定义控件,它用作缩略图复选框。从本质上讲,它只是一个带有一些漂亮边框的图像周围的复选框。它全部封装到一个 DLL 中并部署为自定义控件
如果我想使用控件的单个实例,我可以这样做...
<tcb:ThumbnailCheckBox IsChecked="True"
ImagePath="D:\Pictures3.jpg"
CornerRadius="10"
Height="{Binding ThumbnailSize}"
Margin="10" />
代码清单 1 - 一次性使用
效果很好,可以轻松绑定到我的 ViewModel 上的 ThumbnailSize,因此我可以根据需要更改控件中图像的大小。
问题是当我想将此控件的使用扩展到列表中时,我 运行 遇到了一些问题。
首先,我设计了 ListBox 控件的样式以满足我的需求...
<Style TargetType="{x:Type ListBox}"
x:Key="WrappingImageListBox">
<!-- Set the ItemTemplate of the ListBox to a DataTemplate
which explains how to display an object of type BitmapImage. -->
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{TemplateBinding utilities:MyAttachedProperties.ImageSize}"
CornerRadius="8"
Margin="10">
</tcb:ThumbnailCheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- Swap out the default items panel with a WrapPanel so that
the images will be arranged with a different layout. -->
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<!-- Set this attached property to 'Disabled' so that the
ScrollViewer in the ListBox will never show a horizontal
scrollbar, and the WrapPanel it contains will be constrained
to the width of the ScrollViewer's viewable surface. -->
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
</Style>
代码清单 2 - 列表框样式
从我的主要观点来看,我是这样称呼它的...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent"
util:MyAttachedProperties.ImageSize="500"/>
代码清单 3 - 主调用
除 ImageSize
属性 外,这完全符合我的要求。 ImagePath
和 Selected
都是绑定到 ListBox
.
的单个列表项的属性
如您所见,我创建了一个附加的 属性 来尝试传递值 (500
),但它似乎不起作用。我应该注意到,我认为我创建的样式是正确的,因为元素使用默认值。
public static class MyAttachedProperties
{
public static double GetImageSize(DependencyObject obj)
{
return (double)obj.GetValue(ImageSizeProperty);
}
public static void SetImageSize(DependencyObject obj, double value)
{
obj.SetValue(ImageSizeProperty, value);
}
public static readonly DependencyProperty ImageSizeProperty =
DependencyProperty.RegisterAttached(
"ImageSize",
typeof(double),
typeof(MyAttachedProperties),
new FrameworkPropertyMetadata(50D));
}
代码清单 4 - 附后 属性
最后一行指定的 50D
应用于列出的控件。如果我更改它并重新编译,最终结果会发生变化。但是我在 ListBox
主调用(清单 3)中指定的 500
的发送值从未发送过。当然,我最终想在我的视图模型上将 500
更改为绑定 属性,但在我使用显式值之前我不会这样做。
谁能帮我弄清楚如何从我的 ListBox
主调用(清单 3)中发送一个值并将其应用于由模板填充的各个项目?我有其他属性,但它们是我绑定到 ListBox
的列表中每个项目的属性,而 ImageSize
不是。
编辑以解决第一反应
这似乎有效,但有点奇怪。我的列表框现在被这样调用...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent" />
并且我已将我的风格更改为您建议的代码...
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{Binding Path=DataContext.ThumbnailSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}}"
CornerRadius="8"
Margin="10">
我唯一担心的是,现在样式正在直接访问该控件的 ViewModel,而不是接收绑定值。
假设我想再次使用 ListBox
,但是在 ViewModel
没有 ThumbnailSize
属性 的另一个 UserControl
上使用了一个用另一个名字?
你知道我要用这个做什么......当前的解决方案不是很可扩展,并且仅限于当前的 类,因为它们的名称是准确的。
事实上,在一个完美的世界中,我想要 ImagePath
和 Selected
属性的变量名称,但那是一个不同的讨论。
可以使用FindAncestor
。其思想是,child遍历逻辑树,并尝试找到具体类型的parent(在本例中为ListBox
),然后访问附加的属性。有关更多绑定表达式,请参阅 http://wpftutorial.net/BindingExpressions.html。
在您的 ItemTemplate
中,您可以通过以下方式访问 ThumbnailSize
属性:
{Binding Path=(util:MyAttachedProperties.ImageSize),
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}}}
本质上,这里问的问题有点相反,但结果是一样的。 “ListBox
中的项目如何访问 ListBox
(附加)属性。
今天我在将值从父控件向下传递到列表中子控件的属性时遇到问题。
我有一个自定义控件,它用作缩略图复选框。从本质上讲,它只是一个带有一些漂亮边框的图像周围的复选框。它全部封装到一个 DLL 中并部署为自定义控件
如果我想使用控件的单个实例,我可以这样做...
<tcb:ThumbnailCheckBox IsChecked="True"
ImagePath="D:\Pictures3.jpg"
CornerRadius="10"
Height="{Binding ThumbnailSize}"
Margin="10" />
代码清单 1 - 一次性使用
效果很好,可以轻松绑定到我的 ViewModel 上的 ThumbnailSize,因此我可以根据需要更改控件中图像的大小。
问题是当我想将此控件的使用扩展到列表中时,我 运行 遇到了一些问题。
首先,我设计了 ListBox 控件的样式以满足我的需求...
<Style TargetType="{x:Type ListBox}"
x:Key="WrappingImageListBox">
<!-- Set the ItemTemplate of the ListBox to a DataTemplate
which explains how to display an object of type BitmapImage. -->
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{TemplateBinding utilities:MyAttachedProperties.ImageSize}"
CornerRadius="8"
Margin="10">
</tcb:ThumbnailCheckBox>
</DataTemplate>
</Setter.Value>
</Setter>
<!-- Swap out the default items panel with a WrapPanel so that
the images will be arranged with a different layout. -->
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<!-- Set this attached property to 'Disabled' so that the
ScrollViewer in the ListBox will never show a horizontal
scrollbar, and the WrapPanel it contains will be constrained
to the width of the ScrollViewer's viewable surface. -->
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
</Style>
代码清单 2 - 列表框样式
从我的主要观点来看,我是这样称呼它的...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent"
util:MyAttachedProperties.ImageSize="500"/>
代码清单 3 - 主调用
除 ImageSize
属性 外,这完全符合我的要求。 ImagePath
和 Selected
都是绑定到 ListBox
.
如您所见,我创建了一个附加的 属性 来尝试传递值 (500
),但它似乎不起作用。我应该注意到,我认为我创建的样式是正确的,因为元素使用默认值。
public static class MyAttachedProperties
{
public static double GetImageSize(DependencyObject obj)
{
return (double)obj.GetValue(ImageSizeProperty);
}
public static void SetImageSize(DependencyObject obj, double value)
{
obj.SetValue(ImageSizeProperty, value);
}
public static readonly DependencyProperty ImageSizeProperty =
DependencyProperty.RegisterAttached(
"ImageSize",
typeof(double),
typeof(MyAttachedProperties),
new FrameworkPropertyMetadata(50D));
}
代码清单 4 - 附后 属性
最后一行指定的 50D
应用于列出的控件。如果我更改它并重新编译,最终结果会发生变化。但是我在 ListBox
主调用(清单 3)中指定的 500
的发送值从未发送过。当然,我最终想在我的视图模型上将 500
更改为绑定 属性,但在我使用显式值之前我不会这样做。
谁能帮我弄清楚如何从我的 ListBox
主调用(清单 3)中发送一个值并将其应用于由模板填充的各个项目?我有其他属性,但它们是我绑定到 ListBox
的列表中每个项目的属性,而 ImageSize
不是。
编辑以解决第一反应
这似乎有效,但有点奇怪。我的列表框现在被这样调用...
<ListBox ItemsSource="{Binding DirectoryPictures}"
Grid.Row="1"
Style="{DynamicResource WrappingImageListBox}"
Background="Transparent" />
并且我已将我的风格更改为您建议的代码...
<tcb:ThumbnailCheckBox ImagePath="{Binding ImagePath}"
IsChecked="{Binding Selected}"
Height="{Binding Path=DataContext.ThumbnailSize, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}}"
CornerRadius="8"
Margin="10">
我唯一担心的是,现在样式正在直接访问该控件的 ViewModel,而不是接收绑定值。
假设我想再次使用 ListBox
,但是在 ViewModel
没有 ThumbnailSize
属性 的另一个 UserControl
上使用了一个用另一个名字?
你知道我要用这个做什么......当前的解决方案不是很可扩展,并且仅限于当前的 类,因为它们的名称是准确的。
事实上,在一个完美的世界中,我想要 ImagePath
和 Selected
属性的变量名称,但那是一个不同的讨论。
可以使用FindAncestor
。其思想是,child遍历逻辑树,并尝试找到具体类型的parent(在本例中为ListBox
),然后访问附加的属性。有关更多绑定表达式,请参阅 http://wpftutorial.net/BindingExpressions.html。
在您的 ItemTemplate
中,您可以通过以下方式访问 ThumbnailSize
属性:
{Binding Path=(util:MyAttachedProperties.ImageSize),
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}}}
本质上,这里问的问题有点相反,但结果是一样的。 “ListBox
中的项目如何访问 ListBox
(附加)属性。