2009-12-20 12 views
7

Estoy creando un control personalizado WPF, un Button con un y Text. He agregado dos propiedades de dependencia al control, ImagePath y Text, y la plantilla de control (en Themes \ Generic.xaml) es un panel de pila simple que organiza la imagen y el texto de forma horizontal.WPF Custom Control: TemplateBinding to Image

La propiedad Text funciona bien. Pero por alguna razón, la imagen de muestra en mi proyecto de prueba no aparece cuando utilizo TemplateBinding en la propiedad de dependencia ImagePath para obtener su ruta. He probado la imagen al reemplazar temporalmente el TemplateBinding en el control personalizado con una ruta a la imagen, en cuyo caso aparece.

Espero que alguien con más experiencia en esta área pueda echar un vistazo y decirme por qué el control no está funcionando como se esperaba. Gracias por tu ayuda.

Mi solución VS 2008 contiene un proyecto, CustomControlDemo. El proyecto contiene un control personalizado, TaskButton.cs y una ventana principal, Window1.xaml, que utilizo para probar el control. Mi imagen de prueba, calendar.png, se encuentra en una carpeta de Recursos en el nivel raíz del proyecto, y Generic.xaml se encuentra en una carpeta Temas, también en el nivel raíz del proyecto.

Aquí es el código de mi control personalizado (de 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 
    } 
} 

Y aquí es la plantilla de control (de 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> 

Y, por último, aquí es el marcado Window1 que estoy usando para probar el control:

<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> 

Cualquier idea por qué la ruta de la imagen no está WOR ¿Rey? Gracias de nuevo.

Respuesta

4

La imagen no toma una cadena como fuente :) Puede ver esto en intellisense. Necesita vincular un ImageSource (O utilice un IValueConverter para convertir el string a un ImageSource)

Consulte la pregunta this para obtener algunos consejos sobre cómo hacer esta conversión.

9

Voy a dejar la respuesta de cwap como la respuesta aceptada, porque es técnicamente correcta. Sin embargo, resulta que hay una manera más fácil de resolver este problema.

TemplateBindings no son objetos de encuadernación de primera clase. Están diseñados para ser ligeros, por lo que son unidireccionales, y carecen de algunas características de otros objetos vinculantes. En particular, no son compatibles con convertidores de tipo conocidos asociados con un objetivo. Consulte MacDonald, Pro WPF en C# 2008, p. 872. Es por eso que cwap responde correctamente que probablemente necesite crear un convertidor de tipo y hacer referencia a él específicamente en la plantilla de control para mi botón personalizado.

Pero no tengo que usar un TemplateBinding para vincular la plantilla de control a la propiedad ImagePath de mi control personalizado. Puedo usar un objeto simple de enlace antiguo.Aquí está el marcado revisada para la plantilla de mi control personalizado:

<!-- 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> 

Si nos fijamos en la ImageControl en la plantilla, se puede ver el cambio. Tenga en cuenta la propiedad RelativeSource en el mismo objeto. Establecer esta propiedad en = {RelativeSource TemplatedParent} es lo que me permite ingresar una ruta relativa en mi instancia Window1 del TaskButton y que se resuelva correctamente en el control personalizado.

Así que mi recomendación para los demás que investigan este hilo sería omitir el convertidor de valor y simplemente cambiar de TemplateBinding a Enlace para la propiedad de la Imagen.

Gracias también a Marco Zhou, quien proporcionó this answer a una pregunta similar en el foro de MSDN WPF.

2

Actualmente, ninguna de estas respuestas es correcta.

{TemplateBinding ImagePath} no es más que un atajo para {Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}} y como tal es casi completamente idéntico.

Además, si proporciona una cadena para ImagePath, se resolverá correctamente a ImageSource, aunque tendrá un impacto en el rendimiento de la aplicación. El problema real tiene que ver con la ruta de imagen relativa y absoluta en el ImagePath="Resources\calendar.png" suministrado en el xaml para la prueba. Esto induce al compilador a pensar que la ruta suministrada es absoluta debido al uso de \ en lugar de/en la definición de la ruta.

La razón por la que la forma larga del enlace funciona y el acceso directo no es porque proporciona pistas al compilador de que el origen de la imagen proporcionada (Resources \ calendar.png) es una ruta relativa, no una ruta absoluta , por lo tanto, se encuentra la imagen y el enlace funciona. Si depura el enlace, verá que el atajo intenta resolver la cadena suministrada en un origen de imagen, pero no puede encontrar el archivo "Resources \ calendar.png" si proporciona un URI completo a la imagen, es decir, "C:\...\Resources\calendar.png" o la notación de combinación correspondiente de "/application;component/Resources/calendar.png" luego se encontrará la imagen y se resolverá el enlace.

Este punto se vuelve realmente importante cuando intenta hacer referencia a imágenes de una fuente externa en lugar de aquellas compiladas como recursos en la compilación final.

0

forma sencilla (probado) 1-hacer su valueConverter como esto

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-vincular a su imagen de la fuente de properety cadena de los padres (i utiliza la propiedad "etiqueta") como esto 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}}"/>