I didn’t know why Xaml controls were made to assign the control template directly to the control property, so I wrote an IStyleController interface when I was making GacUI, which is essentially the same as the Control Template object.

As I was writing, I realized that some controls had to have a fixed look (such as the ScrollView family, which always has two scrollbars), so IT was impossible to keep the IStyleController of the ScrollView control open. Instead, the ScrollView provides a fixed IStyleController implementation, and uses the Template Method Pattern to create an IStyleProvider. The base classes of all istyleProviders are exactly the same as IStyleController, and at this point I have a vague feeling that there must be some kind of mistake.

So two or three years later, in 2014, I started allowing GacUI to write skins in XML, but XML, like XAML, was designed to write constructors, so it was awkward to implement interfaces in XML, so I created a Template class for each control. Let XML construct the Template, and then use pre-written code to process the corresponding Template into the corresponding IStyleController or IStyleProvider. Then everybody will be GuiControlTemplateStyles. H found inside a lot of this adaptor.

For those of you who haven’t read the GacUI code, this may be confusing, but I’ll use C# as a simple shorthand:

public class Control
{
    public interface IStyleController
    {
        void SetText(string value);
        void SetFont(Font font);
        // Composition is just a box for composing
        // Box can be placed in geometry
        // The entire window is actually a layout tree composed of vector map
        Control is a facade pattern that allows you to easily control a subtree
        // Because you don't want to walk through the tree to change the text of a button
        Composition BoundsComposition {get; }
    }

    public interface IStyleProvider
    {
        void SetText(string value);
        void SetFont(Font font);
    }

    public Control(IStyleController controller);
}

public class Label : Control
{
    public interface IStyleController : Control.IStyleController
    {
        void SetColor(Color color);
    }

    public Label(IStyleController controller);
}

public class ScrollView : Control
{
    public interface IStyleProvider : Control.IStyleProvider
    {
    }

    private class StyleController : Control.IStyleController
    {
        // Put two scrollbars and the required parameters into the IStyleProvider
        public StyleController(IStyleProvider provider);
    }

    public ScrollView(IStyleProvider provider)
        :base(new StyleController(provider))
    { . }
}

class ControlTemplate : Composition
{
    public string Text{get;set; }
    public Font Font{get;set; }
}

class ControlTemplate_StyleController : Control.IStyleController
{
    private ControlTemplate template;

    public ControlTemplate_StyleController(ControlTemplate template)
    {
        this.template = template;
    }

    public void SetText(string value) { controlTemplate.Text = value; }
    public void SetFont(Font font) { controlTemplate.Font = value; }
    public Composition BoundsComposition { get { return controlTemplate; } }
}
Copy the code

Next comes the XML for the control skin:

<Instance ref.Class="MyControlTemplate">
  <ControlTemplate Text=Fuck, Font=Shit>
    <! -- Some decorations -->
  </ControlTemplate>
</Instance>
<! --
This code generates class MyControlTemplate: ControlTemplate {... }
-->
Copy the code

Then create the control with the skin:

<Window>
  <Control ControlTemplate="MyControlTemplate"/>
</Window>
<! --
The control is actually new like this:
new Control(new ControlTemplate_StyleController(new MyControlTemplate))
-->
Copy the code

I vaguely remember that I had discussed this problem with @Assembly head, and then there was really no way to do it first. But it actually stinks for the following reasons:

  1. IStyleProvider and IStyleController have the same functions.
  2. IStyleProvider is created because some controls restrict the appearance of the IStyleController. In fact, this means that IStyleController inheritance is difficult and should not be designed this way. So what if one of these istyleproviders is occupied again? Invent a new interface and the name won’t be enough, and customizing the name for the depth of inheritance is obviously a silly thing to do.
  3. XML is a constructor, and constructors can only modify classes and not inherit interfaces, so you have a bunch of Adaptors. Adaptor simply forwards functions to the template, changing the pull and push modes to each other.
  4. Adding a new control each time would be a huge hassle.

Of course, I’m not being silly. GacUI didn’t have XML custom controls and skins in the early days, so it’s hard to make choices that take everything into account. This event also tells us that with the change of software functions, even a good design may deteriorate in the past. We must dare to rebuild, so that software development can last for decades, and there is no need to overturn and rewrite.

A few years ago I was thinking about writing a GacUI and Design Patterns series, but then I realized that the problem hadn’t been solved properly, so I put it on hold, and I’m sure I’ll change it later. Of course, this design is not good, does not mean that the knowledge used in this design is not good. Inverse of Control is used a lot, so you should learn it well, and don’t use JavaEE as learning materials. I don’t know why those things stink on JavaEE, maybe it has something to do with his reflexes causing people to mess around. When you can’t help it, you like to fuck.

So three years later, I had a lot of things to deal with, so I came back to the problem. Later, I realized how wise Xaml was to let Control talk to the Control Template directly, perfectly avoiding all of the issues raised above.

I’ve had a lot of design headaches writing GacUI over the past six years. Every time I came up with an idea that turned out to be perfect, I was curious to see how Xaml did it, and Xaml did it that way. Anything I come up with that’s different from Xaml is probably wrong.

The predecessor of Xaml, Avalon (beta version), was released in 2001, and Avalon was actually a clone of the Win32 XML control toolkit within the Office division. But the toolkit is old and has become one of the major obstacles to developing new features in Office. However, one of the pioneers who developed it in those years, the last interviewer when I went over the wall, has already become a Distinguished Engineer. There are still a few people who climb to Partner, and a lot of people stay in Principal Engineer. Microsoft’s Principal is not easy to reason with.

Of course, this is just history, many of the amazing things about Xaml were not in the original Office toolkit, so the credit should go to Xaml.

So we’ve been working on removing the IStyleController and IStyleProvider interfaces and having control talk directly to the Control Template. Doing this kind of earth-shaking refactoring in tens of thousands of lines of code has never been easy. But things that make the code significantly better, of course, should be done. So you’ll see three recently added steps in todo.md:

  1. Replace all istyleProviders with IStyleController, and any additional implementations that need to be inherited are transferred to the corresponding ControlTemplate class. (Just finished today)
  2. Delete IStyleController, controls allow direct communication with ControlTemplate, in fact is equal to the GuiControlTemplateStyles. H almost all the big lump of only one row of direct inline function into control.
  3. Finally, change the constructor’s ControlTemplate* parameter to a property that can be modified at run time.

Refactoring is not a one-size-fits-all exercise, but a step-by-step, safe way to refactor your code.

Ah, finally Xaml like (escape. The following C# code is roughly what it looks like after refactoring:

public class ControlTemplate : Composition
{
    public string Text {get;set; } // The GacUI attribute can have its own Changed event
    public string Font {get;set; }
} 

public class Control
{
    public virtual ControlTemplate Template {get;set; }
}

public class LabelTemplate : ControlTemplate
{
    public Color Color {get;set; }
}

public class Label : Control
{
    // Check that it is a LabelTemplate
    public override ControlTemplate Template {get;set; }
}
Copy the code

Clean and fresh! Xaml great!