C# – Custom WPF/XAML Canvas

c++controlswpfxaml

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>

Best Solution

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.

  1. In MyCanvas.xaml you set it to an Image.
  2. When using it, you set it to your user controls.

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)));
}
Related Question