I’ve spent few hours learning about UIElements and trying to convert one editor tool. I came to the conclusion that UXML is not really needed. I don’t like how to access single VisualElement I have to Query (or Q) and write same object name twice in UXML and C#.
I’ll show what I was trying to do and how I converted it to C#. I’ll skip few parts that are same in both examples. (I’ve decided to inline specific styling if it’s not repetitive as writing object name to .USS file for that reason seems like an overkill.)
Old UXML:
<?xml version="1.0" encoding="utf-8"?>
<UXML
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="UnityEngine.UIElements">
<Box name="header">
<Image name="playfablogo" style="background-image: url(Images/playfablogo.png);"/>
<Box style="flex-direction: row; align-items: center;">
<Button name="gMText" class="gameManagerBtn" text="GAME MANAGER"/>
<Button name="gMIcon" class="gameManagerBtn" />
</Box>
</Box>
<IMGUIContainer name="progressBar"/>
<IMGUIContainer name="mainIMGUI" style="flex-grow: 1;"/>
</UXML >
Old C#:
private Box header;
private VisualElement gMText;
private IMGUIContainer mainIMGUI;
void OnEnable()
{
root = rootVisualElement;
root.Clear();
rootVisualElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(Path.Combine(Strings.PATH_UI, "styles.uss")));
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(Path.Combine(Strings.PATH_UI, "mainView.uxml"));
template.CloneTree(root);
header = root.Q<Box>("header");
mainIMGUI = root.Q<IMGUIContainer>("mainIMGUI");
gMText = root.Q<VisualElement>("gMText");
}
void Update ()
{
//actions on header, gMText, mainIMGUI
}
Here’s converted to full C# with help of few extension methods:
//this class in seperate file like .uxml
public class MainView
{
public Box Header;
public VisualElement GMText;
public IMGUIContainer MainIMGUI;
public VisualElement[] Elements;
public MainView()
{
Elements = new VisualElement[] {
Box.Set(name: "header").AssignTo(out Header).AddRange(
Image.Set(name: "playfablogo", background_image: Strings.PATH_UI_IMG("playfablogo.png")),
Box.Set(flexDirection: FlexDirection.Row, alignItems: Align.Center).AddRange(
Button.Set(name: "gMText", _class: "gameManagerBtn", text: "GAME MANAGER").AssignTo(out GMText),
Button.Set(name: "gMIcon", _class: "gameManagerBtn")
)
),
IMGUIContainer.Set(name: "progressBar"),
IMGUIContainer.Set(name: "mainIMGUI", flexGrow: 1).AssignTo(out MainIMGUI)
};
}
}
//Editor class file
private MainView mainView;
void OnEnable()
{
root = rootVisualElement;
root.Clear();
rootVisualElement.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>(Path.Combine(Strings.PATH_UI, "styles.uss")));
mainView = new MainView();
root.AddRange(mainView.Elements);
}
void Update ()
{
//actions on mainView.Header, mainView.GMText, mainView.MainIMGUI
}
Yes uxml looks slightly better for ex. flex-direction: row; vs flexDirection: FlexDirection.Row but that could be improved with better methods/enums. Other than that C# is far more powerful. I could go further and for ex. type: new Box().Set(name: nameof(Header) instead of new Box().Set(name: "header") that way I wouldn’t have any problems with renaming this object in the future. As you can also see I’m using simple AssignTo to bind elements to fields.
public static VisualElement AssignTo(this VisualElement v, out VisualElement reference)
{
reference = v;
return v;
}