Пользовательский элемент управления WPF: привязка шаблона к изображению


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

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

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

Мое решение VS 2008 содержит один проект CustomControlDemo. Проект содержит пользовательский элемент управления TaskButton.cs, и главное окно, Window1.xaml, который я использую для тестирования элемента управления. Мой тестовый образ, календарь.png, находится в Папка ресурсов на корневом уровне проекта и общие.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="Resourcescalendar.png" Text="Calendar" />
    </Grid>
</Window>

Есть идеи, почему образный путь не работает? Спасибо снова.

4 7

4 ответа:

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

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

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

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

Но мне не нужно использовать 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.

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

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

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

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

Причина того, что длинная форма привязки работает, а ярлык-нет, заключается в том, что она предоставляет компилятору ключи к источнику поставляемого образа (Resources\calendar.png) является относительным путем, а не абсолютным путем, поэтому изображение найдено и привязка работает. Если вы отладите привязку, вы увидите, что ярлык пытается разрешить предоставленную строку в Источник изображения, но не может найти файл " Resources\calendar.png " если вы предоставляете полный URI к образу, то есть "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}}"/>