WPF 自定义控件:使用 TemplateBinding 绑定 Image

7
我正在创建一个WPF自定义控件,这是一个带有图像和文本的按钮。我已经为该控件添加了两个依赖属性:ImagePath和Text,并且控件模板(在Themes\Generic.xaml中)是一个简单的堆栈面板,用于水平排列图像和文本。
Text属性可以正常工作。但是由于某种原因,在使用TemplateBinding将ImagePath依赖属性绑定到路径时,我的测试项目中的示例图像不会出现。我已经通过暂时将自定义控件中的TemplateBinding替换为图像路径来测试了该图像,这样它就出现了。
我希望有更多经验的人能够查看并告诉我为什么控件不能按预期工作。谢谢你的帮助。
我的VS 2008解决方案包含一个名为CustomControlDemo的项目。该项目包含一个自定义控件TaskButton.cs和一个主窗口Window1.xaml,我用它来测试该控件。我的测试图像calendar.png位于项目根目录下的Resources文件夹中,而Generic.xaml位于项目根目录下的Themes文件夹中。
以下是我的自定义控件代码(来自TaskButton.cs):
using System.Windows;
using System.Windows.Controls;

namespace CustomControlDemo
{
    public class TaskButton : RadioButton
    {
        #region Fields

        // Dependency property backing variables
        public static readonly DependencyProperty ImagePathProperty;
        public static readonly DependencyProperty TextProperty;

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        static TaskButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TaskButton), new FrameworkPropertyMetadata(typeof(TaskButton)));

            // Initialize ImagePath dependency properties
            ImagePathProperty = DependencyProperty.Register("ImagePath", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
            TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
        }

        #endregion

        #region Dependency Property Wrappers

        /// <summary>
        /// The ImagePath dependency property.
        /// </summary>
        public string ImagePath
        {
            get { return (string)GetValue(ImagePathProperty); }
            set { SetValue(ImagePathProperty, value); }
        }

        /// <summary>
        /// The Text dependency property.
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        #endregion
    }
}

这是控件模板(来自Generic.xaml):

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:CustomControlDemo">


    <Style TargetType="{x:Type local:TaskButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TaskButton}">
                    <StackPanel Height="Auto" Orientation="Horizontal">
                        <Image Source="{TemplateBinding ImagePath}"  Width="24" Height="24" Stretch="Fill"/>
                        <TextBlock Text="{TemplateBinding Text}"  HorizontalAlignment="Left" Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold"  Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" />
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

最后,这是我用来测试控件的Window1标记:

<Window x:Class="CustomControlDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:customControl="clr-namespace:CustomControlDemo"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <customControl:TaskButton ImagePath="Resources\calendar.png" Text="Calendar" />
    </Grid>
</Window>

有什么原因导致图像路径无法使用呢?再次感谢。
4个回答

11

我将保留cwap的答案作为被接受的答案,因为它在技术上是正确的。然而,事实证明解决这个问题有一种更简单的方法。

TemplateBindings不是一等绑定对象。它们被设计成轻量级的,因此它们是单向的,并且缺少其他绑定对象的一些特性。最显着的是,它们不支持与目标关联的已知类型转换器。参见MacDonald,《Pro WPF in C# 2008》,第872页。这就是为什么cwap回应正确,我可能需要创建一个类型转换器,并在我的自定义按钮的控件模板中专门引用它。

但是,我不必使用TemplateBinding来将控件模板绑定到我的自定义控件的ImagePath属性。我可以使用普通的Binding对象。下面是我自定义控件模板的修改后的标记:

<!-- Task Button Default Control Template-->
<Style TargetType="{x:Type local:TaskButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TaskButton}">
                <StackPanel Height="Auto" Orientation="Horizontal">
                    <Image Source="{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}" Width="24" Height="24" Stretch="Fill" Margin="10,0,0,0" />
                    <TextBlock Text="{TemplateBinding Text}"  HorizontalAlignment="Left" Foreground="{TemplateBinding Foreground}" FontWeight="Bold"  Margin="5,0,10,0" VerticalAlignment="Center" FontSize="12" />
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如果您查看模板中的ImageControl,您可以看到变化。请注意同一对象中的RelativeSource属性。将此属性设置为={RelativeSource TemplatedParent}是让我在TaskButton的Window1实例中输入相对路径并且能够在自定义控件中正确解析的原因。

因此,我建议其他人在研究这个线程时跳过值转换器,并将Image属性从TemplateBinding更改为Binding。

同时感谢Marco Zhou在MSDN WPF论坛上提供了类似问题的答案


4

图片的源不接受字符串作为参数 :) 你可以在 intellisense 中看到这一点。你需要绑定一个 ImageSource(或使用 IValueConverter 将字符串转换为 ImageSource)。

查看这个问题获取如何进行此转换的一些提示。


2
实际上,这两个答案都不正确。{TemplateBinding ImagePath}只是{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}的缩写,因此几乎完全相同。如果为ImagePath提供字符串,则它将正确解析为ImageSource,尽管会影响应用程序性能。真正的问题与在测试xaml中提供的ImagePath="Resources\calendar.png"的相对和绝对图像路径有关。这提示编译器认为所提供的路径是绝对的,因为使用了\而不是/来定义路径。长格式的绑定之所以有效而快捷方式无效,是因为它向编译器提供了提示,表明所提供的图像源(Resources\calendar.png)是相对路径而不是绝对路径,因此找到了图像并解析了绑定。如果调试绑定,您将看到快捷方式尝试将提供的字符串解析为图像源,但找不到文件"Resources\calendar.png"。如果提供图像的完整URI,即"C:\...\Resources\calendar.png"或相应的混合符号"/application;component/Resources/calendar.png",则可以找到图像并解析绑定。当您尝试引用来自外部源而不是编译为最终编译资源的图像时,这一点变得非常重要。

0

简单的方法(已测试) 1-将您的valueConverter设置为以下内容

  public class objectToImageSourceConverter:IValueConverter
    {

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {

            string packUri =value.ToString();
            ImageSource Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
            return Source;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

2-将您的图像源绑定到父级的字符串属性(我使用了“tag”属性),XAML代码如下:

<Image HorizontalAlignment="Right"  Height="Auto" Margin="0,11.75,5.5,10.75" VerticalAlignment="Stretch" Width="40.997" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接