Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a UI-MarkupExtension

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....

like image 713
bwall Avatar asked Jul 19 '19 20:07

bwall


People also ask

What is a markup extension?

Markup extensions are a XAML technique for getting a value that is neither a primitive nor a specific XAML type. Generally, for attribute usage, markup extensions are identified by being covered by a start and end brace. For example: {x:Static local:AppConstants. DefaultName} .

What is XAML extension?

XAML, Extensible Application Markup Language, extension files describe the user interface elements for software applications based on Windows Presentation Foundation (WPF). Though a language, it doesn't require to be programmed as it is based on standard format of XML which is easy to use and understand.

What is markup extension in WPF?

The most common markup extensions used in WPF programming are those that support resource references ( StaticResource and DynamicResource ), and those that support data binding ( Binding ). StaticResource provides a value for a property by substituting the value of an already defined resource.

What is the advantage of using XAML markup extensions?

XAML markup extensions help extend the power and flexibility of XAML by allowing element attributes to be set from sources other than literal text strings. In either case, the text string set to the Color attribute is converted to a Color value by the ColorTypeConverter class.


1 Answers

Poor XAML parser is just really confused about all this...:O) Help it by eliminating ambiguity : instantiate MyList explicitly in your XAML.

enter image description here

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)
    {
    }
}
like image 103
jsanalytics Avatar answered Oct 05 '22 23:10

jsanalytics