我有一个简单的UIElement,我想将其转换为MarkupExtension:
[MarkupExtensionReturnType(typeof(FrameworkElement))]
public class PinkRectangle : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new Rectangle {Height = 100, Width = 300, Fill = Brushes.HotPink };
}
}
在大多数情况下,它的效果非常好。唯一的例外是在列表中:
<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>
在Collection Syntax中,它表示:
如果属性的类型是集合,则不需要在标记中将推断的集合类型指定为对象元素。而是将要成为集合中项目的元素指定为property元素的一个或多个子元素。每个此类项目在加载期间都会被评估为一个对象,然后通过调用隐式集合的Add方法将其添加到集合中。
但是,xaml将上面的语法解释为MyList = PinkRectangle
而不是MyList.Add(PinkRectangle)
,但是如果我先放入Grid,它会正确地调用MyList.Add()。 两种情况下告诉xaml调用MyList.Add()的正确语法是什么?
下面是创建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();
}
}
}
-编辑-
我发现,如果我从MyList中删除了set{ }
,问题就消失了,因为xaml不再认为有一个setter,但最终我需要能够设置MyList。...
答案 0 :(得分:2)
可怜的XAML解析器真的对这一切感到困惑...:O)通过消除歧义来帮助它:在XAML中显式实例化MyList
。
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>
在哪里
public class ObjectCollection : ObservableCollection<object>
{
}
顺便说一句,命名约定是您的标记类定义应使用扩展后缀。
public class CoolBlueRectangleExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
}
}
答案 1 :(得分:0)
如果MyProperty仅在XAML中初始化,并且您永远不需要或不想绑定它,则可以更简单地执行此操作,而不必使XAML的集合类型混乱。为此,需要使用附加属性,将实际集合引用存储在静态扩展类私有的依赖项属性中,并用前导下划线或其他内容修饰依赖项名称。在这种情况下,您自然必须在GetMyProperty()中初始化集合:只需检查目标对象的private依赖项属性是否为null,然后根据需要进行初始化。
请注意,GetMyProperty
必须是静态的。命名约定是必须存在'Get'前缀,其余的方法名称是“ property”名称。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public static StringCollection GetMyProperty(MainWindow wnd)
{
return wnd._myProperty;
}
private StringCollection _myProperty = new StringCollection();
}
public class StringCollection : ObservableCollection<String>
{
}
<local:MainWindow.MyProperty>
<sys:String>Foo</sys:String>
</local:MainWindow.MyProperty>