Пользовательский элемент управления WPF: привязка шаблона к изображению - PullRequest
7 голосов
/ 20 декабря 2009

Я создаю пользовательский элемент управления WPF, Button с Image и Text. Я добавил два свойства зависимостей в элемент управления 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 ]

10 голосов
/ 20 декабря 2009

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

TemplateBindings не являются первоклассными объектами Binding. Они спроектированы так, чтобы быть легкими, поэтому они односторонние и им не хватает некоторых функций других объектов Binding. В частности, они не поддерживают известные преобразователи типов, связанные с целью. См. MacDonald, Pro WPF в 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} позволяет мне вводить относительный путь в моем экземпляре Window1 TaskButton и правильно разрешать его в пользовательском элементе управления.

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

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

4 голосов
/ 20 декабря 2009

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

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

2 голосов
/ 06 декабря 2011

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

{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", тогда изображение будет найдено и привязка разрешена.

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

0 голосов
/ 18 августа 2015

простой способ (проверено) 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}}"/>
...