I have a simple UIElement that I would like to turn into a MarkupExtension:
[MarkupExtensionReturnType(typeof(FrameworkElement))]
public class PinkRectangle : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new Rectangle {Height = 100, Width = 300, Fill = Brushes.HotPink };
}
}
It works really well in most cases. The only exception is in lists:
<local:WindowEx x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.winfx/200x/xaml"
xmlns:local="clr-namespace:WpfApp1"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
MyProperty="{Binding local:PinkRectangle}"> <!--this one works.-->
<local:WindowsEx.MyList>
<!--<Grid/> If I comment this line in, it works-->
<local:PinkRectangle/>
</local:WindowsEx.MyList>
<ContentPresenter Content="{Binding MyProperty}"/>
</local:WindowEx>
In Collection Syntax, it says:
If the type of a property is a collection, then the inferred collection type does not need to be specified in the markup as an object element. Instead, the elements that are intended to become the items in the collection are specified as one or more child elements of the property element. Each such item is evaluated to an object during loading and added to the collection by calling the Add method of the implied collection.
However, xaml interprets the syntax above as MyList = PinkRectangle
rather than MyList.Add(PinkRectangle)
But if I put in a Grid first, it calls MyList.Add() for both correctly. What is the correct syntax for telling xaml to call MyList.Add() for both situations?
Here's the rest of the code to create a Minimal, Reproducable Example:
namespace WpfApp1
{
// I use this class to directly set a few unusual properties directly in xaml.
public class WindowEx : Window
{
//If I remove the set property, the error goes away, but I need the setter.
public ObservableCollection<object> MyList {get; set; } = new ObservableCollection();
public object MyProperty
{
get { return GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(nameof(MyProperty), typeof(object), typeof(MainWindow), new PropertyMetaData(0));
}
public partial class MainWindow : WindowEx
{
public MainWindow()
{
InitializeComponent();
}
}
}
- Edit -
I found that if I removed the set{ }
from MyList, the problem went away because xaml no longer thought there was a setter, but ultimately I need to be able to set MyList....
Poor XAML parser is just really confused about all this...:O) Help it by eliminating ambiguity : instantiate MyList
explicitly in your XAML.
XAML:
<local:UserControlEx x:Class="WpfApp14.UserControl1"
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:local="clr-namespace:WpfApp14"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="450">
<local:UserControlEx.MyList>
<local:ObjectCollection>
<local:CoolBlueRectangle/>
<local:CoolBlueRectangle/>
<local:CoolBlueRectangle/>
<local:CoolBlueRectangle/>
<local:CoolBlueRectangle/>
</local:ObjectCollection>
</local:UserControlEx.MyList>
<Grid>
<ItemsControl HorizontalAlignment="Left"
ItemsSource="{Binding MyList}"/>
</Grid>
</local:UserControlEx>
Where,
public class ObjectCollection : ObservableCollection<object>
{
}
BTW, the naming convention is that your markup class definition should use the Extension suffix.
public class CoolBlueRectangleExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
}
}