Пользовательский элемент управления WPF: TemplateBinding to Image

я создаю пользовательский элемент управления WPF, a Button С Image и Text. Я добавил два свойства зависимостей к элементу управления,ImagePath и Text и шаблон элемента управления (в ThemesGeneric.xaml) - это простая панель стека, которая упорядочивает изображение и текст по горизонтали.

на Text свойства работает отлично. Но по какой-то причине образец изображения в моем тестовом проекте не появляется, когда я использую TemplateBinding до ImagePath свойство зависимостей, чтобы получить его путь. У меня есть протестировал изображение, временно заменив TemplateBinding в пользовательском элементе управления с путем к изображению, в этом случае он появляется.

я надеюсь, что кто-то с большим опытом в этой области может взглянуть и сказать мне, почему контроль не работает, как ожидалось. Спасибо за помощь.

мое решение VS 2008 содержит один проект CustomControlDemo. Проект содержит пользовательский элемент управления, TaskButton.CS, а главное окно, окно window1.xaml, который я использую для тестирования элемента управления. Мой тестовый образ, календарь.png, находится в папке ресурсов на корневом уровне проекта и является универсальным.xaml находится в папке темы, Также на корневом уровне проекта.

вот код для моего пользовательского элемента управления (от 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="Resourcescalendar.png" Text="Calendar" />
    </Grid>
</Window>

любые идеи, почему путь изображения не работает? Спасибо снова.

4 ответов


изображение не принимает строку в качестве источника :) вы можете увидеть это в intellisense. Вам нужно привязать к ImageSource (или использовать IValueConverter для преобразования строки в ImageSource)

посмотреть этой вопрос для некоторых советов о том, как сделать это преобразование.


Я собираюсь оставить ответ cwap в качестве принятого ответа, потому что он технически правильный. Однако оказывается, что есть более простой способ решить эту проблему.

TemplateBindings не являются объектами привязки первого класса. Они разработаны, чтобы быть легкими, поэтому они односторонние, и им не хватает некоторых функций других объектов привязки. В частности, они не поддерживают известные преобразователи типов, связанные с целью. См. Макдональд,Pro WPF в C# 2008, p. 872. Вот почему cwap правильно отвечает, что мне, вероятно, нужно будет создать конвертер типов и ссылаться на него конкретно в шаблоне элемента управления для моей пользовательской кнопки.

но мне не нужно использовать TemplateBinding для привязки шаблона элемента управления к свойству ImagePath моего пользовательского элемента управления. Я могу использовать простой старый объект привязки. Вот пересмотренная разметка для шаблона моего пользовательского элемента управления:

<!-- 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} позволяет мне ввести относительный путь в моем экземпляре Window1 кнопки TaskButton и правильно разрешить его в пользовательском элементе управления.

поэтому моей рекомендацией для других, исследующих этот поток, было бы пропустить преобразователь значений и просто переключиться с TemplateBinding на Binding для свойства Image.

спасибо Марко Чжоу, который предоставил ответ к аналогичному вопросу на форуме MSDN WPF.


на самом деле ни один из этих ответов являются правильными.

{TemplateBinding ImagePath} Это не что иное, как ярлык для {Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}} и как таковой почти полностью идентичны.

кроме того, если вы предоставляете строку ImagePath он правильно разрешится до ImageSource хотя вы принимаете удар в производительности приложения. Реальная проблема связана с относительным и абсолютным путем изображения на поставляемом ImagePath="Resources\calendar.png" в xaml для теста. Это подсказывает компилятору думать, что предоставленный путь абсолютной из-за использования \ вместо / в определении пути.

причина того, что длинная форма привязки работает, а ярлык-нет, заключается в том, что она предоставляет подсказки компилятору, который предоставил источник изображения (Resources\calendar.png) - относительный путь, а не абсолютный путь, поэтому изображение найдено и привязка работает. При отладке привязки вы увидите, что ярлык пытается разрешить предоставленную строку в Источник изображения, но не может найти файл "Ресурсы\календарь.png", если вы предоставите полный URI изображению i.e "C:\...\Resources\calendar.png" или соответствующее обозначение смеси "/application;component/Resources/calendar.png" затем изображение будет найдено и привязка разрешена.

этот момент становится действительно важным, когда вы пытаетесь ссылаться на изображения из внешнего источника вместо тех, которые скомпилированы в качестве ресурсов в окончательную компиляцию.


простой способ(проверено) 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-привязать Источник изображения к родительской строке properety (я использовал свойство " 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}}"/>