Rubberband doesn't select node with custom control

Hi,
I’ve made a custom control with the following controltemplate and all painting in OnRender.
Now my problem is, that I can’t select these nodes with the Rubberband. If I clickselect the node all works well, but if I use the Rubberband I can’t select the nodes where the custom control is shown in.

I have no idea how to debug this - any hint?

Here the controltemplate:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Andy.Controls">
    <Style TargetType="{x:Type local:PegelControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:PegelControl}">
                    <Border x:Name="LayoutRoot"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">

                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

What is your Node DataTemplate?

Hi Walter I have Data Templates with Buttons in it and Data Templates with Slider in it - they are selected by the rubberband. But the DataTemplate with my CustomControl (inherited from RangeBase) gets not selected by the rubberband.

Here’s the DataTemplate:

<DataTemplate x:Key="LevelIndicatorTemplate" DataType="{x:Type classes:Element}">
        <Grid Style="{StaticResource ElementGridStyle}">
            <andycontrols:PegelControl x:Name="GaugeControl"
                                      BorderBrush="{Binding Path=Data.BorderBrush}"
                                      Background="{Binding Path=Data.BackgroundBrush}"
                                      Visibility="{Binding Path=Data.NoDisplay, Converter={StaticResource BooleanToVisibilityConverterInverse}}"
                                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                                      FontFamily="{Binding Path=Data.FontFamily, TargetNullValue=Segoe}"
                                      FontSize="{Binding Path=Data.FontSize}"
                                      IsHitTestVisible="{Binding Path=Part.Diagram.DataContext.EditMode, Converter={StaticResource BooleanNegationConverter}}"
                                      Minimum="{Binding Path=Data.Metadata.MINVALUE}"
                                      Maximum="{Binding Path=Data.Metadata.MAXVALUE}"
                                      TickPlacement="{Binding Path=Data.TickPlacement}"
                                      Multiplier="{Binding Path=Data.Multiplier}"
                                      Value="{Binding Path=Data.Value, Mode=OneWay}"
                                      Ticks="{Binding Path=Data.Ticks}"
                                      ChangeColorAtCollection="{Binding Path=Data.ChangeColorAtCollection}"
                                      ShowTextOnTicks="{Binding Path=Data.ShowTextOnTicks}"
                                      ShowSingleTicksFromTopCount="{Binding Path=Data.ShowSingleTicksFromTopCount}"
                                      ScaleType="{Binding Path=Data.ScaleType}" />
            <ContentControl Template="{StaticResource PlaceholderTemplate}" />
            <ToggleButton x:Name="PinnedButton" Style="{StaticResource PinnedToggleButton}" Opacity="0" />
            <ToggleButton x:Name="QuickZoomButton" Style="{StaticResource QuickZoomToggleButton}" Opacity="0" />
        </Grid>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding Path=Data.IsSelected}" Value="True">
                <Setter TargetName="GaugeControl" Property="Background" Value="{Binding Path=Data.BackgroundBrushSelected}" />
                <Setter TargetName="GaugeControl" Property="BorderBrush" Value="{Binding Path=Data.BorderBrushSelected}" />
                <Setter TargetName="GaugeControl" Property="Foreground" Value="{Binding Path=Data.ForegroundBrushSelected}" />
            </DataTrigger>
            <Trigger Property="Button.IsMouseOver" Value="True">
                <Setter TargetName="PinnedButton" Property="Opacity" Value="1" />
                <Setter TargetName="QuickZoomButton" Property="Opacity" Value="1" />
            </Trigger>
        </DataTemplate.Triggers>
    </DataTemplate>

In the Button DataTemplate PegelControl is exchanged by Button (and of course some other properties - Button has no TickPlacement and so on), but the rest is identical.

My guess is that your control doesn’t have the actual size that you think it is. What are its ActualWidth and ActualHeight? Also, is the Node.Bounds what you would think it should be?

Hi Walter,
you are right - the DesiredSize of the Border (which is the rootelement of the ControlTemplate) is 0; 0.
On the Node with the Slider it is 40; 20.

But I have no Idea how to set the DesiredSize.

No, that’s not the reason!
I have changed the BorderSize so that DesiredSize has another value, but the rubberband still doesn’t select.

I try now to inherit from FrameworkElement and do all stuff by myself, maybe that helps.

If I remove the complete OnRender Method in my custom control, then the Rubberband can select it.
I can’t see anything but it’s selected ;-)

As long as the measurements are correct for all elements, I don’t see how the problem can arise.

Try drawing a very simple small figure in your OnRender method.

This is working! I draw two simple lines in a X-Form.

But normaly I just draw FormattedTextElements, Lines and Rectangles with the drawingcontext.

