【问题标题】:How to make a UI-MarkupExtension如何制作 UI-MarkupExtension
【发布时间】:2019-07-19 20:51:53
【问题描述】:

我有一个简单的 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 中,它说:

如果属性的类型是集合,则推断的集合类型不需要在标记中指定为对象元素。相反,旨在成为集合中项目的元素被指定为属性元素的一个或多个子元素。每个这样的项目在加载期间被评估为一个对象,并通过调用隐含集合的 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....

【问题讨论】:

  • 这有点奇怪。你能详细说明为什么你需要Window 内部的List&lt;object&gt;(特别是有一个公共设置器)以及你为什么要用 UI 元素填充它?从我的角度来看,所有这些似乎都是糟糕的设计。
  • 你应该看看 ILSpy 是如何在网格中实现这种类似行为的吗?似乎有一些 IAddChild 接口可以处理这个,可能。
  • @dymanoid - 这只是一个简化的例子。在我的项目中,我有一个基本上由列表框组成的自定义用户控件——用户控件定义了它们的外观和协同工作的方式。我希望能够从实际控制之外设置这些列表中的内容。

标签: c# .net wpf xaml markup-extensions


【解决方案1】:

糟糕的 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>
{
}

顺便说一句,命名约定是您的标记类定义应该使用 Extension 后缀。

public class CoolBlueRectangleExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
    }
}

【讨论】:

    【解决方案2】:

    如果 MyProperty 仅在 XAML 中初始化,并且您永远不需要或不想绑定它,您可以更简单地执行此操作,而不必将 XAML 与集合类型混淆。要使用附加属性执行此操作,您可以将实际的集合引用存储在静态扩展类私有的依赖属性中,并使用前导下划线或其他内容装饰依赖属性名称。在这种情况下,您自然必须在 GetMyProperty() 中初始化集合:只需检查目标对象的私有依赖属性是否为空,并根据需要进行初始化。

    注意GetMyProperty 必须是静态的。命名约定是必须存在“Get”前缀,方法名称的其余部分是“属性”名称。

    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>
    
    

    【讨论】:

      猜你喜欢
      • 2011-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-07
      • 2021-10-02
      • 1970-01-01
      • 2016-02-16
      • 2011-07-13
      相关资源
      最近更新 更多