Search code examples
c#wpfxamlcanvasmvvm

Get Center Coordinates Of Canvas Panel WPF MVVM


I want to draw a shape in the middle of the canvas with mvvm pattern.

In code behind I was reaching the middle point by using the ActualWidth and ActualHeight properties.

But I'm having trouble doing this in MVVM. How can i achieve ActualWidth and ActualHeight properties from XAML ?

These are XAML codes:

<Window x:Class="CanvasSampleMvvm.View.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        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"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
        xmlns:local="clr-namespace:CanvasSampleMvvm.View"
        xmlns:model="clr-namespace:CanvasSampleMvvm.Model"
        xmlns:vm="clr-namespace:CanvasSampleMvvm.ViewModel"
        mc:Ignorable="d"
        Title="MainView" Height="450" Width="800">

    <Window.Resources>
        <vm:MainViewVM x:Key="vm"/>
    </Window.Resources>
    <Grid DataContext="{StaticResource vm}">

        <ItemsControl ItemsSource="{Binding Path=Shapes}">
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type model:mShape}">
                    <Path Data="{Binding Geometry}" Stroke="{Binding Stroke}" Fill="{Binding Fill}" RenderTransform="{Binding Transform}" />
                </DataTemplate>
            </ItemsControl.Resources>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                  
                        <Canvas ClipToBounds="True" Background="Transparent" DataContext="{StaticResource vm}">
                        
                       </Canvas>
                
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding Path=XPos}" />
                    <Setter Property="Canvas.Top" Value="{Binding Path=YPos}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>

    </Grid>
</Window>

These are MainView Model codes:

public class MainViewVM : INotifyPropertyChanged
    {
        public ObservableCollection<mShape> Shapes { get; } = new ObservableCollection<mShape>();
        public MainViewVM()
        {
            // XPos abd YPos values has to be center coordinates of canvas panel
            Shapes.Add(new mShape
            {
                XPos = 100,
                YPos = 100,
                Stroke = new SolidColorBrush(Color.FromArgb(0xFF, 0x66, 0x66, 0x66)),
                Geometry = new EllipseGeometry { RadiusX = 50, RadiusY = 50 },
                Fill = (SolidColorBrush)new BrushConvert,r().ConvertFrom("#D3D3D3")

            });


        }
        
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;

    }

This is Model Class:

public class mShape
    {
        public double XPos { get; set; }
        public double YPos { get; set; }
        public Geometry Geometry { get; set; }
        public Brush Stroke { get; set; }
        public Brush Fill { get; set; }
    }

enter image description here


Solution

  • How can i achieve ActualWidth and ActualHeight properties from XAML ?

    You need to implement some piece of code that tracks changes of these properties in the view and set your view model properties.

    You can take a look at this SizeObserver implementation on GitHub for an example of this can be achieved.

    It implements attached properties that you can bind to in your XAML markup:

    <Canvas SizeObserver.Observe="True"
            SizeObserver.ObservedWidth="{Binding YourViewModelWidthProperty, Mode=OneWayToSource}"
            SizeObserver.ObservedHeight="{Binding YourViewModelHeightProperty, Mode=OneWayToSource}">
            ... />