Reputation: 5534
I have this class
public class FeatureTabBase<T> : UserControl, IFeatureTab<T>
where T : BaseModel
{
public string TabGuid { get; set; }
public T FeaturedElement
{
get { return (T)GetValue(FeaturedElementProperty); }
set { SetValue(FeaturedElementProperty, value); }
}
// Using a DependencyProperty as the backing store for FeaturedElement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register("FeaturedElement", typeof(T), typeof(FeatureTabBase<T>), new PropertyMetadata(null));
}
That implements this interface
public interface IFeatureTab<T>
where T : class
{
T FeaturedElement { get; set; }
string TabGuid { get; set; }
}
And this instance from it
public partial class MyClass : FeatureTabBase<MyType>
{
public MyClass()
{
InitializeComponent();
}
}
But don't know how instantiate it on XAML
All I'm trying to do is a generic console that can show some pages for my different kind of items.
I was reading about x:TypeArguments at
https://learn.microsoft.com/en-us/dotnet/desktop-wpf/xaml-services/xtypearguments-directive
But nothing works.
Any Ideas?
Upvotes: 0
Views: 809
Reputation: 20471
XAML doesnt support generics, so the ctrl:FeatureTabBase
will never work. Also, you cannot inherit the XAML part of a UserControl if you derive a new class from an existing UserControl.
You can't use strongly-typed DataTemplates as they only hook up to the concrete class specified in the type. You need to take a different approach. Maybe simplify ?
public class FeatureTab : UserControl
{
public static readonly DependencyProperty FeaturedElementProperty =
DependencyProperty.Register(
"FeaturedElement",
typeof(ModelBase),
typeof(FeatureTab)
, new PropertyMetadata(null));
public string TabGuid { get; set; }
public ModelBase FeaturedElement
{
get => (ModelBase) GetValue(FeaturedElementProperty);
set => SetValue(FeaturedElementProperty, value);
}
}
NOTE This answer is valid for .Net frameworks prior to 4.7, if you point your project to .Net framework 4.7.2 Generics must work on xaml.
Upvotes: 0
Reputation: 128136
Add x:TypeArguments
to the UserControl declaration in XAML:
<ctr:FeatureTabBase
x:Class="YourNamespace.MyClass"
x:TypeArguments="local:MyType"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ctr="clr-namespace:YourNamespace"
xmlns:local="clr-namespace:YourNamespace"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
...
</ctr:FeatureTabBase>
Upvotes: 1
Reputation: 12276
Here's a control I use which will at least illustrate the concept.
Obviously, this is not going to be cut and paste for whatever it is you have in mind.
An editrow allows me to easily line up a series of labelled controls inside a stackpanel, and add various standardised functionality to the controls I make content.
public class EditRow : ContentControl
{
public string LabelFor
{
get { return (string)GetValue(LabelForProperty); }
set { SetValue(LabelForProperty, value); }
}
public static readonly DependencyProperty LabelForProperty = DependencyProperty.RegisterAttached(
"LabelFor",
typeof(string),
typeof(EditRow));
public string LabelWidth
{
get { return (string)GetValue(LabelWidthProperty); }
set { SetValue(LabelWidthProperty, value); }
}
public static readonly DependencyProperty LabelWidthProperty = DependencyProperty.RegisterAttached(
"LabelWidth",
typeof(string),
typeof(EditRow)
);
public string PropertyWidth
{
get { return (string)GetValue(PropertyWidthProperty); }
set { SetValue(PropertyWidthProperty, value); }
}
public static readonly DependencyProperty PropertyWidthProperty = DependencyProperty.RegisterAttached(
"PropertyWidth",
typeof(string),
typeof(EditRow)
);
public EditRow()
{
this.IsTabStop = false;
}
}
I template this in a resource dictionary. ( There are other options including custom control generic xaml)
<Style TargetType="{x:Type spt:EditRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type spt:EditRow}">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelWidth, TargetNullValue=2*}"/>
<ColumnDefinition Width="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=PropertyWidth, TargetNullValue=3*}"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding RelativeSource={
RelativeSource FindAncestor,
AncestorType=spt:EditRow},
Path=LabelFor}"
HorizontalAlignment="Right"
TextAlignment="Right"
Margin="2,4,0,2"/>
<Border Padding="8,2,8,2" Grid.Column="1" BorderThickness="0">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ErrorToolTip}"/>
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource ErrorToolTip}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<ItemsControl>
<spt:EditRow LabelFor="Name:" >
<TextBox Text="{Binding EditVM.TheEntity.CustomerName,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
<spt:EditRow LabelFor="Address:" >
<TextBox Text="{Binding EditVM.TheEntity.Address1,
UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" />
</spt:EditRow>
Notice that I have a TextBox as content of each of those editrows, but it could be a datepicker or whatever.
You could bind that content. Then use datatype on viewmodel type for variable datatemplates.
Upvotes: 1