Live Example:
http://www.adefwebserver.com/Richard/ooNacontrols/ooNaControls.TestTestPage.html
ooNaverse sidebars are separate projects that focus on narrow issues within the ooNaverse. In this case, the demo just shows that a Position property can be implemented in an abstract base class (ooNaThing) with no UI that inherits from UserControl and in turn be inherited by a subclass (Seeker) that does define a UI (xaml file). Clicking on the page will cause the ooNaSeeker object to move to the click point by updating the Position property, which incidentally is a Dependency property and so will support animation of the ‘higher’ order class that inherits from it (Seeker). I provide the source mainly so you can have a working example where you can just lift the code if you want.
My goal right now is to allow designer access to the Seeker’s visual implementation, so I’m trying to convert the Seeker class to a templated control. I want to focus on just that aspect, so I’m doing it in a separate project from the Seeker. I’m hoping that blogging about it will help clarify the process, because I’ve been thrashing. I can make it happen, but I really want to understand, so I hope blogging about it will help. As you’ll see, I decided to break it into two parts by just describing how to inherit from UserControl and supply base class functionality at the same time.
For those who don’t know, the ooNaverse, ruled over by ooBer, is the Seeker’s plane of existence, possibly in a land called ooNa. The Seeker like all else in the ooNaverse, is an ooNaThing. I think, and I’m sure you’re all hoping, that’s enough ooNaThology to set the foundation for what I want to discuss.
If you’ve been following along you may know that I’ve been using interfaces to support the ooNaThing taxonomy. iSeeker inherits from IooNaBeing which inherits from IooNaThing. And of course there’s IooBer. MainPage implements IooBer, giving ooBer access to the main UI, and allowing ‘this’ to be passed to every Seeker as a reference to ooBer / IooBer.
For the Seeker, the problem with this approach is that the Seeker class has to implement every interface from IooNaThing through iSeeker. That would be ok, but the intent is that lots of different inhabitants of the ooNaverse inherit from IooNaThing and so forth, so every single one of them would have to implement all those interfaces. In this case, far better to inherit from classes that provide the functionality, instead of interfaces, which just define it.
I got into this issue because I want to make a point about when you don’t need a templated control but you still want to inherit from classes that provide functionality instead of implementing interfaces, which I’d say is any time you don’t need to let anyone monkey around with the visual representation – if you don’t need it, not using a templated control is a lot less work and easier to work with in Blend, so I think it’s useful approach to know. It’s how I would have implemented the Seeker originally if I’d known how.
The answer is to create a base class that inherits from UserControl. For instance, I want everything that inherits from ooNaThing at any level to support a Position property. In this test project, I call it unaThingLocal, and here’s the code.
namespace ooNaControls.Test
{
public abstract class uNaThingLocal : UserControl
{
// Dependency properties declaration
public static readonly DependencyProperty PositionProperty =
DependencyProperty.Register("Position",
typeof(Point),
typeof(uNaThingLocal),
new PropertyMetadata(OnPositionChanged));
public uNaThingLocal()
{
}
public Point Position {
get { return (Point)this.GetValue(PositionProperty); }
set { this.SetValue(PositionProperty, value); }
}
private static void OnPositionChanged(object sender, DependencyPropertyChangedEventArgs e)
{
uNaThingLocal thing = sender as uNaThingLocal;
if (thing == null)
return;
thing.SetValue(Canvas.LeftProperty, ((Point)e.NewValue).X);
thing.SetValue(Canvas.TopProperty, ((Point)e.NewValue).Y);
}
}
}
uNaThingLocal is all implementation with no UI. It lives in uNaThingLocal.cs and there is no XAML file. Notice that it is abstract, because without a UI, there is no point in creating a strictly uNaThingLocal object. I could have an unaBeingLocal class, also abstract, that would support the rudimentary methods I have defined for uNaThings that are alive, such as die(this), but in this case I just make uNaSeekerLocal inherit from uNaThingLocal. And, for this test case, uNaSeekerLocal is all UI except for the unaThingLocal.Position property it inherits, which is the whole point of this demonstration. Here’s the XAML in uNaSeekerLocal.xaml. Notice that the opening tag starts with <local:uNaThingLocal instead of <UserControl. Instead of relying on UserControl directly, the implementation of UserControl behind the xaml in uNaSeekerLocal.xaml goes through the uNaThingLocal inheritance of UserControl. The benefit is that we pick up the implementation of uNaThingLocal along the way.
<local:uNaThingLocal x:Class="ooNaControls.Test.uNaSeekerLocal"
xmlns:local="clr-namespace:ooNaControls.Test"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="40" Height="60">
<Grid x:Name="LayoutRoot">
<Ellipse Fill="#FF85AF7A"
x:Name="VisualMe"
Stroke="Black"
Height="60"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="40" RenderTransformOrigin="0.5,0.5">
<Ellipse.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform Angle="40"/>
<TranslateTransform/>
</TransformGroup>
</Ellipse.RenderTransform>
</Ellipse>
</Grid>
</local:uNaThingLocal>
And of course unaSeekerLocal inherits from uNaThingLocal:
namespace ooNaControls.Test
{
public partial class uNaSeekerLocal : uNaThingLocal
{
public uNaSeekerLocal()
{
InitializeComponent();
}
}
}
Here’s the instantiation of uNaSeekerLocal in MainPage.xaml
<Canvas x:Name="ooBerCanvas" Height="500" Width="800" Background="Aqua" HorizontalAlignment="Left" VerticalAlignment="Top">
<local:uNaSeekerLocal x:Name="seeker" Canvas.Left="100" Canvas.Top="200" />
</Canvas>
And here’s the proof in MainPage.xaml.cs that the Position property on uNaThingLocal is accessible through uNaSeekerLocal.
void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
seeker.Position = e.GetPosition(null);
}
So there you have it. Just inherit from UserControl in an abstract base class with no XAML. Provide whatever functionality makes sense in your base classes. When you’re at the level where you want to provide a UI, make the XAML opening tag refer to your base class instead of UserControl.
Download code:
http://www.adefwebserver.com/Richard/ooNacontrols/ooNaControls.Test.zip