diff --git a/XSDVisualiser.Desktop/Converters/CardinalityToLabelConverter.cs b/XSDVisualiser.Desktop/Converters/CardinalityToLabelConverter.cs
new file mode 100644
index 0000000..133e5ce
--- /dev/null
+++ b/XSDVisualiser.Desktop/Converters/CardinalityToLabelConverter.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using XSDVisualiser.Models;
+
+namespace XSDVisualiser.Desktop.Converters
+{
+ public class CardinalityToLabelConverter : IValueConverter
+ {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is Occurs occ)
+ {
+ var min = occ.Min;
+ var max = occ.MaxIsUnbounded ? "*" : occ.Max.ToString(culture);
+ return $"[{min}..{max}]";
+ }
+ return null;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+ }
+}
diff --git a/XSDVisualiser.Desktop/Converters/OptionalToBorderBrushConverter.cs b/XSDVisualiser.Desktop/Converters/OptionalToBorderBrushConverter.cs
new file mode 100644
index 0000000..8fef8b7
--- /dev/null
+++ b/XSDVisualiser.Desktop/Converters/OptionalToBorderBrushConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Globalization;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+using XSDVisualiser.Models;
+
+namespace XSDVisualiser.Desktop.Converters
+{
+ public class OptionalToBorderBrushConverter : IValueConverter
+ {
+ private static readonly IBrush RequiredBrush = new SolidColorBrush(Color.FromRgb(0x33, 0x99, 0x33));
+ private static readonly IBrush OptionalBrush = new SolidColorBrush(Color.FromRgb(0xCC, 0x66, 0x33));
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is Occurs occ)
+ {
+ // Optional if minOccurs == 0
+ return occ.Min == 0 ? OptionalBrush : RequiredBrush;
+ }
+ return RequiredBrush;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+ }
+}
diff --git a/XSDVisualiser.Desktop/Converters/OptionalToThicknessConverter.cs b/XSDVisualiser.Desktop/Converters/OptionalToThicknessConverter.cs
new file mode 100644
index 0000000..6145c90
--- /dev/null
+++ b/XSDVisualiser.Desktop/Converters/OptionalToThicknessConverter.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Globalization;
+using Avalonia;
+using Avalonia.Data.Converters;
+using XSDVisualiser.Models;
+
+namespace XSDVisualiser.Desktop.Converters
+{
+ public class OptionalToThicknessConverter : IValueConverter
+ {
+ private static readonly Thickness Required = new Thickness(2);
+ private static readonly Thickness Optional = new Thickness(2);
+
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is Occurs occ)
+ {
+ // We could vary thickness; for now same thickness regardless, reserved for future styling.
+ return occ.Min == 0 ? Optional : Required;
+ }
+ return Required;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ => throw new NotSupportedException();
+ }
+}
diff --git a/XSDVisualiser.Desktop/MainWindow.axaml b/XSDVisualiser.Desktop/MainWindow.axaml
index 9acbc8e..23aab81 100644
--- a/XSDVisualiser.Desktop/MainWindow.axaml
+++ b/XSDVisualiser.Desktop/MainWindow.axaml
@@ -1,10 +1,59 @@
-
-
-
-
-
+ x:DataType="m:SchemaModel"
+ Title="XSD Visualiser" Width="1100" Height="800">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XSDVisualiser.Desktop/MainWindow.axaml.cs b/XSDVisualiser.Desktop/MainWindow.axaml.cs
index 50f86e3..b8be322 100644
--- a/XSDVisualiser.Desktop/MainWindow.axaml.cs
+++ b/XSDVisualiser.Desktop/MainWindow.axaml.cs
@@ -1,24 +1,21 @@
using System;
-using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
+using XSDVisualiser.Models;
using XSDVisualiser.Parsing;
-using XSDVisualiser.Utils;
namespace XSDVisualiser.Desktop
{
public partial class MainWindow : Window
{
private Button _openBtn = null!;
- private TextBox _output = null!;
public MainWindow()
{
InitializeComponent();
_openBtn = this.FindControl