【问题标题】:Passing Non-Item Values to Properties in ItemTemplate将非项值传递给 ItemTemplate 中的属性
【发布时间】:2015-01-09 20:42:51
【问题描述】:

今天我无法将值从父控件传递到列表中子控件的属性。

我有一个自定义控件,我已经制作了它作为缩略图复选框的功能。本质上,它只是一个包裹在带有一些漂亮边框的图像周围的复选框。它全部封装到一个 DLL 中并部署为自定义控件

如果我想使用控件的单个实例,我可以这样做...

<tcb:ThumbnailCheckBox IsChecked="True"
                       ImagePath="D:\Pictures\123.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 属性外,这完全符合我的要求。 ImagePathSelected 都是绑定到 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,但在另一个UserControl 上,其ViewModel 没有ThumbnailSize 属性,但使用了另一个名称?

你知道我要做什么了...当前的解决方案不是很可扩展,并且仅限于当前的类,因为它们的名称是准确的。

事实上,在一个完美的世界中,我希望为 ImagePathSelected 属性设置变量名,但这是一个不同的讨论。

【问题讨论】:

    标签: c# wpf xaml mvvm data-binding


    【解决方案1】:

    可以使用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(附加)属性。

    【讨论】:

    • 对原始帖子进行了编辑以询问有关此解决方案的一些问题。谢谢!
    • @imdandman;是的,你说的很有道理。您可以绑定附加属性(更新帖子)。您可能应该创建一个新的UserControl 并将ListBox 包装在其中,并公开ThumbnailSize 属性。附加的属性看起来很可疑。
    • 做到了!谢谢!我将为圆角半径和边距属性复制它。快速的问题 - 有没有一种“更清洁”的方式来代替使用附加的属性?为它们创建附加属性有点麻烦,但它很有效,而且比我见过的任何其他中途解决方案都简单得多。再次感谢!这太棒了。
    • @imdanman:是的,这里不一定推荐附加属性。您应该创建新的UserControl,并在那里添加依赖属性(未附加)。创建新控件,将其命名为 YourImageGallery 或其他任何名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-23
    • 1970-01-01
    • 2013-01-30
    • 2023-04-04
    • 1970-01-01
    • 2023-03-10
    相关资源
    最近更新 更多