Reputation: 23685
I'm trying to create and use a custom Canvas. Here is the XAML (MyCanvas.xaml):
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamepace" xmlns:Properties="clr-namespace:MyNamepace.Properties" Core:Class="MyNamepace.MyCanvas">
<Canvas.Resources>
<Namespace:ImagesConverter Core:Key="ImagesConverter"/>
</Canvas.Resources>
<Image Source="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
</Canvas>
Here is the code declaration (MyCanvas.xaml.cs):
public partial class MyCanvas : Canvas
When I try to use it like so:
<Namespace:MyCanvas Core:Name="Layout" Loaded="OnLoaded">
<Namespace:MyUserControl Core:Name="Control1" Namespace:MyCanvas.Left="50" MyProperty="50">
<Namespace:MyCanvas.Top>
<MultiBinding Converter="{StaticResource MathConverter}" ConverterParameter="(x - y) / 2">
<Binding ElementName="Layout" Path="ActualHeight"/>
<Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Namespace:MyCanvas.Top>
</Namespace:MyUserControl>
<Namespace:MyUserControl Core:Name="Control2" Namespace:MyCanvas.Left="744" Namespace:MyCanvas.Top="42" MyProperty="150"/>
</Namespace:MyCanvas>
I get two different errors:
The property "Content" can only be set once. ==> Isn't it inheriting Canvas?!?!?!
The member "Top" is not recognized or is not accessible. ==> Isn't it inheriting Canvas again?!?!?! The member "Left" is not recognized or is not accessible. ==> Isn't it inheriting Canvas again?!?!?!
EDIT: this is what I have so far... still getting the "Content" already set error!
MyCanvas.xaml
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamespace" xmlns:Properties="clr-namespace:MyNamespace.Properties" Core:Class="MyNamespace.MyCanvas">
<Canvas.Background>
<ImageBrush ImageSource="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
</Canvas.Background>
<Canvas.Resources>
<Namespace:ImagesConverter Core:Key="ImagesConverter"/>
</Canvas.Resources>
</Canvas>
MyCanvas.xaml.cs
public class MyCanvas : Canvas
{
// ...
}
MainWindow.xaml
<Namespace:MyCanvas Core:Name="MyCanvas" Loaded="OnLoaded">
<Namespace:MyUserControl ...
<Namespace:MyUserControl ...
<Namespace:MyUserControl ...
</Namespace:MyCanvas>
Upvotes: 3
Views: 9698
Reputation: 11439
Adding to Daniel's answer, you have to understand that WPF introduces a concept called 'Dependency Properties' and 'Dependency Objects'. In short, just as Objects in C# can have properties, Dependency Objects in WPF can have dependency properties. The way this works is a bit different though that the c# method.
For dependency properties (such as attached properties), WPF manages a seperate table with records indicating which objects have which properties. Which in other words, means it's possible for any object to have any property, WPF just adds a record to the table giving said object said property. So for example, Canvas.Left, while being defined by Canvas, can be implemented by any control. WPF simply inserts a record into the dependency table, and voila now your image has Canvas.Left / Canvas.Top properties. This means a much smaller memory footprint, because object opt into having a property (when the record is added), rather than having a property just because it derives from a certain class.
The way this works programatically, is you use DependencyObject.SetValue / GetValue (all WPF objects derive from DependencyObject see: http://miteshsureja.blogspot.com/2011/06/wpf-class-hierarchy.html - in fact, memorize this graph because it will help you better to understand how WPF works under the hood), and this adds / reads from a record to the aforementioned table. When you define a dependency property, you your c# accessors should really redirect to these methods, because WPF (and not the object itself) should be managing these values (this is how WPF is able to do data-binding and what not, because it manages the values and just shifts them to other objects as neccesasry). For an example of creating a dependency property, see: http://msdn.microsoft.com/en-us/library/ms752914.aspx . The concept behind dependency properties is very simple, but you have to at least be aware of it in order to understand a lot of WPFs functionality.
Upvotes: 4
Reputation: 174457
Left
and Top
are attached properties. That means that they are not inherited by your class.
You need to change the user control declaration to use Canvas.Left
and Canvas.Top
instead:
<Namespace:MyUserControl Core:Name="Control2" Canvas.Left="744" Canvas.Top="42"
MyProperty="150"/>
The problem with the content is that you set it twice, just like the error message says.
MyCanvas.xaml
you set it to an Image
. To fix it, you need to add an ItemsControl
to MyCanvas
and declare it as the control that represents the content:
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamepace" xmlns:Properties="clr-namespace:MyNamepace.Properties" Core:Class="MyNamepace.MyCanvas">
<Canvas.Resources>
<Namespace:ImagesConverter Core:Key="ImagesConverter"/>
</Canvas.Resources>
<Image Source="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
<ItemsControl Content="{Binding Path=LocalContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Namespace:MyCanvas}}}" />
</Canvas>
In your class file:
[ContentProperty("LocalContent")]
public partial class MyCanvas : Canvas
{
public static readonly DependencyProperty LocalContentProperty =
DependencyProperty.Register(
"LocalContent",
typeof(UIElementCollection),
typeof(MyCanvas ),
new PropertyMetadata(default(UIElementCollection)));
}
Upvotes: 4