Group and ungroup

Hi,
I need some help to get group and ungroup working.

using System;
using System.Collections.ObjectModel;
using System.Windows;
using Northwoods.GoXam.Model;

namespace GoWpfGrouping
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            CreateData();
        }

        private void CreateData()
        {
            // model is a GraphModel using GraphModelNodeData<String> as the node data,
            // and the node keys are strings
            var model = new GraphModel<GraphModelNodeData<String>, String>();
            model.NodeKeyPath = "Key"; // use the GraphModelNodeData.Key property
            model.ToNodesPath = "ToKeys"; // this node property gets a list of node keys
            model.NodeIsGroupPath = "IsSubGraph"; // node property is true if it’s a group
            model.GroupNodePath = "SubGraphKey"; // node property gets container’s name
            model.NodesSource = new ObservableCollection<GraphModelNodeData<String>>()
            {
                new GraphModelNodeData<String>()
                {
                    Key = "Alpha",
                    ToKeys = new ObservableCollection<String>() {"Beta", "Gamma"}
                },
                new GraphModelNodeData<String>()
                {
                    Key = "Beta",
                    ToKeys = new ObservableCollection<String>() {"Beta"}
                },
                new GraphModelNodeData<String>()
                {
                    Key = "Gamma",
                    ToKeys = new ObservableCollection<String>() {"Delta"},
                    SubGraphKey = "Epsilon"
                },
                new GraphModelNodeData<String>()
                {
                    Key = "Delta",
                    ToKeys = new ObservableCollection<String>() {"Alpha"},
                    SubGraphKey = "Epsilon"
                },
                new GraphModelNodeData<String>()
                {
                    Key = "Epsilon",
                    IsSubGraph = true
                },
            };
            MyDiagram.Model = model;
        }
    }
}

and in Xaml:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:go="http://schemas.nwoods.com/GoXam" x:Class="GoWpfGrouping.MainWindow"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <go:Diagram x:Name="MyDiagram">
            <go:Diagram.ContextMenu>
                <ContextMenu> 
                    <MenuItem Command="{Binding CommandHandler.GroupCommand, ElementName=MyDiagram}" Header="Group" />
                    <MenuItem Command="{Binding CommandHandler.UngroupCommand, ElementName=MyDiagram}" Header="Ungroup" />
                </ContextMenu>
            </go:Diagram.ContextMenu>
        </go:Diagram>
    </Grid>
</Window>

I read something about PrototypeGroup, but have found no description how to use it.

Just set CommandHandler.PrototypeGroup to a GraphModelNodeData that should be represented by a Group, and the CommandHandler.Group command will be enabled.

Like the following?

MyDiagram.CommandHandler.PrototypeGroup = new GraphModelNodeData<String>()
{
    Key = "Test",
    IsSubGraph = true
};

Another question:
I can’t get the CommandBinding working.

That looks OK to me. Here’s an example:

    <go:Diagram ...>
      <go:Diagram.CommandHandler>
        <go:CommandHandler>
          <go:CommandHandler.PrototypeGroup>
            <local:NodeData Key="Group" IsSubGraph="True" Color="Blue" Stroke="LightBlue" />
          </go:CommandHandler.PrototypeGroup>
        </go:CommandHandler>
      </go:Diagram.CommandHandler>
    </go:Diagram>

You might not even need to set the Key, since adding a node data to a model will automatically result in a unique Key for it.

Oh, sorry Walter,
I meant the Binding of the Group and Ungroup Commands in the MenuItem of my sample.

And a second question:
The sample is from the intro document. What is a proper PrototypeGroup for this?

If you set CommandHandler.PrototypeGroup, either in XAML as I showed you or in code as you were trying, the Group command should automatically be enabled and it should work.

I suppose if you had set Node.Groupable to false or Diagram.AllowGroup to false, the command would be disabled anyway, but it seems unlikely that you have done this. Or if the model is not modifiable or if the diagram is readonly.

Thank you Walter,
it’s working now. The Problem was only the Binding of the Group- and UngroupCommand in the MenuItem, since MenuItem in ContextMenus are not in the Visual Tree and so somewhat tricky to bind. With the help of StackOverflow I solved this, and now it’s working like a charm.

