views:

823

answers:

1

I've been trying to make a simple usercontrol to display an image, and eventually make it act as a button in my WPF application.

I had one of these usercontrols written into the form's XAML, and the image always displayed. However, when adding them programmatically to a stackpanel, the control was there but the image never displayed.

Here's the UserControl: (simple, but works for this example)

<UserControl x:Class="ImgButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    MinHeight="32" MinWidth="32"
    x:Name="uc_ImgButton">
    <Border BorderBrush="Gray" BorderThickness="2">
        <Image Source="{Binding ElementName=uc_ImgButton, Path=Source}" x:Name="img"/>
    </Border>
</UserControl>

I set up the Source property as a dependency property of the ImageSource type:

Partial Public Class ImgButton

    Public Property Source() As ImageSource
        Get
            Return GetValue(SourceProperty)
        End Get
        Set(ByVal value As ImageSource)
            SetValue(SourceProperty, value)
        End Set
    End Property

    Public Shared ReadOnly SourceProperty As DependencyProperty = _
                           DependencyProperty.Register("Source", _
                           GetType(ImageSource), GetType(ImgButton))

End Class

Here's an example of how I add them programmatically in VB:

Dim newBtn As New myApp.ImgButton
newBtn.Width = 100
newBtn.Height = 100
Dim bi As New BitmapImage
bi.BeginInit()
bi.UriSource = New Uri("C:\test.png", UriKind.RelativeOrAbsolute)
bi.EndInit()
'MsgBox(bi.Width)  '(a simple debug test I added)
newBtn.Source = bi
Me.StackPanelMain.Children.Add(newBtn)

Here's the strange part... the code as shown above runs without error, but the result on my form is an empty border with no image displayed inside.

However, if you un-comment the MsgBox line, now the image displays. It's as if forcing it to get some value from the BitmapImage makes it work. I also tried replacing the MsgBox line with something innoculous like "dim x as integer = bi.PixelWidth" and that ALSO made the image display. If I take it away, no image on my form.

I suspect I'm missing something or just don't understand something. I'd like to learn what's going on, rather than leave a seemingly pointless line of code in my app.

A: 

I'm not sure if I understand the binding that you're trying to setup. Typically when you use ElementName that means that you are binding to another control in the same visual tree. However, based on the code sample for your ImgButton class it looks like that is just a normal class. Are you sure that you want to use ElementName?

Also, I believe that in order for a class to have a DependencyProperty it must derive from DependencyObject. If you don't want to make that change, you can always implement INotifyPropertyChanged and fire the PropertyChanged event in the setter of the Source property.

Update: I think part of the problem was that I misread your question (I didn't realize that ImgButton was your UserControl class).

Anyway, I'm not really sure what's going on here. However, what if your property was for the path to the image file, rather than to the image itself? Maybe something like this:

XAML:

<UserControl x:Class="ImgButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    MinHeight="32" MinWidth="32"
    x:Name="uc_ImgButton">
    <Border BorderBrush="Gray" BorderThickness="2">
        <Image>
            <Image.Source>
                <BitmapImage UriSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=Source}" />
            </Image.Source>
        </Image>
    </Border>
</UserControl>

C#:

Partial Public Class ImgButton

    Public Property Source() As String
        Get
            Return GetValue(SourceProperty)
        End Get
        Set(ByVal value As ImageSource)
            SetValue(SourceProperty, value)
        End Set
    End Property

    Public Shared ReadOnly SourceProperty As DependencyProperty = _
                           DependencyProperty.Register("Source", _
                           GetType(String), GetType(ImgButton))

End Class

Then just set the Source property to the path to your image:

Dim newBtn As New myApp.ImgButton
newBtn.Width = 100
newBtn.Height = 100
newBtn.Source = "C:\test.png"
Me.StackPanelMain.Children.Add(newBtn)

I haven't used the BitmapImage class before, but it's possible that it takes something in order to have it render the image. I'm not really why your code doesn't work, though, but maybe if you set the source via bindings in XAML it will be more like your original case, and it will work.

Andy
At this point in my WPF experience, much of my work is based on code snippets I find online to get me started. I just want a usercontrol that has a property I can set to change the image displayed within it. If there's a better way to accomplish this, please fee free to share, I want to learn :-) I don't quite understand your suggestion... sample code would help.
B2Ben
Thanks for the extra code, I'll play around with it. I've always had trouble understanding how databinding statements should be written in XAML, and when it's appropriate to use certain methods of binding.
B2Ben
It takes a while to get the hang of it. In VS2010, there is intellisense for markup extensions (including bindings), so hopefully that will help with the learning curve.
Andy