DaveDev
DaveDev

Reputation: 42185

How to make this nested user control display an image?

I've created an ImageButton user control. I want another user control - TabItem - to use that ImageButton but the TabItem won't display the image.

What do I need to do to get TabItem to display the ImageButton's image?

This is the code I have:

<!--MainPage.xaml-->
<Page
    x:Class="ButtonTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ButtonTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:buttons="using:ButtonTest" Background="Transparent">

    <StackPanel>

        <!--there is an image displayed for this-->
        <buttons:ImageButton ImageSource="overview_64.png" />

        <!--but not for this-->
        <buttons:TabItem ImageSource="overview_64.png" TabItemText="button text" />        
    </StackPanel>
</Page>

<!--ImageButton.xaml-->
<UserControl
    x:Class="ButtonTest.ImageButton"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ButtonTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="100"
    d:DesignWidth="100">

    <Button Name="imgButton" Style="{StaticResource imageButton}" />

</UserControl>

public sealed partial class ImageButton : UserControl
{
    public static readonly DependencyProperty ImageProperty = 
        DependencyProperty.Register("ImageSource", typeof(string), typeof(ImageButton), null);

    public ImageButton()
    {
        this.InitializeComponent();
        this.DataContext = this;
    }

    public string ImageSource
    {
        get { return (string)GetValue(ImageProperty); }
        //set { SetValue(ImageProperty, value); }
        set { SetValue(ImageProperty, "/Resources/Images/" + value); }
    }
}

This is TabItem.xaml, which uses ImageButton. There is no image displayed for the TabItem. Am I binding the ImageSource correctly?

<!--TabItem.xaml-->
<UserControl x:Class="ButtonTest.TabItem"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        xmlns:btn="using:ButtonTest"
        d:DesignHeight="85" d:DesignWidth="80">

    <StackPanel Orientation="{Binding TabOrientation}">
        <!-- TabItem uses ImageButton -->
        <btn:ImageButton ImageSource="{Binding ImageSource}"  />

        <TextBlock Name="txtText" Text="{Binding TabItemText}" />
    </StackPanel>
</UserControl>

public sealed partial class TabItem : UserControl
{
    public static readonly DependencyProperty TextProperty = 
        DependencyProperty.Register("TabItemText", typeof(string), typeof(TabItem), null);
    public static readonly DependencyProperty ImageProperty = 
        DependencyProperty.Register("ImageSource", typeof(string), typeof(TabItem), null);

    public string TabItemText
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    public string ImageSource
    {
        get { return (string)GetValue(ImageProperty); }
        set { SetValue(ImageProperty, "/Resources/Images/" + value); }
    }

    public TabItem()
    {
        this.InitializeComponent();

        this.DataContext = this;
    }
}

The style that ImageButton uses is defined here:

<!--Image Button Style is defined here-->
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:HKC.Winterfell.WP81.Resources.Styles.Buttons">

    <Style x:Key="imageButton" TargetType="Button">
        <Setter Property="MinWidth" Value="50"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button">
                    <Grid>
                        <Border x:Name="border" Background="Transparent" BorderBrush="Transparent" BorderThickness="2" CornerRadius="20">
                            <Grid>
                                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Width="Auto" Grid.RowSpan="2"/>
                            </Grid>
                        </Border>

                        <Image x:Name="ImgShadow" Source="/Resources/Images/shadow_64.png" Visibility="Visible" Height="67" Width="67" Margin="10,8,0,0" />
                        <Image x:Name="Img" Source="{Binding ImageSource}" Height="64" Width="64" Margin="0,0,0,0"  />
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="MouseOver"/>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ImgShadow">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="Img">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="10,8,0,0" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

Upvotes: 0

Views: 301

Answers (1)

yasen
yasen

Reputation: 3580

I don't think it's a good idea to change the DataContext of a control from within the control. I've seen it a few times and it always caused some problems.

Potential problem

I think here's the problem in your case. Look at this line from the TabItem's xaml:

<btn:ImageButton ImageSource="{Binding ImageSource}"  />

This binding binds the ImageSource property of the ImageButton to the DataContext of the ImageButton (as all bindings do - they bind to the DataContext of the control on which they are set). But in the constructor you change that context to be the ImageButton itself, so this property is basically bound to itself.

Easy fix

The easiest fix is, instead of changing the DataContext of the whole control, to change the DataContext of its contents. In your case, that'll be the DataContext of the ImageButton's Button element.

In other words - change this line in the ImageButton's constructor:

this.DataContext = this;

to

this.imgButton.DataContext = this;

Of course, do the same for the TabItem control.

Conclusion or something...

Basically, it's a bad idea to change DataContext (even if you change it as I suggested), so try to avoid it as much as possible.

P.S. I may give you a solution without DC changes, but am too busy now, sorry. If someone else does it, I'll upvote them! :)

Edit - additional observation (unrelated to the problem)

I also don't think that setter is a good idea:

set { SetValue(ImageProperty, "/Resources/Images/" + value); }

If you ever need to use an image that's not in Resources/Images, you would be very sad. Also, it might be a good idea to change the type of the property to be ImageSource (as that's normally what's used for image sources :)) or object. In WinRT a lot of things can be converted to ImageSource, so it would be more flexible if it's of type object.

Upvotes: 2

Related Questions