0

I am currently building a diagram designer using C# and WPF. I have read this article, but found the lack of mvvm driven approach not good enough for my requirements (while this article happened to be too complex for my needs).

I have a basic data class - SimpleDataEntry.cs (holds color, name, left position and top position). In my ViewModel i have the following property-

public class ViewModel : INotifyPropertyChanged
{
    public ObservableCollection<SimpleDataEntry> SimpleDataEntryCollection{ get; set; }
}

In my MainWindow.xaml i have a canvas who's ItemsControl ItemSource is binded to the SimpleDataEntryCollection in the ViewModel :

<Window ...>
<Window.Resources>
    <local:ViewModel x:Key="ViewModel"/>

    <ControlTemplate x:Key="MoveThumbTemplate"  TargetType="{x:Type local:MoveThumb}">
        <Rectangle Fill="Transparent"/>
    </ControlTemplate>

    <ControlTemplate x:Key="DesignerItemControlTemplate" TargetType="ContentControl">
        <Grid>
            <local:MoveThumb DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" 
                             Template="{StaticResource MoveThumbTemplate}" 
                             Cursor="SizeAll"/>
            <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
        </Grid>
    </ControlTemplate>
</Window.Resources>

<Grid>

<ScrollViewer Name="DesignerScrollViewer"
Background="Transparent"
    HorizontalScrollBarVisibility="Auto"
    VerticalScrollBarVisibility="Auto">

<local:DesignerCanvas x:Name="MyDesignerCanvas"
    MinHeight="800"
    MinWidth="1000"
    AllowDrop="True"
    Background="Beige">

    <ItemsControl ItemsSource="{Binding Source={StaticResource ViewModel}, Path=SimpleDataEntryCollection}">

        ...A Template for the itmes...

    </ItemsControl>
</local:DesignerCanvas>
</ScrollViewer>
</Grid>

The Items that the canvas source is bonded to are templated to show as ellipses that can be dragged around with the mouse (using a MoveThumb class). Lets call each item of the described here

"Diagram Item"

Here is how the Diagram Item is templated:

<ItemsControl.ItemTemplate>
        <DataTemplate>
            <Canvas>
                <ContentControl Template="{StaticResource DesignerItemControlTemplate}" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}">
                    <Grid>
                        <Ellipse Fill="{Binding Fill}" Width="50" Height="50" IsHitTestVisible="False"/>
                        <TextBlock Text="{Binding Data}" IsHitTestVisible="False"/>
                    </Grid>
                </ContentControl>
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

Now i want my canvas, on each time i move a Diagram Item out of its borders, to resize to be big enough so it can contain the Diagram Item at its new location. To achieve this im using a class by sukram(from the first article mentioned here) Called "DesignerCanvas" that overrides the method "MeasureOverride". By doing so, the DesignerCanvas cheks if its borders are capable of containing its children locations, and if not - stretching its borders accordingly.

        protected override Size MeasureOverride(Size constraint)
    {
        Size size = new Size();
        foreach (UIElement element in Children)
        {
            double left = Canvas.GetLeft(element);
            double top = Canvas.GetTop(element);
            left = double.IsNaN(left) ? 0 : left;
            top = double.IsNaN(top) ? 0 : top;

            element.Measure(constraint);

            Size desiredSize = element.DesiredSize;
            if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
            {
                size.Width = Math.Max(size.Width, left + desiredSize.Width);
                size.Height = Math.Max(size.Height, top + desiredSize.Height);
            }
        }

        // add some extra margin
        size.Width += 10;
        size.Height += 10;
        return size;
    }

This does not work for my approach. My problem here is as follows - In the example im basing my work upon, the Designer Itmes are Visual Only objects with no logical representation behind them, while in my project the Designer Items are logical entities living in a collection in the viewmodel, a collection my DesignerCanvas takes as items source. While debugging i have found out that my DesignerCanvas has only one child of the type :

{System.Windows.Controls.ItemsControl Items.Count:3}

//I have 3 Designer Items i create and add to the SimpleDataEntryCollection in the viewModel

I assume that that is because when im adding a new Designer Item im doing so only in the viewmodel, without adding the produced templated Designer Item as a child to the Designer Canvas. If so, where should i insert this addition to children of canvas part?(Is there a way to make this process automated? Any elements in the items source would be automatically added as children of the DesignerCanvas?) Otherwise, is there a method for me to iterate directly on the DesignerCanvas itemSource as a collection (as if it was the children collection of the DesignerCanvas)?

UPDATE 2

i have tried using an itemspanel as DesignerCanvas. Now my designer canvas has children of the type ContentPresenter. Yet the MeasureOverride() function wont fire when i drag and move the DesignerItems around, only when an item is added to the SimpleDataEntryCollection. What can be the cause of that?

<ItemsControl ItemsSource="{Binding Source={StaticResource ViewModel}, Path=SimpleDataEntryCollection}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <local:DesignerCanvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Canvas>
                            <ContentControl Template="{StaticResource DesignerItemControlTemplate}" Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}">
                                <Grid>
                                <Ellipse Fill="{Binding Fill}" Width="50" Height="50" IsHitTestVisible="False"/>
                                <TextBlock Text="{Binding Data}" IsHitTestVisible="False"/>
                            </Grid>
                        </ContentControl>
                        </Canvas>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
Community
  • 1
  • 1
CodeMex
  • 3
  • 3
  • 1
    You should probably use the Canvas as ItemsPanel of the ItemsControl, as shown e.g. here: http://stackoverflow.com/a/40190793/1136211. – Clemens Jan 25 '17 at 15:20
  • Have done as you suggested in the given example, and yet encounter the same problem. My DesignerCanvas has an only child - a Controls.ItemsControl collection. I want to "break" this barrier and have each and every DesignerItem object be assigned as the canvas child automaticly – CodeMex Jan 26 '17 at 13:14
  • Not sure what you're asking. Your DesignerCanvas has exactly the one child that you delcared in XAML, i.e. the ItemsControl. Did you perhaps intend to use the DesignerCanvas as ItemsPanel? – Clemens Jan 26 '17 at 13:43
  • My bad, i got confused and added the itemsControl to the designer canvas. Fixed it now (added a general itemscontrol and set the designerCanvas as its panel) which caused the DesignerCanvas to have as many children as there are members in the SimpleDataEntryCollection. Another problem have raised, as seen in Update2 – CodeMex Jan 26 '17 at 14:39

0 Answers0