If I want that the first click (on the group or a node in the group) will always select the group and then a seconde click (on the node this time) will select the node - how can I handle that?

Override ClcikSelectionTool?

I tried this:

public class CustomSelectionTool : ClickSelectingTool
{
    public override void DoMouseUp()
    {
        // group selected before mouseup
        var selectedGroup = Diagram.SelectedGroup;

        base.DoMouseUp();

        // check after mouseup
        var selectedPart = Diagram.SelectedPart;
        if (selectedPart != null)
        {
            if (selectedGroup == null || !Equals(selectedGroup, selectedPart.ContainingSubGraph))
            {
                if (selectedPart.ContainingSubGraph != null)
                {
                    selectedPart.IsSelected = false;
                    selectedPart.ContainingSubGraph.IsSelected = true;
                }
            }
        }
    }
}

Yes, override ClickSelectingTool.

I do not recall ever implementing an example with the exact policy that you describe, but later I can search for it. If I find anything useful I’ll post it.

You might find it better to override DiagramTool.StandardMouseSelect, rather than DoMouseUp, so that it is only selection policy that you override.

Ok

protected override void StandardMouseSelect()
{
    // group selected before mouseup
    var selectedGroup = Diagram.SelectedGroup;

    base.StandardMouseSelect();

    // check after mouseup
    var selectedPart = Diagram.SelectedPart;
    if (selectedPart != null)
    {
        if (selectedGroup == null || !Equals(selectedGroup, selectedPart.ContainingSubGraph))
        {
            if (selectedPart.ContainingSubGraph != null)
            {
                selectedPart.IsSelected = false;
                selectedPart.ContainingSubGraph.IsSelected = true;
            }
        }
    }
}

Hi Walter,
is it possible to use an object reference as the NodeKeyPath and GroupNodePath?

And is it possible to set the Group to IsHitTestVisible=False if my program is not in EditMode?

The type of the GraphLinksModel.NodeKeyPath property is a string because that property allows you to customize which property is used to hold the unique “key” for a node data object.

So are you really asking what types can be used as the NodeKey type parameter of GraphLinksModel<NodeType, NodeKey, PortKey, LinkType> ? Basically the NodeKey is used as the key type of several Dictionarys. If you are not going to use a value type as the NodeKey, you’ll need to make it act like one for the purposes of the model’s treating such objects as key values.

Regarding IsHitTestVisible, I don’t see why not.

You are right. I really want to know if I can use a reference as a NodeKey.

My GroupTemplate looks like the following:

<DataTemplate x:Key="GroupTemplate" DataType="{x:Type classes:Element}">
    <StackPanel go:Node.LocationElementName="GroupPanel" go:Part.SelectionAdorned="True">
        <Border BorderBrush="Gray" BorderThickness="1" CornerRadius="5" Background="Transparent" Margin="0">
            <go:GroupPanel x:Name="GroupPanel" 
                            IsHitTestVisible="{Binding Path=Part.Diagram.DataContext.EditMode}">
            </go:GroupPanel>
        </Border>
    </StackPanel>
</DataTemplate>

But if I’m not in EditMode I can still select the Group. I tried it on the StackPanel also, but this doesn’t work either.

Did you mean to bind IsHitTestVisible on the GroupPanel rather than on the outermost StackPanel?

Also, you asked about hit testing, not about selectability. After all, Control-A selects all Selectable Parts without any regard to mouse/finger position. You can bind go:Part.Selectable if you like.

Did you hear the bang? That was my hand when she slapped against my forehead ;-)

Sure Selectable is is right property - it’s much to warm in Germany, I can’t think anymore.

Hi Walter,
I found out that modifing the ClickSelectingTool is not all! If I start to drag a node that is in a group I want also to move the group instead of the node. If the node is selected than the user should be able to move it, but if it is not selected than the drag should not select the node but the group and drag the group instead.

Is this possible?

This isn’t really about “Group and ungroup” any more, is it? Could you start a new topic, please? (It isn’t obvious to me as an Administrator that there’s a way for me to change your reply into a new topic, and if you do it it will seem more natural anyway.)