Как изогнуть список и его полосу прокрутки в WPF .NET 5.0

#c# #.net #wpf #listbox

Вопрос:

коллеги-программисты!

Я только начал изучать WPF, и в рамках своих первых проектов я пытаюсь создать 2 изогнутых списка каруселей рядом с центральным кругом с обеих сторон. Используя пользовательскую панель в списке, мне удалось получить этот результат:

 <Grid Margin="7" Height="450">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="300"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Viewbox Grid.Column="1" Margin="20 0 20 0" PreviewMouseDown="Viewbox_PreviewMouseDown" Style="{StaticResource MainOption}">
            <Grid Width="230" Height="230" >
                <Ellipse Width="230" Height="230" Stroke="{StaticResource PrimaryDarkColor}" StrokeThickness="2" Fill="Transparent" />
                <!--<Ellipse Width="200" Height="200">
                    <Ellipse.Fill>
                        <ImageBrush ImageSource="/Assets/Profile.ico" />
                    </Ellipse.Fill>
                </Ellipse>-->
                <md:PackIcon Kind="Map" Foreground="White" Width="180" Height="180" Margin="25 10 0 0" />
                <TextBlock Text="Map" HorizontalAlignment="Center" VerticalAlignment="Bottom" Foreground="White" Margin="0 0 0 30" Style="{StaticResource MenuItem}"/>
            </Grid>
        </Viewbox>


        <ListView Grid.Column="0" ItemsSource="{Binding LeftMenuItems}" Height="360" FlowDirection="RightToLeft" ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <cc:CurvedPanel RadiusX="70"/>
        
                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <!--<Button Content="{Binding Text}" Width="100" Command="{Binding OpenCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding Window}"/>-->
                    <TextBlock Text="{Binding Text}" Style="{StaticResource MenuItem}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <ListView Grid.Column="2" ItemsSource="{Binding RightMenuItems}" Height="360" ScrollViewer.VerticalScrollBarVisibility="Auto">
            <ListView.ItemsPanel>
                <ItemsPanelTemplate>
                    <cc:CurvedPanel RadiusX="90" />

                </ItemsPanelTemplate>
            </ListView.ItemsPanel>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <!--<Button Content="{Binding Text}" Width="100" Command="{Binding OpenCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding Window}"/>-->
                    <TextBlock Text="{Binding Text}" Style="{StaticResource MenuItem}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
 
 public class CurvedPanel : StackPanel
    {
        public double RadiusX { get; set; }
        protected override Size MeasureOverride(Size availableSize)
        {
            return base.MeasureOverride(availableSize);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            double angle = 80, centerX = 0, centerY = finalSize.Height / 2, decrement = 20, radiusY = centerY * 0.8, radiusX = 70;
            Point prevPoint = new Point(0, 0);

            if (Children.Count > 0)
            {
                foreach (UIElement child in Children)
                {
                    var angleInRadians = AngleToRadian(angle);

                    Point childPosition = new Point(radiusX * Math.Cos(angleInRadians), radiusY * -Math.Sin(angleInRadians)   radiusY);

                    child.Arrange(new Rect(new Point(childPosition.X, childPosition.Y), new Size(finalSize.Width, child.DesiredSize.Height)));

                    angle -= decrement;
                }
            }

            return finalSize;
        }


        private double AngleToRadian(double angle)
        {
            return angle * (Math.PI / 180);
        }
    }
 

enter image description here

But this is far from ideal, because (as image shows) items overlap. Probably because it doesn’t account for item’s size itself. I want to prevent this overlapping, but can’t figure out changes that need to be implemented.
Plus I want to make curved scrollbar, which when scrolling will scroll along curved path. But I have no idea how to do this.

I heard there was once PahtListBox, which could accomplish what I need, but I don’t think this is available in core.

Any help will be greatly appreciated!