tags:

views:

27

answers:

1

Well it's not really very dynamic, at least it won't change at runtime.

The idea is I have buttons and each one has a unique image ( icon 32x32 ). The buttons all share a style where I mess with the ControlTemplate. So each image also has 2 colors one normal and another when I mouse over.

I noticed that when I declare the source path for the images is that they are almost all the same so I though DRY ( Don't Repeat Yourself ). What if I could use the button Name or some other property as part of the source path ( i.e. the name of the image file ). That would be good programming.

Problem is I'm new to XAML, WPF and perhaps programming all together so I'm not sure how to do that. I guess this would need code behind or a converter of some sort ( guess I'll read about converters a bit more ). Here is a bit of code ( this doesn't work but it gives you the general idea ( hopefully )):

<Style x:Key="ButtonPanelBigButton" TargetType="{x:Type Button}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="Button">
                <Border Name="ButtonBorder"
                        Height="78"
                        MaxWidth="70"
                        MinWidth="50"
                        BorderThickness="0.5"
                        BorderBrush="Transparent"
                        CornerRadius="8" >
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="2*" />
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>

                        <!-- Here I wan't to put in the Name property of the button because there is a picture there to match -->
                        <Image x:Name="ButtonIcon" Source="..\Images\Icons\32x32\Blue\{Binding Name}.png" 

                               Margin="4"
                               Height="32"
                               Width="32"
                               HorizontalAlignment="Center" 
                               VerticalAlignment="Center" />

                        <TextBlock Grid.Row="1"
                                   Padding="5,2,5,2"
                                   TextWrapping="Wrap"
                                   Style="{StaticResource MenuText}"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center">
                            <ContentPresenter ContentSource="Content" />                
                        </TextBlock>
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True" >
                        <Setter TargetName="ButtonIcon" Property="Source" Value="..\Images\Icons\32x32\Green\user.png" /> <!-- Same Here -->
                        <Setter TargetName="ButtonBorder" Property="BorderBrush" Value="{StaticResource SecondColorBrush}" />
                        <Setter TargetName="ButtonBorder" Property="Background">
                            <Setter.Value>
                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1" Opacity="0.5">
                                    <GradientStop Color="{StaticResource MainColor}" Offset="1" />
                                    <GradientStop Color="{StaticResource SecondColor}" Offset="0.5" />
                                    <GradientStop Color="{StaticResource MainColor}" Offset="0" />
                                </LinearGradientBrush>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </ControlTemplate.Triggers>

            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Hopefully you get where I'm going with this and someone might be able to help me so my code is nice and DRY ( now I'M repeating myself!!! ).

+1  A: 

You're right: The easy way to solve this is to use a converter.

The Source property takes an ImageSource, so you'll need to load the bitmap yourself in your converter.

The converter is used like this:

 <Image Source="{Binding Name,
                         RelativeSource={RelativeSource TemplatedParent},
                         Converter={x:Static local:ImageSourceLoader.Instance},
                         ConverterParameter=..\Images\Icons\32x32\Blue\{0}.png}" />

And implemented like this:

public class ImageSourceLoader : IValueConverter
{
  public static ImageSourceLoader Instance = new ImageSourceLoader();

  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    var path = string.Format((string)parameter, value.ToString());
    return BitmapFrame.Create(new Uri(path, UriKind.RelativeOrAbsolute));
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

Note that this simple solution can't handle relative Uris because the BaseUri property of the Image is unavailable to the converter. If you want to use relative Uris, you can do this by binding an attached property and using StringFormat:

 <Image local:ImageHelper.SourcePath="{Binding Name,
                         RelativeSource={RelativeSource TemplatedParent},
                         StringFormat=..\Images\Icons\32x32\Blue\{0}.png}" />

And the attached property's PropertyChangedCallback handles loads the image after combining the BaseUri with the formatted Uri string:

public class ImageHelper : DependencyObject
{
  public static string GetSourcePath(DependencyObject obj) { return (string)obj.GetValue(SourcePathProperty); }
  public static void SetSourcePath(DependencyObject obj, string value) { obj.SetValue(SourcePathProperty, value); }
  public static readonly DependencyProperty SourcePathProperty = DependencyProperty.RegisterAttached("SourcePath", typeof(string), typeof(ImageHelper), new PropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
      {
        ((Image)obj).Source =
          BitmapFrame.Create(new Uri(((IUriContext)obj).BaseUri, (string)e.NewValue));
      }
  });
}
Ray Burns
OK trying to get this to work but having problem with string literals and markup extensions because of the Path and {0}
Ingó Vals
OK works like a charm, had to change one property because of literals like this StringFormat={}..\\Images\\Icons\\32x32\\Green\\{0}.png}"
Ingó Vals