Reputation: 561
I'm trying to create a "DropDownButton" with an icon in it, and I want to be able to set the icon source via an attached property (I found this is the (only?) way to do this). But for some reason, everything I tried fails, the best I could get was an empty Image container.
I thought this looked pretty good, but now I'm getting these errors:
The local property "Image" can only be applied to types that are derived from "IconButton".
The attachable property 'Image' was not found in type 'IconButton'.
The attached property 'IconButton.Image' is not defined on 'Button' or one of its base classes.
I'm probably doing this completely wrong (I've been trying and editing for about 2 hours now), but I just know there must be a way of doing this.
Relevant code is provided below, if anybody can even point me in the right direction that would be awesome!
EDIT: Updated code, still experiencing issue
Now I get this error in debug log:
System.Windows.Data Error: 40 : BindingExpression path error: 'Image' property not found on 'object' ''ContentPresenter' (Name='')'. BindingExpression:Path=Image; DataItem='ContentPresenter' (Name=''); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
ImageButton.cs (thank you Viv):
class ImageButton : Button
{
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(ImageSource), typeof(ImageButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender));
public ImageSource Image
{
get { return (ImageSource)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
}
ImageButton Style:
<Style TargetType="{x:Type Controls:ImageButton}" x:Key="FormIconDropDownButton">
<Setter Property="Margin" Value="5" />
<Setter Property="Padding" Value="10,5,4,5" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type Controls:ImageButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image Style="{StaticResource FormButtonIcon-Small}"
Source="{Binding Image, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Grid.Column="1"
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center"
Margin="0,0,9,0"/>
<Path Grid.Column="2"
Fill="Black"
Data="M 0 0 L 3.5 4 L 7 0 Z"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
In window xaml:
<Controls:ImageButton Content="Hello"
Style="{StaticResource FormIconDropDownButton}"
Image="{StaticResource Icon-Small-Locations}" />
Upvotes: 0
Views: 3494
Reputation: 17380
You're just using the wrong control type in your xaml. You're still using the base class instead of your derived class.
Also you're declaring a dependency property not an attached property.
Attached Properties are registered with DependencyProperty.RegisterAttached(...)
Now you'll need to add a namespace to where your IconButton
class is defined in your xaml such as
xmlns:local="clr-namespace:Mynamespace"
and then switch occurrences of
{x:Type Button}
to {x:Type local:IconButton}
and
<Button ...> to <local:IconButton ...>
I wouldn't recommend an attached property for this tbh. Attached properties get way over-used when they shouldn't be probably just my opinion.
Check This thread for some differences between DP and AP usage. In this case it's a custom Button
that shows an Image
. Make it unique than homogenize the lot.
Update:
Download link using derived class(ImageButton
) of Button
with normal DP.
Upvotes: 3
Reputation: 2804
This is my example of Extending Base class, use Dependency Properties in Style and in View. For more details write in this post.
public class ItemsList : ListView {
public static readonly DependencyProperty ItemIconProperty = DependencyProperty.Register("ItemIcon", typeof(ImageSource), typeof(ItemsList));
public ImageSource ItemIcon {
get { return (ImageSource)GetValue(ItemIconProperty); }
set { SetValue(ItemIconProperty, value); }
}
public static readonly DependencyProperty DoubleClickCommandProperty = DependencyProperty.Register("DoubleClickCommand", typeof(ICommand), typeof(ItemsList));
public ControlTemplate DoubleClickCommand {
get { return (ControlTemplate)GetValue(DoubleClickCommandProperty); }
set { SetValue(DoubleClickCommandProperty, value); }
}
}
/Style for Extended ItemList where is 'ItemIcon' DependencyProperty Declared/
<Style x:Key="BaseDataSourcesWindowListMenuStyle" TargetType="Controls:ItemsList">
<Setter Property="ItemIcon" Value="/Presentation.Shared;component/Resources/Images/data_yellow.png" />
</Style>
<Style x:Key="DataSourcesListMenuStyle" TargetType="Controls:ItemsList"
BasedOn="{StaticResource BaseDataSourcesWindowListMenuStyle}">
<Setter Property="DoubleClickCommand" Value="{Binding Path=VmCommands.EditDataSourceDBCommand}" />
</Style>
/HOW I'M USING 'ItemIcon' DependencyProperty ON VIEW/
<Controls:ItemsList Grid.Column="0" Grid.Row="1" Margin="8" ItemsSource="{Binding DataSourceDbs}"
Style="{DynamicResource DataSourcesListMenuStyle}"
SelectedItem="{Binding SelectedDataSourceDB, Mode=TwoWay}" />
Upvotes: 1
Reputation: 24453
Everything looks correct except that you haven't declared an attached property. Instead you've just declared a normal DependencyProperty
on your IconButton
class which is then only valid to set on IconButton
or classes derived from it. The declaration of an attached property (which can be set on any type) uses a different call to register and also uses get/set methods instead of a wrapper property:
public static readonly DependencyProperty ImageProperty =
DependencyProperty.RegisterAttached(
"Image",
typeof(ImageSource),
typeof(IconButton),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender));
public static ImageSource GetImage(DependencyObject target)
{
return (ImageSource)target.GetValue(ImageProperty);
}
public static void SetImage(DependencyObject target, ImageSource value)
{
target.SetValue(ImageProperty, value);
}
Upvotes: 1
Reputation: 1526
First of all, I think that's a dependency property you declared there, and it belongs to the IconButton custom control, and you're trying to use it on a Button control.
See http://msdn.microsoft.com/en-us/library/ms749011.aspx for reference on how to declare attached properties, and assign that property to the Button class instead of the IconButton since you're not using that custom control in the code above.
Upvotes: 0