2009-12-20 11 views
7

Sto creando un controllo personalizzato WPF, un Button con un Image e Text. Ho aggiunto due proprietà di dipendenza al controllo, ImagePath e Text, e il modello di controllo (in Themes \ Generic.xaml) è un pannello di stack semplice che dispone l'immagine e il testo orizzontalmente.Controllo personalizzato WPF: TemplateBinding to Image

La proprietà Text funziona correttamente. Ma per qualche motivo, l'immagine di esempio nel mio progetto di test non viene visualizzata quando utilizzo la proprietà di dipendenza per ottenere il suo percorso. Ho testato l'immagine sostituendo temporaneamente il TemplateBinding nel controllo personalizzato con un percorso per l'immagine, nel qual caso appare.

Spero che qualcuno con più esperienza in questo settore possa dare un'occhiata e dirmi perché il controllo non funziona come previsto. Grazie per l'aiuto.

La soluzione VS 2008 contiene un progetto, CustomControlDemo. Il progetto contiene un controllo personalizzato, TaskButton.cs e una finestra principale, Window1.xaml, che utilizzo per testare il controllo. La mia immagine di prova, calendar.png, si trova in una cartella Risorse al livello root del progetto e Generic.xaml si trova in una cartella Temi, anche a livello di root del progetto.

Ecco il codice per il mio controllo personalizzato (da 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 
    } 
} 

E qui è il modello di controllo (da 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> 

E, infine, ecco è il markup Window1 che sto usando per testare il controllo:

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

Tutte le idee perché il percorso dell'immagine non è wor re? Grazie ancora.

risposta

4

L'immagine non accetta una stringa come sorgente :) È possibile vederlo in intellisense. È necessario associare a un ImageSource (o utilizzare un IValueConverter per convertire la stringa in un ImageSource)

Vedere la domanda this per alcuni suggerimenti su come eseguire questa conversione.

9

Ho intenzione di lasciare la risposta di cwap come risposta accettata, perché è tecnicamente corretta. Tuttavia, risulta che esiste un modo più semplice per risolvere questo problema.

TemplateBindings non sono oggetti Binding di prima classe. Sono progettati per essere leggeri, quindi sono a senso unico e mancano alcune funzionalità di altri oggetti Binding. In particolare, non supportano i convertitori di tipi noti associati a un target. Vedi MacDonald, Pro WPF in C# 2008, p. 872. Ecco perché cwap risponde correttamente che probabilmente avrei bisogno di creare un convertitore di tipi e di fare un riferimento specifico nel modello di controllo per il mio pulsante personalizzato.

Ma non è necessario utilizzare un TemplateBinding per associare il modello di controllo alla proprietà ImagePath del controllo personalizzato. Posso usare un semplice oggetto Binding vecchio.Ecco il markup rivisto per il modello del mio controllo personalizzato:

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

Se si guarda alla ImageControl nel modello, è possibile vedere il cambiamento. Nota la proprietà RelativeSource nello stesso oggetto. L'impostazione di questa proprietà su = {RelativeSource TemplatedParent} è ciò che consente di immettere un percorso relativo nell'istanza Window1 di TaskButton e di risolverlo correttamente nel controllo personalizzato.

Quindi la mia raccomandazione per gli altri alla ricerca di questa discussione sarebbe quella di saltare il convertitore di valori e passare semplicemente da TemplateBinding a Binding per la proprietà Image.

Grazie anche a Marco Zhou, che ha fornito this answer a una domanda simile nel forum WPF MSDN.

2

In realtà nessuna di queste risposte è corretta.

{TemplateBinding ImagePath} non è altro che una scorciatoia per {Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}} e come tale è quasi completamente identico.

Inoltre, se si specifica una stringa per ImagePath, verrà risolto correttamente su ImageSource anche se si riscontra un notevole successo nelle prestazioni dell'applicazione. Il vero problema ha a che fare con il percorso dell'immagine relativo e assoluto sullo ImagePath="Resources\calendar.png" fornito in xaml per il test. Questo induce il compilatore a pensare che il percorso fornito sia assoluto a causa dell'uso di \ invece di/nella definizione del percorso.

Il motivo per cui la forma lunga del collegamento funziona e il collegamento non è che fornisce indicazioni al compilatore che l'origine dell'immagine fornita (Resources \ calendar.png) è un percorso relativo non un percorso assoluto , quindi l'immagine viene trovata e il binding funziona. Se esegui il debug del binding, vedrai che il collegamento tenta di risolvere la stringa fornita in una sorgente di immagini ma non riesce a trovare il file "Resources \ calendar.png" Se fornisci un URI completo all'immagine, ad esempio "C:\...\Resources\calendar.png" o la corrispondente notazione di blend di "/application;component/Resources/calendar.png" quindi l'immagine verrà trovata e l'associazione risolta.

Questo punto diventa davvero importante quando si tenta di fare riferimento a immagini da una fonte esterna anziché a quelle compilate come risorse nella compilazione finale.

0

modo semplice (testato) 1-rendere il vostro ValueConverter come questo

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-vincolare il proprio Fonte immagine per properety stringa del genitore (io ho usato la proprietà "tag") come questo 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}}"/>