wpfxamllistboxwpf-controlswpf-positioning

wpf mvvm add bezier curves to canvas


I'm creating a BezierSpline Editor to make custom animation easing functions. The user will have the possibility to add beziercurve parts to create his own easing function.

I have a MainWindow with 2 usercontrols DesignerControl and InfoControl sharing a DesignerVM as DataContext.

DesignerControl is the main view for viewing and editing the splines using draggable Rectangles, and the InfoControl is the view for creating, selecting, removing splines parts with buttons and a listbox and also editing control points using textblocks.

The DesignerVM contains an ObservableCollection of SplineVM.

I have a listbox in each usercontrol binded to the ObservableCollection.

I have used ItemsPanelTemplate to change the listbox into a Canvas in the designerControl, and for now, I'm using a DataTemplate as ListBox.ItemTemplate to change my items in a usercontrol named SplineControl.

In this SplineControl, I have a Canvas with a fixed size containing a Path defining the curve, and 4 Rectangles to define the controlPoints.

<UserControl x:Class="Easing_Spline_Tool.SplineControl"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:local="clr-namespace:Easing_Spline_Tool"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         DataContext="SplineVM">
    <UserControl.Resources>
        <local:MathConverter x:Key="mathconverter"/>
    </UserControl.Resources>
    <Canvas Width="300" Height="300" x:Name="mycanvas" Background="Black">
        <Path x:Name="curve" Stroke="#F02828" StrokeThickness="3">
            <Path.Data>
                <PathGeometry>
                    <PathGeometry.Figures>
                        <PathFigureCollection>
                            <PathFigure>
                                <PathFigure.Segments>
                                    <PathSegmentCollection x:Name="pathsegment"/>
                                </PathFigure.Segments>
                            </PathFigure>
                        </PathFigureCollection>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>
        <Rectangle Fill="White" x:Name="curvePoint1" Width="8" Height="8" Canvas.Bottom="0" Canvas.Left="0"/>
        <Rectangle Fill="White" x:Name="curvePoint2" Width="8" Height="8" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(@VALUE/4)}"/>
        <Rectangle Fill="White" x:Name="curvePoint3" Width="8" Height="8" RadiusX="4" RadiusY="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(@VALUE/2)}"/>
        <Rectangle Fill="White" x:Name="curvepoint4" Width="4" Height="4" Canvas.Bottom="0" Canvas.Left="{Binding ElementName=mycanvas, Path=ActualWidth, Converter={StaticResource mathconverter}, ConverterParameter=(@VALUE)}"/>
    </Canvas>
</UserControl>

Edit: I'm using Rachel Lim's MathConverter for my bindings in the Rectangles and they work very well.

When I launch the application, my usercontrol is in the upleft corner of my mainwindow Canvas and should be in the downleft corner instead. I've set my userControl's vertical alignment and horizontal alignment to bottom and left...

I've also tried to set Canvas.Bottom and Canvas.Left on my userControl but nothing changed

Am I missing something?


Solution

  • I've managed to get around this problem using a Grid to contain my UserControl in the Desiger view so the userControl is well positioned using Margin and stretched in the Grid.

    I also changed my listbox into an ItemsControl to define my own rules of selection but this has nothing to do with this post (and it works now ^^)

    <ItemsControl x:Name="curveList"
                  ItemsSource="{Binding SplineVMList}" 
                  Background="{x:Null}"
                  >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid x:Name="canvas" Margin="46,60,83,46"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:SplineControl x:Name="usercontrol" IsSelected="{Binding IsSelected, Mode=TwoWay}" Index="{Binding Index, Mode=TwoWay}">
                    <local:SplineControl.Style>
                        <Style>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                    <Setter Property="Panel.ZIndex" Value="99"/>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding IsSelected}" Value="False">
                                    <Setter Property="Panel.ZIndex" Value="-99"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </local:SplineControl.Style>
                </local:SplineControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <Grid>
        <TextBlock FontSize="20" Foreground="Black" Text="Time" VerticalAlignment="Bottom" HorizontalAlignment="Right" Margin="0,0,11,35"/>
        <TextBlock FontSize="20" Foreground="Black" Text="Acceleration" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,9"/>
        <Rectangle Fill="White" Stroke="Black"  Height="2"  VerticalAlignment="Bottom" Margin="45,0,65,45"/>
        <Rectangle Fill="White" Stroke="Black" Width="2" HorizontalAlignment="Left" Margin="45,45,0,45"/>
    </Grid>
    

    Sometimes, a Canvas is not so usefull....

    Thx for concern anyway.