0

I have a button in a custom control with an image defined like so in Xaml (I've just added the part of the xaml that specifically refers to the image for the sake of brevity;

<StackPanel>
                                <Image Style="{StaticResource vtlImageStyle}">
                                    <Image.Source>
                                        <Binding Path="FirstImage"
                                                 RelativeSource="{RelativeSource TemplatedParent}">
                                            <Binding.TargetNullValue>
                                                <ImageSource>/ViewToLearn.VtlWpfControls;component/Images/button_rounded_blue_previous24.png</ImageSource>
                                            </Binding.TargetNullValue>
                                        </Binding>
                                    </Image.Source>
                                </Image>
                            </StackPanel>

Now I would like to change this slightly so that the TargetNullValue is actually bound to a dependencyProperty which would then give me the freedom to change the default image based on another criteria.

To this end I created a dependency property of type string to represent the Uri of the image (with its own default value).

Protected Friend Shared ReadOnly DefaultFirstButtonImageUriProperty As DependencyProperty = DependencyProperty.Register("DefaultFirstButtonImageUri", GetType(String), GetType(VtlDataNavigator), New PropertyMetadata("/ViewToLearn.VtlWpfControls;component/Images/button_rounded_blue_first24.png", New PropertyChangedCallback(AddressOf OnDefaultFirstButtonImageUriChanged)))
Private Shared Sub OnDefaultFirstButtonImageUriChanged(ByVal o As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim vtlDataNavigator As VtlDataNavigator = CType(o, VtlDataNavigator)
    If vtlDataNavigator IsNot Nothing Then
        vtlDataNavigator.OnDefaultFirstButtonImageUriChanged(CType(e.OldValue, String), CType(e.NewValue, String))
    End If
End Sub

Protected Overridable Sub OnDefaultFirstButtonImageUriChanged(ByVal oldValue As String, ByVal newValue As String)
    ' TODO: Add your property changed side-effects. Descendants can override as well.
End Sub
Public Property DefaultFirstButtonImageUri As String
    ' IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
    Get
        Return CType(GetValue(VtlDataNavigator.DefaultFirstButtonImageUriProperty), String)
    End Get
    Set(ByVal value As String)
        SetValue(VtlDataNavigator.DefaultFirstButtonImageUriProperty, value)
    End Set
End Property

However when I try to set this in the xaml like so

<Image.Source>
                                        <Binding Path="FirstImage"
                                                 RelativeSource="{RelativeSource TemplatedParent}">
                                            <Binding.TargetNullValue>
                                                <ImageSource>{Binding Path=DefaultFirstButtomImageUri}</ImageSource>
                                            </Binding.TargetNullValue>
                                        </Binding>
                                    </Image.Source>

It throws an error pointing out that it couldn't create an imagesource from the text '{Binding Path=DefaultFirstButtonImageUri}'. Fine I understand the logic behind this, so I tried another approach;

 <Image Style="{StaticResource vtlImageStyle}"
                                       Source="{Binding Path=FirstImage}"
                                       TargetNullValue="{Binding Path=DefaultFirstButtonImageUri}">
                                    <!--<Image.Source>
                                        <Binding Path="FirstImage"
                                                 RelativeSource="{RelativeSource TemplatedParent}">
                                            <Binding.TargetNullValue>
                                                <ImageSource>{Binding Path=DefaultFirstButtomImageUri}</ImageSource>
                                            </Binding.TargetNullValue>
                                        </Binding>
                                    </Image.Source>-->
                                </Image>

Which would have been a great and easy solution if Image actually had a TargetNullValue property.

Can anybody suggest a way to do this?

Edit

In response to the suggestion of using PriorityBinding (which I'd never encountered before so thanks for the info on that) I tried the following:

<Image Style="{StaticResource vtlImageStyle}">
                                    <Image.Source>
                                        <PriorityBinding>
                                            <Binding Path="FirstImage"></Binding>
                                            <Binding Path="DefaultFirstButtonImage"></Binding>
                                        </PriorityBinding>
                                    </Image.Source>
                                    <!--<Image.Source>
                                        <Binding Path="FirstImage"
                                                 RelativeSource="{RelativeSource TemplatedParent}">
                                            <Binding.TargetNullValue>
                                                <ImageSource>{Binding Path=DefaultFirstButtomImageUri}</ImageSource>
                                            </Binding.TargetNullValue>
                                        </Binding>
                                    </Image.Source>-->
                                </Image>

and the DefaultFirstButtonImage dependecyProperty is defined like so;

    Protected Friend Shared ReadOnly DefaultFirstButtonImageProperty As DependencyProperty = DependencyProperty.Register("DefaultFirstButtonImage", GetType(ImageSource), GetType(VtlDataNavigator), New PropertyMetadata(New BitmapImage(New Uri("/ViewToLearn.VtlWpfControls;component/Images/button_rounded_blue_first24.png")), New PropertyChangedCallback(AddressOf OnDefaultFirstButtonImageChanged)))

Private Shared Sub OnDefaultFirstButtonImageChanged(ByVal o As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
    Dim vtlDataNavigator As VtlDataNavigator = CType(o, VtlDataNavigator)
    If vtlDataNavigator IsNot Nothing Then
        vtlDataNavigator.OnDefaultFirstButtonImageChanged(CType(e.OldValue, ImageSource), CType(e.NewValue, ImageSource))
    End If
End Sub

Protected Friend Overridable Sub OnDefaultFirstButtonImageChanged(ByVal oldValue As ImageSource, ByVal newValue As ImageSource)
    ' TODO: Add your property changed side-effects. Descendants can override as well.
End Sub
Public Property DefaultFirstButtonImage As ImageSource
    ' IMPORTANT: To maintain parity between setting a property in XAML and procedural code, do not touch the getter and setter inside this dependency property!
    Get
        Return CType(GetValue(VtlDataNavigator.DefaultFirstButtonImageProperty), ImageSource)
    End Get
    Set(ByVal value As ImageSource)
        SetValue(VtlDataNavigator.DefaultFirstButtonImageProperty, value)
    End Set
End Property

Unfortunately this appears not to work at all. When I place an instance of my control in which this code resides onto a window I get an error stating that the format of the Uri could not be determined. Unfortunately this is thrown from the test application I have to built to test the control, however I suspect that it must originate in the dependency property.

Dom Sinclair
  • 2,458
  • 1
  • 30
  • 47
  • You approach would require a bindable TargetNullValue, i.e. a dependency property. However, a Binding is not a DependencyObject, hence none of its properties are dependency properties. You may need to do this with a `MultiBinding` ([see here](http://stackoverflow.com/a/15309844/1136211)), or probably better, with a `PriorityBinding`. – Clemens Nov 26 '15 at 12:55
  • Hi Clemens, thanks for your suggestion. Ironically I'm actually trying to do this off the back of an answer you gave to dynamically setting image sources a while back. I essentially want to dynamically set the target nullvalue image to be drawn from a particular directory of fall back images depending on the value of another dependency property that the end consumer of this control would select. Do you think that a multi- binding approach would work in this context. Xaml binding still has the ability to completely throw me unfortunately. – Dom Sinclair Nov 26 '15 at 13:03
  • 1
    Sounds more like PriorityBinding, but MultiBinding should of course also work. – Clemens Nov 26 '15 at 13:05
  • Hi Clemens, never heard of those before so thanks for pointing them out. Tried it however with not much success. Probably doing something stupid though. Have edited question so you might be able to spot my error. – Dom Sinclair Nov 26 '15 at 13:33
  • The Uri is missing the `pack://application:,,,` prefix, which is necessary for a valid Resource File Pack URI in C# (it is automatically added only in XAML). Given that `ViewToLearn.VtlWpfControls` is really the assembly name (and not a namespace name) the full URI should look like this: `pack://application:,,,/ViewToLearn.VtlWpfControls;component/Images/button_rounded_blue_first24.png`. – Clemens Nov 26 '15 at 13:38
  • Sadly it doesn't appear to be as simple a solution as that. I can only thank you for your time in trying to assist me, Perhaps the original idea I had (ie dynamically settable images for targetNullValue wasn't as clever as I thought it was after all). – Dom Sinclair Nov 26 '15 at 13:51
  • You did not by accident just forget to set the sources of the Bindings inside the PriorityBinding (i.e. the `RelativeSource TemplatedParent`). – Clemens Nov 26 '15 at 14:21
  • Well in truth I had, but again no difference. The DefaultFirtsButtonImage property is pointing to the correct Image (whose build action is set to resource (but also tried it with None)) but the image doesn't appear either in design time or in runtime. Works perfectly when defined the way it was originally, but that of course doesn't allow for dynamic control. – Dom Sinclair Nov 26 '15 at 14:39

0 Answers0