Hi Walter,
I get in big trouble here.
If I draw the two lines all is working well. As soon as I change the two lines to a simple Rect (drawed in OnRender with dc.DrawRectangle the selection with the Rubberband doesn’t work.

What can I do? What is wrong?

It’s even getting better!
Please see the attached video (video)
My Control is getting selected if I don’t draw any visible rect in OnRender.
In the video you can see my control on the left with no rect in the middle. It get’s selected by the rubberband.
Now I change a value in the control what leads to draw a rect in the middle and it can’t be selected by the rubberband, just by click.
I really have no Idea what to do.

Remark: If I change the DrawRectangle to DrawLine and give the Pen the Width from the rectangle it’s also working - it must have something to do with the Rect!

That’s very odd. I can look into it.

In the meantime, does your control draw a background, or does one of its containing panels have a background? Try setting Background=“White” or Background=“Transparent”.

Hi Walter,
it seems that it is the Pen used to draw the rectangle. If it has a Thickness of 0.0 the rubberband can’t select. If it has any value greater than 0.0, or give it a null pen, it works.

OK, so you have a work-around.

I still think setting the Background will cause it not to try to look inside your control.

Hi Walter,
I modified another CustomControl I found on the Internet to help you reproduce the problem. If you put this in a template for a node and change the Pens Thickness in OnRender to 0.0 you should see the effect.

using System;
using System.Windows;
using System.Windows.Media;

namespace My.Controls
{
    public class MyElement : FrameworkElement
    {
        private static FrameworkPropertyMetadata childMetadata =
                   new FrameworkPropertyMetadata(null,
                            FrameworkPropertyMetadataOptions.AffectsParentArrange,
                            OnChildChanged);

        public static void OnChildChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            MyElement thisElement = obj as MyElement;
            if (thisElement == null)
                throw new Exception("Child property must be attached to MyElement");

            // Remove old one
            Visual oldChild = e.OldValue as Visual;
            if (oldChild != null)
            {
                thisElement.RemoveVisualChild(oldChild);
                thisElement.RemoveLogicalChild(oldChild);
            }

            // Attach new one
            Visual newChild = e.NewValue as Visual;
            if (newChild != null)
            {
                thisElement.AddVisualChild(newChild);
                thisElement.AddLogicalChild(newChild);
            }
        }

        public static readonly DependencyProperty ChildProperty =
            DependencyProperty.RegisterAttached("Child", typeof(UIElement),
                typeof(MyElement), childMetadata);

        public static void SetChild(DependencyObject depObj, UIElement value)
        {
            depObj.SetValue(ChildProperty, value);
        }

        protected override int VisualChildrenCount
        {
            get
            {
                UIElement childElement = (UIElement)GetValue(ChildProperty);
                return childElement != null ? 1 : 0;
            }
        }

        protected override Visual GetVisualChild(int index)
        {
            // (ignoring index)
            return (UIElement)GetValue(ChildProperty);
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            UIElement childElement = (UIElement)GetValue(ChildProperty);
            if (childElement != null)
                childElement.Measure(availableSize);

            // "X" and child both use all of the available space
            return availableSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            UIElement childElement = (UIElement)GetValue(ChildProperty);
            if (childElement != null)
                childElement.Arrange(new Rect(new Point(0.0, 0.0), finalSize));

            return finalSize;
        }

        // Render a big "X"
        protected override void OnRender(DrawingContext dc)
        {
            //dc.DrawLine(new Pen(Brushes.Blue, 2.0),
            //    new Point(0.0, 0.0),
            //    new Point(ActualWidth, ActualHeight));
            //dc.DrawLine(new Pen(Brushes.Green, 2.0),
            //    new Point(ActualWidth, 0.0),
            //    new Point(0.0, ActualHeight));

            dc.DrawRectangle(Brushes.Blue, new Pen(Brushes.Green, 0.01), new Rect(new Point(0,0), new Point(ActualWidth, ActualHeight)));
        }
    }
}

Thanks.

Could you confirm that setting the Background of the control’s containing Panel solves the problem in general for you?

That MyElement control that you quoted is incorrectly implemented, because its MeasureOverride can return Infinity when the availableSize is Infinity (either or both Width and Height).

Still, when I use it in my node DataTemplate with a defined size:

<local:MyElement Width="60" Height="30" />

I find that the DragSelectingTool works well on such nodes.

It’s just a copy out of the internet.
Did you try to set the Pen Thickness in OnRender to 0.0?

Oops, no I didn’t. Thanks – I can reproduce the problem now. And I can see that setting the Panel.Background doesn’t help either.

Thank you - I already thought I was stupid :wink: