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; }
}
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}">
... />