C
C#•10mo ago
Cam

[AVALONIA] Polylines not displaying on Canvas

I have been having this issue for a while. My Polylines which I have generated are not being displayed in the Canvas. I am using MVVM so I have created all the Models, ensured they are correctly populating, checked that they are inside the canvas using the F12 debugging tool but they just are not visible. Help? Canvas Model
c#
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;

namespace BirdsEye.Models.Panel
{
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public List<Polyline> Polylines { get; set; } // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT

}
}
c#
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using System.Collections.Generic;
using System.Text;
using System.Xml.Linq;

namespace BirdsEye.Models.Panel
{
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public List<Polyline> Polylines { get; set; } // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT

}
}
Panel View Model
c#
using BirdsEye.Models.Panel;
using BirdsEye.Resources.Functions.Data;

namespace BirdsEye.ViewModels
{
public class PanelViewModel
{
public PanelSegment PanelSegment { get; set; }
public CanvasSegment CanvasSegment { get; set; }
public PanelViewModel()
{
PanelBuilder buildPanel = new PanelBuilder();
PanelSegment = buildPanel.PanelSegment;
CanvasSegment = buildPanel.CanvasSegment;
}
}
}
c#
using BirdsEye.Models.Panel;
using BirdsEye.Resources.Functions.Data;

namespace BirdsEye.ViewModels
{
public class PanelViewModel
{
public PanelSegment PanelSegment { get; set; }
public CanvasSegment CanvasSegment { get; set; }
public PanelViewModel()
{
PanelBuilder buildPanel = new PanelBuilder();
PanelSegment = buildPanel.PanelSegment;
CanvasSegment = buildPanel.CanvasSegment;
}
}
}
45 Replies
Cam
CamOP•10mo ago
XAML View
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel">

<Canvas x:Name="PanelCanvas" Width="1201" Height="601">
<ItemsControl ItemsSource="{Binding CanvasSegment.Polylines}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Create a Polyline element for each item in Polylines -->
<Polyline Points="{Binding Points}" Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel">

<Canvas x:Name="PanelCanvas" Width="1201" Height="601">
<ItemsControl ItemsSource="{Binding CanvasSegment.Polylines}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Create a Polyline element for each item in Polylines -->
<Polyline Points="{Binding Points}" Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Canvas>
</UserControl>
Main Window View
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:BirdsEye.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BirdsEye.Views.MainWindow"
xmlns:panels="clr-namespace:BirdsEye.Views.Panels"

x:Class="BirdsEye.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="BirdsEye">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<panels:Newmarket/>
</Window>
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:BirdsEye.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BirdsEye.Views.MainWindow"
xmlns:panels="clr-namespace:BirdsEye.Views.Panels"

x:Class="BirdsEye.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="BirdsEye">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<panels:Newmarket/>
</Window>
Cam
CamOP•10mo ago
Results
No description
Klarth
Klarth•10mo ago
Wrong XAML layout. Your ItemsControl should be top and use an ItemsPanel for the Canvas. Check https://docs.avaloniaui.net/docs/concepts/custom-itemspanel for specifics
Klarth
Klarth•10mo ago
Attached properties like Canvas.Left only get inspected when they're a direct child of the Canvas control.
Cam
CamOP•10mo ago
Appreciate that. I’ll give it a try
Cam
CamOP•10mo ago
No change 😦 XAML Code
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel">

<ItemsControl ItemsSource="{Binding CanvasSegment.Polylines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1201" Height="601"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Create a Polyline element for each item in Polylines -->
<Polyline Points="{Binding Points}" Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel">

<ItemsControl ItemsSource="{Binding CanvasSegment.Polylines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1201" Height="601"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Create a Polyline element for each item in Polylines -->
<Polyline Points="{Binding Points}" Stroke="{Binding Stroke}" StrokeThickness="{Binding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
No description
Klarth
Klarth•10mo ago
Are you using compiled bindings?
Cam
CamOP•10mo ago
can you elaborate a bit? im just grabbing the value of a list variable of polylines in c#
c#
using Avalonia.Controls.Shapes;
using System.Collections.Generic;

