How to use Attached property within a style?

21,460

Here is how you can set your attached property in a style

<Style x:Key="ToolBarButtonStyle" TargetType="Button">
    <Setter Property="PrismExt:ImgSourceAttachable.ImgSource"
            Value="./Images/New.png"/>
    <!--...-->
</Style>

When binding to attached properties then the Path should be within parentheses so try to use RelativeSource Binding with TemplatedParent instead

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="Button">
            <Image x:Name="toolbarImage"
                    Source="{Binding RelativeSource={RelativeSource TemplatedParent},
                                    Path=(PrismExt:ImgSourceAttachable.ImgSource)}"
                    Width="48"
                    Height="48">
            </Image>
        </ControlTemplate>
    </Setter.Value>
</Setter>

Edit: The above code works in WPF, in Silverlight the Image shows in runtime but it fails in the designer with an exception. You can use the following code in the PropertyChangedCallback to get the Image as a workaround

private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    Button button = d as Button;
    Image image = GetVisualChild<Image>(button);
    if (image == null)
    {
        RoutedEventHandler loadedEventHandler = null;
        loadedEventHandler = (object sender, RoutedEventArgs ea) =>
        {
            button.Loaded -= loadedEventHandler;
            button.ApplyTemplate();
            image = GetVisualChild<Image>(button);
            // Here you can use the image
        };
        button.Loaded += loadedEventHandler;
    }
    else
    {
        // Here you can use the image
    }
}
private static T GetVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    T child = default(T);

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++)
    {
        DependencyObject v = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
            child = GetVisualChild<T>(v);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}
Share:
21,460
Houman
Author by

Houman

I'm a thinker and a dreamer. Love pets but don't have any. I'm a passionate tech entrepreneur.

Updated on November 30, 2020

Comments

  • Houman
    Houman over 3 years

    I have created an Image within a ButtonStyle. Now I have created an Attached Property so that I can set the Source for that Image. Should be straight forward but I am stuck with it.

    This is my shortened ButtonStyle:

    <Style x:Key="ToolBarButtonStyle"
            TargetType="Button">
        ...
        <Image x:Name="toolbarImage"
                Source="{TemplateBinding PrismExt:ImageSourceAttachable:ImageSource}"
                Width="48"
                Height="48" />
        ...
    </Style>
    

    And this is the attached property definition, Note that I have no idea how to fix the callback, as the dependencyproperty seems to be the button instead of the image. And Button doesn't expose my Image within its style. Its tricky.

    namespace SalesContactManagement.Infrastructure.PrismExt
    {
        public class ImgSourceAttachable
        {
            public static void SetImgSource(DependencyObject obj, string imgSource)
            {
                obj.SetValue(ImgSourceProperty, imgSource);
            }
    
            public static string GetImgSource(DependencyObject obj)
            {
                return obj.GetValue(ImgSourceProperty).ToString();
            }
    
            // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty ImgSourceProperty =
                DependencyProperty.RegisterAttached("ImgSource", typeof(string), typeof(ImgSourceAttachable), new PropertyMetadata(Callback));
    
            private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                //((Button)d).Source = new BitmapImage(new Uri(Application.Current.Host.Source, e.NewValue.ToString()));
            }
        }
    }
    

    This is how I set the image source within XAML:

    <Button PrismExt:ImgSourceAttachable.ImgSource="./Images/New.png"
            Style="{StaticResource ToolBarButtonStyle}" />
    

    Any ideas please? Many Thanks,

  • Houman
    Houman almost 13 years
    Thank you. This code compiles now, however I still dont see the images. I am pretty sure its because the CallBack of Attached Property is empty. However the "DependencyObject d" there is the button itself. I dont understand...
  • Fredrik Hedblad
    Fredrik Hedblad almost 13 years
    I just noticed your question had both WPF and Silverlight tags, my answer was for WPF. I'll try it out for Silverlight.
  • Houman
    Houman almost 13 years
    You are right. apologies. I thought regarding attached properties they would be no different. :)
  • Fredrik Hedblad
    Fredrik Hedblad almost 13 years
    Updated my answer. That code worked in runtime on my end, but failed in the designer. Added code to get the image in the callback
  • Houman
    Houman almost 13 years
    Thank you so much. It works like a charm. I don't think I could have done it without your help. :)
  • Fredrik Hedblad
    Fredrik Hedblad almost 13 years
    Sure thing, glad I could help!