0

My goal is to display a simple Graph in a UWP application. The graph contains a list of vertices and edges. A Vertex has an absolute Position and should be displayed in the View correspondingly. An Edge contains two vertices and should be represented as a simple line between one vertex to the other. Additionally it would also be nice if those GraphItems (Vertex and Edge) were selectable in the view.

So far I've tried to use a ListBox with it's ItemsPanel set to a Canvas so that the items were selectable. I've created my own GraphDataTemplateSelector that is inherited from DataTemplateSelector for choosing whether a vertex or an edge should be drawn. My ViewModel returns the graph as an ObservableCollection of objects which is used as the ItemsSource of the ListBox. I've tried to position the elements with the ItemContainerStyle and binding the Canvas.Left and Canvas.Top properties. But according to this post the binding for Setter.Value isn't supported under UWP so I used the workaround with the RenderTransform described in the same post.

The problem with this approach is that the selection rectangle of the elements always stays at the top left corner of the canvas. As far as I understand if I'm selecting a Canvas as the ItemsPanel of the view it's children will be presented in a ContentPresenter. My guess is that this is caused due to the fact the RenderTransform is only applied to the DataTemplate and not to the ContentPresenter itself.

My question now is how can I solve this problem, so that the selection rectangle moves along with the elements? Maybe this isn't the best way for achieving this goal and there's a better solution for this? Thanks for your time and help!

My XAML-Code:

<Page.Resources>
    <DataTemplate x:DataType="models:VertexModel" x:Key="VertexTemplate">
            <StackPanel>
                <StackPanel.RenderTransform>
                    <TranslateTransform X="{x:Bind Position.X}" Y="{x:Bind Position.Y}"/>
                </StackPanel.RenderTransform>
                <Ellipse Fill="Red" Height="40" Width="40" StrokeThickness="2" Stroke="Black"/>
                <TextBlock Text="{x:Bind Name}" HorizontalAlignment="Center"/>
            </StackPanel>
        </DataTemplate>

    <DataTemplate x:DataType="models:EdgeModel" x:Key="EdgeTemplate">
        <Line X1="{x:Bind StartVertex.Position.X}" 
              Y1="{x:Bind StartVertex.Position.Y}" 
              X2="{x:Bind EndVertex.Position.X}" 
              Y2="{x:Bind EndVertex.Position.Y}" 
              Stroke="DarkGreen" StrokeThickness="2"/>
    </DataTemplate>

    <templateSelector:GraphDataTemplateSelector x:Key="GraphDataTemplateSelector"
                                                VertexTemplate="{StaticResource VertexTemplate}"
                                                EdgeTemplate="{StaticResource EdgeTemplate}"/>
</Page.Resources>

<ListBox ItemsSource="{Binding Graph}"
         ItemTemplateSelector="{StaticResource GraphDataTemplateSelector}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

My GraphDataTemplateSelector:

public class GraphDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate VertexTemplate { get; set; }
    public DataTemplate EdgeTemplate { get; set; }

    protected override DataTemplate SelectTemplateCore(object item)
    {
        if (item is VertexModel)
            return VertexTemplate;
        if (item is EdgeModel)
            return EdgeTemplate;

        return base.SelectTemplateCore(item);
    }

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
    {
        return SelectTemplateCore(item);
    }
}
Community
  • 1
  • 1
Brokdar
  • 15
  • 5

1 Answers1

0

My question now is how can I solve this problem, so that the selection rectangle moves along with the elements

The selection rectangle is actually the BorderBackground rectangle which is inside the ListViewItem control template at default. The transform you defined inside the DateTemplate is only applied to the ContentPresenter of the control template, not also to the BorderBackground rectangle. So you may need to define a new ListViewItem style for the ListView and set the transform for the BorderBackground rectangle with the position data.

As you known you may not able to bind data to the setter.value, but you should be able to bind the data to the elements inside the control template with Binding. Code sample as follows, define a new style in the page resource:

<Page.Resources>
...
 <Style x:Key="ListViewItemExpanded" TargetType="ListViewItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ListViewItem">
                    <Grid
                        x:Name="ContentBorder"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">                     
                        <Rectangle
                            x:Name="BorderBackground"
                            Fill="{ThemeResource SystemControlHighlightListAccentLowBrush}"
                            Opacity="0"
                            Control.IsTemplateFocusTarget="True"
                            IsHitTestVisible="False">
                            <Rectangle.RenderTransform>
                                <TranslateTransform X="{Binding Position.X}" Y="{Binding Position.Y}" />
                            </Rectangle.RenderTransform>
                        </Rectangle>
                        <Grid
                            x:Name="ContentPresenterGrid"
                            Margin="0,0,0,0"
                            Background="Transparent">
                            <Grid.RenderTransform>
                                <TranslateTransform x:Name="ContentPresenterTranslateTransform" />
                            </Grid.RenderTransform>
                            <ContentPresenter
                                x:Name="ContentPresenter"
                                Margin="{TemplateBinding Padding}"
                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                Content="{TemplateBinding Content}"
                                ContentTemplate="{TemplateBinding ContentTemplate}"
                                ContentTransitions="{TemplateBinding ContentTransitions}" />

                        </Grid> 
                        <TextBlock
                            x:Name="PlaceholderTextBlock"
                            Margin="{TemplateBinding Padding}"
                            Foreground="{x:Null}"
                            Opacity="0"
                            AutomationProperties.AccessibilityView="Raw"
                            IsHitTestVisible="False"
                            Text="Xg" />
                        <Rectangle
                            x:Name="PlaceholderRect"
                            Fill="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}"
                            Visibility="Collapsed" />
                        <Rectangle
                            x:Name="MultiArrangeOverlayBackground"
                            Grid.ColumnSpan="2"
                            Fill="{ThemeResource ListViewItemDragBackgroundThemeBrush}"
                            Opacity="0"
                            IsHitTestVisible="False" />
                        <Border
                            x:Name="MultiSelectSquare"
                            Width="20"
                            Height="20"
                            Margin="12,0,0,0"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            BorderBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                            BorderThickness="2"
                            Visibility="Collapsed">
                            <Border.Clip>
                                <RectangleGeometry Rect="0,0,20,20">
                                    <RectangleGeometry.Transform>
                                        <TranslateTransform x:Name="MultiSelectClipTransform" />
                                    </RectangleGeometry.Transform>
                                </RectangleGeometry>
                            </Border.Clip>
                            <Border.RenderTransform>
                                <TranslateTransform x:Name="MultiSelectCheckBoxTransform" />
                            </Border.RenderTransform>
                            <FontIcon
                                x:Name="MultiSelectCheck"
                                Foreground="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                                Opacity="0"
                                FontFamily="{ThemeResource SymbolThemeFontFamily}"
                                FontSize="16"
                                Glyph="&#xE73E;"
                                Visibility="Collapsed" />
                        </Border>
                        <TextBlock
                            x:Name="MultiArrangeOverlayText"
                            Grid.ColumnSpan="2"
                            Margin="18,9,0,0"
                            Foreground="{ThemeResource ListViewItemDragForegroundThemeBrush}"
                            Opacity="0"
                            FontFamily="{ThemeResource ContentControlThemeFontFamily}"
                            FontSize="26.667"
                            AutomationProperties.AccessibilityView="Raw"
                            IsHitTestVisible="False"
                            Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.DragItemsCount}"
                            TextTrimming="WordEllipsis"
                            TextWrapping="Wrap" />
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal">
                                    <Storyboard>
                                        <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="BorderBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderBackground" Storyboard.TargetProperty="Fill">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="BorderBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderBackground" Storyboard.TargetProperty="Fill">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListMediumBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerDownThemeAnimation TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Selected">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0:0:0"
                                            Storyboard.TargetName="MultiSelectCheck"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="BorderBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderBackground" Storyboard.TargetProperty="Fill">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListAccentLowBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="PointerOverSelected">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0:0:0"
                                            Storyboard.TargetName="MultiSelectCheck"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="BorderBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderBackground" Storyboard.TargetProperty="Fill">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListAccentMediumBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerUpThemeAnimation Storyboard.TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="PressedSelected">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0:0:0"
                                            Storyboard.TargetName="MultiSelectCheck"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="BorderBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderBackground" Storyboard.TargetProperty="Fill">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightListAccentHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltBaseHighBrush}" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <PointerDownThemeAnimation TargetName="ContentPresenter" />
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="DisabledStates">
                                <VisualState x:Name="Enabled" />
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="ContentBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="{ThemeResource ListViewItemDisabledThemeOpacity}" />
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>                                
                            ...
                            <VisualStateGroup x:Name="DataVirtualizationStates">
                                <VisualState x:Name="DataAvailable" />
                                <VisualState x:Name="DataPlaceholder">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextBlock" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderRect" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="ReorderHintStates">
                                <VisualState x:Name="NoReorderHint" />
                                <VisualState x:Name="BottomReorderHint">
                                    <Storyboard>
                                        <DragOverThemeAnimation
                                            Direction="Bottom"
                                            ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                                            TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="TopReorderHint">
                                    <Storyboard>
                                        <DragOverThemeAnimation
                                            Direction="Top"
                                            ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                                            TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="RightReorderHint">
                                    <Storyboard>
                                        <DragOverThemeAnimation
                                            Direction="Right"
                                            ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                                            TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="LeftReorderHint">
                                    <Storyboard>
                                        <DragOverThemeAnimation
                                            Direction="Left"
                                            ToOffset="{ThemeResource ListViewItemReorderHintThemeOffset}"
                                            TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.2" To="NoReorderHint" />
                                </VisualStateGroup.Transitions>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="DragStates">
                                <VisualState x:Name="NotDragging" />
                                <VisualState x:Name="Dragging">
                                    <Storyboard>
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="ContentBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="{ThemeResource ListViewItemDragThemeOpacity}" />
                                        <DragItemThemeAnimation TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="DraggingTarget">
                                    <Storyboard>
                                        <DropTargetItemThemeAnimation TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="MultipleDraggingPrimary">
                                    <Storyboard>                                           
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="MultiArrangeOverlayBackground"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="MultiArrangeOverlayText"
                                            Storyboard.TargetProperty="Opacity"
                                            To="1" />
                                        <DoubleAnimation
                                            Duration="0"
                                            Storyboard.TargetName="ContentBorder"
                                            Storyboard.TargetProperty="Opacity"
                                            To="{ThemeResource ListViewItemDragThemeOpacity}" />
                                        <FadeInThemeAnimation TargetName="MultiArrangeOverlayBackground" />
                                        <FadeInThemeAnimation TargetName="MultiArrangeOverlayText" />
                                        <DragItemThemeAnimation TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="MultipleDraggingSecondary">
                                    <Storyboard>
                                        <FadeOutThemeAnimation TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="DraggedPlaceholder">
                                    <Storyboard>
                                        <FadeOutThemeAnimation TargetName="ContentBorder" />
                                    </Storyboard>
                                </VisualState>
                                <VisualStateGroup.Transitions>
                                    <VisualTransition GeneratedDuration="0:0:0.2" To="NotDragging" />
                                </VisualStateGroup.Transitions>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>   
</Page.Resources> 

And set the style to the ListViewItem.

<ListView
    ItemContainerStyle="{StaticResource ListViewItemExpanded}"
    ItemTemplateSelector="{StaticResource GraphDataTemplateSelector}"
    ItemsSource="{x:Bind Graph1}">
    <ListView.ItemsPanel>
...
</ListView>

And the result:

enter image description here

You should be able just set transform to the ContentBorder element, in that case you will not set transform inside Datetemplate again.

Sunteen Wu
  • 10,509
  • 1
  • 10
  • 21
  • Thanks for your answer. This solutions works quite well just for the fact that I applied the `RenderTransform` to the base grid and not to the `BorderBackground Rectangle`. With this change it does exactly what it needs to do. Thank you very much. – Brokdar Mar 25 '17 at 14:59