namespace BirdsEye.Models.Panel
{
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public List<Polyline> Polylines { get; set; } // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT

}
}
c#
using Avalonia.Controls.Shapes;
using System.Collections.Generic;

namespace BirdsEye.Models.Panel
{
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public List<Polyline> Polylines { get; set; } // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT

}
}
looking at the docs i dont believe i am, wouldnt have a clue how to use them
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel"
x:CompileBindings="True">

<ItemsControl ItemsSource="{CompiledBinding CanvasSegment.Polylines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1201" Height="601"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Polyline Points="{CompiledBinding Points}" Stroke="{CompiledBinding Stroke}" StrokeThickness="{CompiledBinding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1201" d:DesignHeight="601"
xmlns:viewModels="clr-namespace:BirdsEye.ViewModels"
x:Class="BirdsEye.Views.Panels.Newmarket"
x:DataType="viewModels:PanelViewModel"
x:CompileBindings="True">

<ItemsControl ItemsSource="{CompiledBinding CanvasSegment.Polylines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1201" Height="601"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Polyline Points="{CompiledBinding Points}" Stroke="{CompiledBinding Stroke}" StrokeThickness="{CompiledBinding StrokeThickness}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
like this?
Klarth
Klarth•10mo ago
That's one way. You should typically enable them project wide in csproj and use {ReflectionBinding} if you're limited in some scenario. I need to leave for awhile though.
Cam
CamOP•10mo ago
your allgood. still didnt work but thanks for your help this far 🙂
Klarth
Klarth•10mo ago
This is weird because you're storing Polyline controls. You're also storing them in properties that don't raise INPC and in a collection that doesn't raise INotifyCollectionChanged. So you need to be very careful about initialization order and when the binding happens. You can try a ContentControl instead, but I'm not entirely sure what happens when you try to wrap a control in it.
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
Cam
CamOP•10mo ago
All I want to do is get a bunch of Polylines and smash them onto a canvas how would you implement INotifyCollectionChanged to this?
Klarth
Klarth•10mo ago
What Mvvm framework are you using?
Cam
CamOP•10mo ago
Avalonia. Also is this an issue?
No description
Klarth
Klarth•10mo ago
Avalonia is a GUI framework, not an MVVM framework.
Cam
CamOP•10mo ago
oh then .NET i think
Klarth
Klarth•10mo ago
That's also not an MVVM framework.
Cam
CamOP•10mo ago
No description
Klarth
Klarth•10mo ago
How did you create your project? Avalonia templates (the MVVM one at least) lets you choose because ReactiveUI and Mvvm Toolkit.
Cam
CamOP•10mo ago
No description
Cam
CamOP•10mo ago
then Reactive i suppose sorry its one of the first times ive used model view
Klarth
Klarth•10mo ago
You can probably get around it for now by doing:
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public ObservableCollection<Polyline> Polylines { get; } = new(); // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT
}
public class CanvasSegment
{
public string ID { get; set; } // IDENTIFICATION OF THE CANVAS SEGMENT
public string Name { get; set; } // NAME OF THE CANVAS SEGMENT
public ObservableCollection<Polyline> Polylines { get; } = new(); // LIST OF CANVAS RAILS WITHIN THE CANVAS SEGMENT
public LiveDisplay Display { get; set; } // DATA CONTROLLING THE LIVE DISPLAY OF THE CANVAS SEGMENT
}
But I'm guessing CanvasSegment is in another collection, so that probably needs changed too.
Cam
CamOP•10mo ago
it is when i begin making multiple different "Panels" but at the moment its only used as a singular instance
Cam
CamOP•10mo ago
This Model is unused at the moment but will be in the future
No description
Klarth
Klarth•10mo ago
I really suggest you work on some sample apps instead. Like basic data entry on a form so you can understand what INotifyPropertyChanged does, how bindings actually work, etc. You simply can't work with the UI this way unless you refresh everything on any change. And change tracking without specific logic everywhere is hard. Which is an option (I do this for a vector art app, but still have change notifications), but it's not ideal in all cases.
Cam
CamOP•10mo ago
i knew that a refresh everything would be the case but i havent got any other option im afraid at least not one that i have thought of
Klarth
Klarth•10mo ago
It's different in my case because everything is custom drawn via Skia. I'm not using UI controls.
Cam
CamOP•10mo ago
i dont necessarily need to have these controls be interacted with. its purely just a line that needs to be on the ui, everything logic and interaction wise can pretty much be done seperately from the lines that are drawn on
Klarth
Klarth•10mo ago
If you're using UI controls (provided by Avalonia), you'll need to recreate the entire hierarchy, write a lot of synchronization code, or explicitly set DataContext = null; DataContext = vm; to force all bindings to update...which recreates a lot of controls if you're using collection controls. ^ I mean without using proper MVVM techniques with INotifyPropertyChanged.
Cam
CamOP•10mo ago
Oh i see. you scared me a bit. Yea well im still learning so I will definitely look at the INotifyPropertyChanged The basics of what I need from this specific part of the application are just Lines drawn onto the UI that I can change the look of (size, length etc) and also apply a sort of "collision detection" to. Best way to describe the need for the "collision detection" is if you think of a volume slider how it follows a path, now make that path able to change to a diagonal or whichever direction it needs to go to follow the "track" the lines represent a railway track and basically i need to be able to "slide a train" along the railway track and it follows the path long story lol
Klarth
Klarth•10mo ago
If you're using ReactiveUI, then it provides an implementation via ReactiveObject and this.RaiseAndSetIfChanged or whatever. I find Mvvm Toolkit to be much more intuitive and easier to use though.
Cam
CamOP•10mo ago
reckon for a newbie that its better if i switch to that rather than Rx?
Klarth
Klarth•10mo ago
IMO, a beginner isn't going to be learning how to use observables...which is a core part of Rx and really the only reason why you'd use it over (or side-by-side with) Mvvm Toolkit. So I would say yes.
Cam
CamOP•10mo ago
Okay sweet ill give that a go. Ill do some readup on this INotify... and see if i can get somewhere im guessing that I would have to recreate the project to change toolkits? unless you know a way to do it without that option?
Klarth
Klarth•10mo ago
It's easier to recreate if you don't have any experience.
Cam
CamOP•10mo ago
fun times 🙂 Cheers for the help. Ill let you know how it goes i suppose
Klarth
Klarth•10mo ago
I would still recommend some basic practice regardless. It's not obvious to beginners to know when INPC matters, but it's basically: anytime a property is being changed from code or is binded to 2 or more controls.
Cam
CamOP•10mo ago
And if that property change intends to affect the UI then it needs INPC?
Klarth
Klarth•10mo ago
In your ViewModels, it's more straightforward to make all properties respect INPC or otherwise be get-only. The case where you don't need it is when the UI updates the property in the VM. Say you bind a string to a TextBox.Text. When the user types, the TextBox will update the property. Which is fine, no notifications needed because no other controls need to be aware of the change. TextBox already knows. However, say you do MyBindedText = "Testing123"; in your VM, then the TextBox needs a change notification to let it know the property changed.
Cam
CamOP•10mo ago
Oh i got you, so if its "UI to Code to UI" then no need for INPC however if its "Code to UI" then yes
Klarth
Klarth•10mo ago
"UI to Code" No "Code to UI" Yes "UI to Code to UI" Yes
Cam
CamOP•10mo ago
yea i realised i put it in a bad way i got you tho haha
Klarth
Klarth•10mo ago
It's easier to just implement it for all cases when you're starting out. Because it's easy to find yourself in a 30 minute debugging session over something dumb.
Cam
CamOP•10mo ago
try a week on this shit just to figure out i need INPC and a new MVVM toolkit.....i love my hobby 😆 its algood tho. I knew this wouldnt be easy at all. I just needed a way to keep my software skills fresh because I havent had any use for them since leaving study and covid forcing me to choose a different path of work
Cam
CamOP•10mo ago
Fixed it up 🙂
No description
Want results from more Discord servers?
Add your server