kekekeks / XamlX

General purpose pluggable XAML compiler with no runtime dependencies.
MIT License
317 stars 55 forks source link

Add support for x:Reference markup extension #6

Open wieslawsoltes opened 5 years ago

wieslawsoltes commented 5 years ago

I have use case for x:Reference markup extension when used in Avalonia for defining docking layouts using xaml.

Example:

<Dock xmlns="https://github.com/avaloniaui" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:vm="clr-namespace:Demo.ViewModels;assembly=Demo"
      ActiveLayout="{x:Reference layout1}">
  <Dock.Items>
      <vm:Item1ViewModel x:Name="item1"/>
      <vm:Item2ViewModel x:Name="item2"/>
      <vm:Item3ViewModel x:Name="item3"/>
  <Dock.Items>
  <Dock.Layouts>
    <Layout x:Name="layout1">
      <x:Reference Name="item1"/>
      <x:Reference Name="item2"/>
    </Layout>
    <Layout x:Name="layout2">
      <x:Reference Name="item1"/>
      <x:Reference Name="item2"/>
      <x:Reference Name="item3"/>
    </Layout>
  </Dock.Layouts>
</Dock>

In my example I have two separate instances of Layout object (layout1 and layout2) but both reference same child items. This allows switching layouts during runtime (by setting ActiveLayout property) while items retain their state.

Dock:

https://github.com/wieslawsoltes/Dock/blob/94c8a59596c0e01139285b071b37d0679066f88f/samples/AvaloniaDemo.Perspectives/Layouts/Default.xaml#L23-L54

https://github.com/wieslawsoltes/Dock/blob/94c8a59596c0e01139285b071b37d0679066f88f/src/Dock.Model.Avalonia/Core/DockableBase.cs#L12

Docs:

https://docs.microsoft.com/en-us/dotnet/framework/xaml-services/x-reference-markup-extension

kekekeks commented 5 years ago

Do you need forward-reference support? That what makes it hard to support x:Rereference properly

wieslawsoltes commented 5 years ago

Do you need forward-reference support? That what makes it hard to support x:Rereference properly

I think so. So for example ActiveLayout="{x:Reference layout1}" is forward reference, because it's referencing object not yet defined?

How is that different from using Binding: https://github.com/wieslawsoltes/Dock/blob/94c8a59596c0e01139285b071b37d0679066f88f/samples/AvaloniaDemo.Perspectives/Layouts/Default.xaml#L23-L24

kekekeks commented 5 years ago

That won't play well with our current way of handling ISupportsInitialize and the general object graph traversal that doesn't support forward references at all.

kekekeks commented 5 years ago

How exactly it's supposed to work with Lists? Should the compiler insert a null value and replace it with an actual value later?

kekekeks commented 5 years ago

We could potentially create an out-of-tree object on the first usage, but that would break any markup extensions that expect the parent list to be present. So we have to create the object while initializing its parent, not on the first usage. I think this prevents us from using forward x:Reference in lists.

wieslawsoltes commented 5 years ago

I think this prevents us from using forward x:Reference in lists.

I think it will be enough for my use case to have lists without support for forward x:Reference. This will limit some xaml scenations, but still very useful. The forward x:Reference would only be necessary for properties.

kekekeks commented 5 years ago

The original XAML engine seems to only support x:Reference for properties as well.

wieslawsoltes commented 5 years ago

The original XAML engine seems to only support x:Reference for properties as well.

Support for properties itself would be good, as I am currently using Binding markup extension instead.

wieslawsoltes commented 5 years ago

@kekekeks This works for lists:

    public class ReferenceExtension
    {
        public ReferenceExtension()
        {
        }

        public ReferenceExtension(string name)
        {
            Name = name;
        }

        [ConstructorArgument("name")]
        public string Name { get; set; }

        public object ProvideValue(IServiceProvider serviceProvider)
        {
            var nameScope = serviceProvider.GetService(typeof(INameScope)) as INameScope;
            var element = nameScope.Find(Name);
            return element;
        }
    }
            <dmc:ProportionalDock.HiddenDockables>
                <vmt:LeftTopTool1ViewModel x:Name="LeftTop1" Id="LeftTop1" Title="LeftTop1"/>
                <vmt:LeftTopTool2ViewModel x:Name="LeftTop2" Id="LeftTop2" Title="LeftTop2"/>
            </dmc:ProportionalDock.HiddenDockables>
            <dmc:ProportionalDock x:Name="LeftPane" Id="LeftPane" Title="LeftPane" Proportion="NaN" Orientation="Vertical">
                <dmc:ToolDock x:Name="LeftPaneTop" Id="LeftPaneTop" Title="LeftPaneTop" Proportion="NaN" ActiveDockable="{Binding #LeftTop1}">
                    <!--<vmt:LeftTopTool1ViewModel x:Name="LeftTop1" Id="LeftTop1" Title="LeftTop1"/>
                    <vmt:LeftTopTool2ViewModel x:Name="LeftTop2" Id="LeftTop2" Title="LeftTop2"/>-->
                    <idc:Reference Name="LeftTop1"/>
                    <idc:Reference Name="LeftTop2"/>
                </dmc:ToolDock>
                <dmc:SplitterDock x:Name="LeftPaneTopSplitter" Id="LeftPaneTopSplitter" Title="LeftPaneTopSplitter" />
                <dmc:ToolDock x:Name="LeftPaneBottom" Id="LeftPaneBottom" Title="LeftPaneBottom" Proportion="NaN" ActiveDockable="{Binding #LeftBottom1}">
                    <vmt:LeftBottomTool1ViewModel x:Name="LeftBottom1" Id="LeftBottom1" Title="LeftBottom1"/>
                    <vmt:LeftBottomTool2ViewModel x:Name="LeftBottom2" Id="LeftBottom2" Title="LeftBottom2"/>
                </dmc:ToolDock>
            </dmc:ProportionalDock>
kekekeks commented 5 years ago

Unfortunately it won't work for forward references and we need compiler support for detecting forward references in lists.

wieslawsoltes commented 5 years ago

Unfortunately it won't work for forward references and we need compiler support for detecting forward references in lists.

For this purpose I am using Binding and only for property values.

Compiler support for detecting forward references in lists would be nice.