1

So here's an issue that I'm having. I'm trying to use MouseDragElementBehavior in listbox. I was able to make it work when I was creating items in listbox directly, as in this example:

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.Items>
            <Border Width="20" Height="20">

                    <i:Interaction.Behaviors>
                        <ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
                    </i:Interaction.Behaviors>

                    <Rectangle Fill="Red"/>

                </Border>
        </ItemsControl.Items>

    </ItemsControl>

But as soon as I've started using DataTemplate, it stopped working.

    <ItemsControl Grid.Column="1" >

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.Items>
            Test item
        </ItemsControl.Items>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Width="20" Height="20">

                    <i:Interaction.Behaviors>
                        <ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
                    </i:Interaction.Behaviors>

                    <Rectangle Fill="Red"/>

                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

    </ItemsControl>

Any ideas as to why? I can't really figure out how a DataTemplate would affect MouseDragElementBehavior.

L.E.O
  • 1,069
  • 1
  • 13
  • 28

1 Answers1

0

The MouseDragElementBehavior acts upon the FrameworkElement you attach it to. In your case it is the Border element which will be contained by a ContentPresenter which is the container generated by the ItemsControl. You have set ConstrainToParentBounds="True" which will ensure the visual will not be displayed outside its container, in this case the ContentPresenter. There are a few options, some easy, one probably not worth undertaking (but I did to figure some stuff out).

  1. Set ConstrainToParentBounds="False". I am supposing that you don't want the Border to leave the ItemsControl so this probably won't suit.
  2. Set the ItemContainerStyle to a Style which sets the Template to and adds the interaction to a similarly configured ContentPresenter. The base implementation of the ItemsControl uses a vanilla ContentPresenter. A caveat here is that if you aren't using UI elements as items you will need to wrap the item in one using a custom items control (see this answer on setting the container style):
    <ItemsControl Grid.Column="1" >

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Control.Template">
                    <Setter.Value>
                        <ControlTemplate>
                            <ContentPresenter>

                                <i:Interaction.Behaviors>
                                    <ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
                                </i:Interaction.Behaviors>

                            </ContentPresenter>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style >
        </ItemsControl.ItemContainerStyle>

        <ItemsControl.Items>
            Test item
        </ItemsControl.Items>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Border Width="20" Height="20">

                    <Rectangle Fill="Red"/>

                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

    </ItemsControl>
  1. Attach the interaction using the ItemsControl.ItemContainerStyle. This is a little involved because the Interaction.Behaviors attached property only has a setter:
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="beh:AddCollectionsToSetter.Behaviors">
                <Setter.Value>
                    <beh:BehaviorCollection>
                        <ei:MouseDragElementBehavior ConstrainToParentBounds="True"/>
                    </beh:BehaviorCollection>
                </Setter.Value>
            </Setter>
        </Style>
    </ItemsControl.ItemContainerStyle>

For this I had to create a separate attached property AddCollectionsToSetter.Behaviors which is read/write and a BehaviorCollection that allows the interactions to be added to.

    public static class AddCollectionsToSetter
    {

        #region Behaviors Dependency Property (Attached)

        /// <summary>Gets the behaviours to add.</summary>
        public static BehaviorCollection GetBehaviors(DependencyObject obj)
        {
            return (BehaviorCollection)obj.GetValue(BehaviorsProperty);
        }

        /// <summary>Sets the behaviours to add.</summary>
        public static void SetBehaviors(DependencyObject obj, BehaviorCollection value)
        {
            obj.SetValue(AddCollectionsToSetter.BehaviorsProperty, value);
        }

        /// <summary>DependencyProperty backing store for <see cref="Behaviors"/>. Represents the behaviours to add.</summary>
        /// <remarks></remarks>
        public static readonly DependencyProperty BehaviorsProperty =
            DependencyProperty.RegisterAttached("Behaviors", typeof(BehaviorCollection), typeof(AddCollectionsToSetter), new PropertyMetadata(null, BehaviorsPropertyChanged));

        private static void BehaviorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var oldBehaviors = (BehaviorCollection)e.OldValue;
            var newBehaviors = (BehaviorCollection)e.NewValue;
            var interaction = Interaction.GetBehaviors(d);
            interaction.RemoveRange(oldBehaviors); // extension method, simple iterate and remove
            interaction.AddRange(newBehaviors.Clone()); // extension method, simple iterate and add
        }

        #endregion Behaviors Dependency Property (Attached)

    }

    public class BehaviorCollection : FreezableCollection<System.Windows.Interactivity.Behavior>
    {
        public BehaviorCollection()
            : base()
        {
        }
        public BehaviorCollection(int capacity)
            : base(capacity)
        {
        }
        public BehaviorCollection(IEnumerable<System.Windows.Interactivity.Behavior> behaviors)
            : base(behaviors)
        {
        }
    }
strongbutgood
  • 655
  • 7
  • 22