cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Troubleshooting Drag and Drop?

28 Dec 2020, 14:21

Do you have any suggestions for troubleshooting Drag and Drop? I'm using version 2.

Function are being called on the view model, the behavior, and the adorner. But nothing visually changes.

I simplified the inventory example down to a very simplified form and using that with a hard coded image rect for testing.
 
User avatar
sfernandez
Site Admin
Posts: 3014
Joined: 22 Dec 2011, 19:20

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 12:22

The drag and drop behaviors provide the connection between the drag events and the commands you expose in your view model. And DragAdornerBehavior just notifies about the mouse position when dragging. But none of the behaviors will directly change anything related to the rendering, that would be the consequence of changing properties in the view model when drag commands are executed and the UI bindings to the view model properties.

To better understand the process of drag and drop you should probably start with a simple example with just 2 slots and 1 item you can move between them.
The view model will expose 3 commands for StartDrag, EndDrag and DropItem, and also properties for Slot1, Slot2 and DraggedItem.
You should set the DragItemBehavior and DropItemBehavior on the slot UI controls. And DragAdornerBehavior should be set on the root of your UI to receive all mouse events when dragging.
When the drag commands are called (the order of execution will be: StartDrag, DropItem and EndDrag, receiving as command parameter the DataContext of the control where the drag/drop behaviors are set) you should update the view model properties accordingly, and UI bindings will do the rest.

For example, if you initially have an item in slot 1, and start dragging from there, you should set Slot1 to null, DraggingItem to the item. When dropping the item in slot 2 you should set DraggingItem to null and Slot2 to the item.

Please let me know if you need further help with any specific part.
 
cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 14:18

I have reduced the example to the minimal set of operations to get drag working, and then modified my code to match the example.
All of that seems to be working. I just cannot get the Drag Panel to display or move. The Drag X and Y are getting set and the DraggedItem contains a valid rectangle and is being accessed, but nothing happens with the Drag Panel.
 
User avatar
sfernandez
Site Admin
Posts: 3014
Joined: 22 Dec 2011, 19:20

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 17:49

In our inventory sample we have a Grid filling the entire screen named "DragPanel", inside that panel we put a ContentControl with its Content property bound to the DraggedItem property of the view model, so when dragging starts and view model sets that property to the corresponding item, the item will be rendered on top of everything. The position of that dragging item is specified by binding to the DraggedItemX and DraggedItemY properties of the DragAdornerBehavior set on the root element.

If you attach here your xaml and viewmodel I can take a look to see what could be failing in your setup.
 
cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 21:50

Here is my XAML.. it's a Page instead of a Window.
<Page
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
	xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="clr-namespace:SimLabs"
	x:Name="Root"
	x:Class="SimLabs.StageEdit"
	Title="Stage Edit" Width="1280" Height="720">
	<Page.Resources>
		<local:CourseDataObject x:Name="CourseData"/>
	</Page.Resources>
	<i:Interaction.Behaviors>
		<local:DragAdornerBehavior x:Name="DragAdorner"/>
	</i:Interaction.Behaviors>
	<StackPanel>		
		<WrapPanel HorizontalAlignment="Center" Margin="0,4,0,6">
			<Button x:Name="DeleteButton" Click="OnDeleteClick">Delete target</Button>
			<Button x:Name="EditTargetButton" Click="OnEditTargetClick">Edit target</Button>
			<Button x:Name="BackButton" Click="OnBackClick">Back</Button>
			<Button x:Name="CenterHorizontallyButton" Width="24" Height="24" Click="OnCenterHorizontallyClick" Padding="0">
				<Image Source="Icons\CenterXIcon.png"/>
			</Button>
			<Button x:Name="CenterVerticallyButton" Width="24" Height="24" Click="OnCenterVerticallyClick" Padding="0">
				<Image Source="Icons\CenterYIcon.png"/>
			</Button>
			<Button x:Name="LeftEdgeButton" Width="24" Height="24" Click="OnLeftEdgeClick" Padding="0">
				<Image Source="Icons\LeftEdgeIcon.png"/>
			</Button>
			<Button x:Name="RightEdgeButton" Width="24" Height="24" Click="OnRightEdgeClick" Padding="0">
				<Image Source="Icons\RightEdgeIcon.png"/>
			</Button>
			<Button x:Name="TopEdgeButton" Width="24" Height="24" Click="OnTopEdgeClick" Padding="0">
				<Image Source="Icons\TopEdgeIcon.png"/>
			</Button>
			<Button x:Name="BottomEdgeButton" Width="24" Height="24" Click="OnBottomEdgeClick" Padding="0">
				<Image Source="Icons\BottomEdgeIcon.png"/>
			</Button>
		</WrapPanel>
		<WrapPanel HorizontalAlignment="Center" Margin="0,4,0,6">
			<TextBlock HorizontalAlignment="Center" Foreground="#FFFFFF">Position:</TextBlock>
			<TextBox x:Name="PositionX" Width="100" TextChanged="OnXChange"/>
			<TextBox x:Name="PositionY" Width="100" TextChanged="OnYChange"/>
			<TextBlock HorizontalAlignment="Center" Foreground="#FFFFFF">Distance:</TextBlock>
			<TextBox x:Name="Distance" Width="100" Margin="10,0,0,0" TextChanged="OnDistanceChange"/>
			<ComboBox x:Name="Units" Width="100" Margin="0,4,0,4" SelectionChanged="OnUnitsChange">
				<ComboBoxItem>Inches</ComboBoxItem>
				<ComboBoxItem>Feet</ComboBoxItem>
				<ComboBoxItem>Yards</ComboBoxItem>
				<ComboBoxItem>Centimeter</ComboBoxItem>
				<ComboBoxItem>Meter</ComboBoxItem>
			</ComboBox>
		</WrapPanel>
		<WrapPanel HorizontalAlignment="Center" Margin="0,4,0,6" Width="1280" Height="512">
			<TabControl x:Name="Resources" Width="300" Height="512">
				<TabItem Header="Targets">
					<Grid>
						<ListBox x:Name="TargetImages" Width="300" ItemsSource="{Binding Children, Source={StaticResource CourseData}, Path=TargetImages}">
							<ListBox.ItemTemplate>
								<DataTemplate>
									<Image Source="{Binding Path}" Margin="0" Width="100" Height="166">
										<i:Interaction.Behaviors>
											<local:TargetDragBehavior StartDragCommand="{Binding Source={StaticResource CourseData}, Path=StartTargetDrag, ElementName=Root}" EndDragCommand="{Binding Source={StaticResource CourseData}, Path=EndTargetDrag, ElementName=Root}"/>
										</i:Interaction.Behaviors>
									</Image>
								</DataTemplate>
							</ListBox.ItemTemplate>
						</ListBox>
						<Grid x:Name="DragPanel">
							<Grid.ColumnDefinitions>
								<ColumnDefinition Width="0.05*"/>
								<ColumnDefinition Width="0.95*"/>
							</Grid.ColumnDefinitions>
							<Decorator x:Name="DragItemRef"/>
							<ContentControl Content="{Binding Source={StaticResource CourseData}, Path=DraggedItem}" HorizontalAlignment="Left" VerticalAlignment="Top" 
								Width="{Binding ActualWidth, ElementName=DragItemRef}" Height="{Binding ActualWidth, ElementName=DragItemRef}" Style="{StaticResource Style.DraggedItem}">
								<ContentControl.RenderTransform>
									<TranslateTransform X="{Binding DraggedItemX, ElementName=DragAdorner}" Y="{Binding DraggedItemY, ElementName=DragAdorner}"/>
								</ContentControl.RenderTransform>
							</ContentControl>
						</Grid>
					</Grid>
				</TabItem>
				<TabItem Header="Backgrounds">
					<ListBox x:Name="BackgroundImages" Width="300" ItemsSource="{Binding Children, Source={StaticResource CourseData}, Path=BackgroundImages}">
						<ListBox.ItemTemplate>
							<DataTemplate>
								<Image Source="{Binding Path}" Margin="0" Width="200" Height="113">
									<i:Interaction.Behaviors>
										<local:BackgroundDragBehavior StartDragCommand="{Binding Source={StaticResource CourseData}, Path=StartBackgroundDrag, ElementName=Root}" EndDragCommand="{Binding Source={StaticResource CourseData}, Path=EndBackgroundDrag, ElementName=Root}"/>
									</i:Interaction.Behaviors>
								</Image>
							</DataTemplate>
						</ListBox.ItemTemplate>
					</ListBox>
				</TabItem>
				<TabItem Header="Objects">
					<ListBox x:Name="ObjectImages" Width="300" ItemsSource="{Binding Children, Source={StaticResource CourseData}, Path=ObjectImages}">
						<ListBox.ItemTemplate>
							<DataTemplate>
								<Image Source="{Binding Path}" Margin="0" Width="280" Height="166">
									<i:Interaction.Behaviors>
										<local:ObjectDragBehavior StartDragCommand="{Binding Source={StaticResource CourseData}, Path=StartObjectDrag, ElementName=Root}" EndDragCommand="{Binding Source={StaticResource CourseData}, Path=EndObjectDrag, ElementName=Root}"/>
									</i:Interaction.Behaviors>
								</Image>
							</DataTemplate>
						</ListBox.ItemTemplate>
					</ListBox>
				</TabItem>
			</TabControl>
			<local:SceneView x:Name="SceneView" Stretch="Uniform" Focusable="True">
				<local:SceneView.ContextMenu>
				   <ContextMenu>
						<MenuItem x:Name="CenterHorizontallyMenu" Header="Center horizontally" Click="OnCenterHorizontallyMenu"/>
						<MenuItem x:Name="CenterVerticallyMenu" Header="Center vertically" Click="OnCenterVerticallyMenu"/>
						<MenuItem x:Name="MoveToLeftMenu" Header="Move to left edge" Click="OnLeftEdgeMenu"/>
						<MenuItem x:Name="MoveToRightMenu" Header="Move to right edge" Click="OnRightEdgeMenu"/>
						<MenuItem x:Name="MoveToTopMenu" Header="Move to top edge" Click="OnTopEdgeMenu"/>
						<MenuItem x:Name="MoveToBottomMenu" Header="Move to bottom edge" Click="OnBottomEdgeMenu"/>
						<Separator x:Name="SeparatorMenu"/>
						<MenuItem x:Name="EditTargetMenu" Header="Edit target" Click="OnEditTargetMenu"/>
						<MenuItem x:Name="DuplicateTargetMenu" Header="Duplicate target" Click="OnDuplicateTargetMenu"/>
						<MenuItem x:Name="DeleteTargetMenu" Header="Delete target" Click="OnDeleteTargetMenu"/>
				   </ContextMenu>
				</local:SceneView.ContextMenu>
				<i:Interaction.Behaviors>
					<local:SceneDropBehavior/>
				</i:Interaction.Behaviors>
			</local:SceneView>
			<StackPanel VerticalAlignment="Top" Width="32">
				<Button x:Name="UpButton" Width="16" Height="16" Click="OnUpButton" Padding="0">
					<Image Source="Icons\MoveUpIcon.png"/>
				</Button>
				<Button x:Name="DownButton" Width="16" Height="16" Click="OnDownButton" Padding="0">
					<Image Source="Icons\MoveDownIcon.png"/>
				</Button>
				<Button x:Name="DuplicateButton" Width="16" Height="16" Click="OnDuplicateButton" Padding="0">
					<Image Source="Icons\DuplicateIcon.png"/>
				</Button>
				<Button x:Name="DeleteButton" Width="16" Height="16" Click="OnDeleteButton" Padding="0">
					<Image Source="Icons\DeleteIcon.png"/>
				</Button>
			</StackPanel>
		</WrapPanel>
	</StackPanel>
</Page>
 
cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 21:58

Here is relevent part of the View Model header:
#pragma once

#include <NsApp/DelegateCommand.h>
#include <NsCore/Noesis.h>
#include <NsCore/ReflectionImplement.h>
#include <NsDrawing/Rect.h>
#include <NsGui/ImageSource.h>
#include <NsGui/ObservableCollection.h>
#include <NsGui/ResourceDictionary.h>

#include "BackgroundImageList.h"
#include "Macros.h"
#include "ObjectImageList.h"
#include "SoundList.h"
#include "TargetImageList.h"

using namespace Noesis;

NAMESPACE(SimLabs)

class Item : public Noesis::BaseComponent
{
    public:
		Noesis::Rect	m_icon;	// Icon rect

		Item()
		{
			m_icon.x		= 0;
			m_icon.y		= 0;
			m_icon.width	= 62;
			m_icon.height	= 62;
		}

	private:
		NS_DECLARE_REFLECTION(Item, BaseComponent)
};

class NotifierBase: public Noesis::BaseComponent, public Noesis::INotifyPropertyChanged
{
	public:
		SUPER(BaseComponent)

		Noesis::PropertyChangedEventHandler& PropertyChanged() final;

		NS_IMPLEMENT_INTERFACE_FIXUP

	protected:
		void OnPropertyChanged(const char* _propertyName);

	private:
		Noesis::PropertyChangedEventHandler	m_changed;

		NS_DECLARE_REFLECTION(NotifierBase, BaseComponent)
};

class CourseDataObject : public NotifierBase
{
	public:
		SUPER(NotifierBase)
		
		// Constructor
		CourseDataObject();
		
		// Destructor
		~CourseDataObject();

		// Get start target drag
		NoesisApp::DelegateCommand* GetStartTargetDrag() const;

		// Get end target drag
		NoesisApp::DelegateCommand* GetEndTargetDrag() const;

		// Get dragged item
		Item* GetDraggedItem() const;

		// Set dragged item info
		void setDraggedItemInfo(Noesis::Rect _imageRect);

	private:
		NS_IMPLEMENT_INLINE_REFLECTION(CourseDataObject, NotifierBase)
		{
			NsMeta<TypeId>("SimLabs.CourseDataObject");
	        
			NsProp("StartTargetDrag", &CourseDataObject::GetStartTargetDrag);
			NsProp("EndTargetDrag", &CourseDataObject::GetEndTargetDrag);
		    NsProp("DraggedItem", &CourseDataObject::GetDraggedItem);
		}

		Noesis::Ptr<Item>									m_pDraggedItem;			// Dragged item
		Noesis::Ptr<NoesisApp::DelegateCommand>				m_pStartTargetDrag;		// Start target drag
		Noesis::Ptr<NoesisApp::DelegateCommand>				m_pEndTargetDrag;		// End target drag

		// On can start target drag
		bool OnCanStartTargetDrag(Noesis::BaseComponent* _pParam);
		
		// On start target drag
		void OnStartTargetDrag(Noesis::BaseComponent* _pParam);
		
		// On end target drag
		void OnEndTargetDrag(Noesis::BaseComponent* _pParam);
};

ENDNAMESPACE
And the code:
#include <NsGui/INotifyPropertyChanged.h>
#include <NsGui/IntegrationAPI.h>

#include "CourseData.h"
#include "Log.h"
#include "System.h"

using namespace Noesis;

NAMESPACE(SimLabs)

PropertyChangedEventHandler& NotifierBase::PropertyChanged()
{
	return	m_changed;
}

void NotifierBase::OnPropertyChanged(const char* _propertyName)
{
	if (false == m_changed.Empty())
	{
		NsSymbol	propId(_propertyName);

		m_changed(this, PropertyChangedEventArgs(propId));
	}
}

CourseDataObject::CourseDataObject()
{
	m_pDraggedItem			= *new Item();
	m_pStartTargetDrag		= MakePtr<DelegateCommand>(MakeDelegate(this, &CourseDataObject::OnCanStartTargetDrag), MakeDelegate(this, &CourseDataObject::OnStartTargetDrag));
	m_pEndTargetDrag		= MakePtr<DelegateCommand>(MakeDelegate(this,&CourseDataObject::OnEndTargetDrag));
}

CourseDataObject::~CourseDataObject()
{
}

NoesisApp::DelegateCommand* CourseDataObject::GetStartTargetDrag() const
{
	return	m_pStartTargetDrag;
}

NoesisApp::DelegateCommand* CourseDataObject::GetEndTargetDrag() const
{
	return	m_pEndTargetDrag;
}

Item* CourseDataObject::GetDraggedItem() const
{
	return	m_pDraggedItem;
}

void CourseDataObject::setDraggedItemInfo(Noesis::Rect _imageRect)
{
	if (m_pDraggedItem != nullptr)
	{
		m_pDraggedItem->m_icon	= _imageRect;
	}
}

bool CourseDataObject::OnCanStartTargetDrag(Noesis::BaseComponent* _pParam)
{
	return	true;
}
		
void CourseDataObject::OnStartTargetDrag(Noesis::BaseComponent* _pParam)
{
	setDraggedItemInfo(Noesis::Rect(0.0f, 0.0f, 62.0f, 62.0f));

	OnPropertyChanged("DraggedItem");
}
		
void CourseDataObject::OnEndTargetDrag(Noesis::BaseComponent* _pParam)
{
	OnPropertyChanged("DraggedItem");
}

NS_IMPLEMENT_REFLECTION(NotifierBase)
{
	NsImpl<INotifyPropertyChanged>();
}

NS_IMPLEMENT_REFLECTION(Item)
{
	NsProp("Icon", &Item::m_icon);
}

ENDNAMESPACE
 
cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Re: Troubleshooting Drag and Drop?

29 Dec 2020, 22:05

The Resources info has been split into different parts:

I'm using the default InventoryAtlas,png for testing. It's located in an Image folder under the folder containing the UI. I'm also hard coding the image atlas rect to 0, 0, 62, 62 for testing.

Here's the template and Style info:
<ResourceDictionary
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="clr-namespace:SimLabs">
    <ControlTemplate x:Key="Template.DraggedItem" TargetType="{x:Type ContentControl}">
		<Rectangle Width="62" Height="62" Margin="0">
			<Rectangle.RenderTransform>
				<TransformGroup>
					<TranslateTransform X="-31" Y="-31"/>
				</TransformGroup>
			</Rectangle.RenderTransform>
			<Rectangle.Fill>
				<ImageBrush ImageSource="Images/InventoryAtlas.png" Stretch="Fill" ViewboxUnits="Absolute" Viewbox="{Binding Content.Icon, RelativeSource={RelativeSource TemplatedParent}}"/>
			</Rectangle.Fill>
		</Rectangle>
        <ControlTemplate.Triggers>
            <Trigger Property="Content" Value="{x:Null}">
                <Setter Property="Visibility" Value="Collapsed"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
    <Style x:Key="Style.DraggedItem" TargetType="{x:Type ContentControl}">
        <Setter Property="Template" Value="{StaticResource Template.DraggedItem}"/>
    </Style>
</ResourceDictionary>
Here's the DragBehavior:
#pragma once

#include "DragBehavior.h"
#include "Macros.h"

NAMESPACE(SimLabs)

class TargetDragBehavior : public DragBehavior
{
	public:
		SUPER(DragBehavior)

		static const Noesis::DependencyProperty*	DragStartOffsetProperty;
		static const Noesis::DependencyProperty*	StartDragCommandProperty;
		static const Noesis::DependencyProperty*	EndDragCommandProperty;

		// Constructor
		TargetDragBehavior();

		// Destructor
		~TargetDragBehavior();

		// Get drag start offset
		const Noesis::Point& GetDragStartOffset() const;

		// Set drag start offset
		void SetDragStartOffset(const Noesis::Point& _offset);

		// Get start drag command
		Noesis::BaseCommand* GetStartDragCommand() const;

		// Set start drag command
		void SetStartDragCommand(Noesis::BaseCommand* _pCommand);

		// Get end drag drag command
		Noesis::BaseCommand* GetEndDragCommand() const;

		// Set end drag drag command
		void SetEndDragCommand(Noesis::BaseCommand* _pCommand);

	protected:
		// Create instance core
		Noesis::Ptr<Freezable> CreateInstanceCore() const override;
		
		// On attached
		void OnAttached() override;
		
		// On detached
		void OnDetaching() override;
		
		// On give feedback
		void OnGiveFeedback(Noesis::BaseComponent* _pSender, const Noesis::GiveFeedbackEventArgs& _event);
		
		// On mouse down
		void OnMouseDown(Noesis::BaseComponent* _pSender, const Noesis::MouseButtonEventArgs& _event);

		// On mouse up
		void OnMouseUp(Noesis::BaseComponent* _pSender, const Noesis::MouseButtonEventArgs& _event);

		// On mouse move
		void OnMouseMove(Noesis::BaseComponent* _pSender, const Noesis::MouseEventArgs& _event);

	private:
		NS_DECLARE_REFLECTION(TargetDragBehavior, Behavior)
};

ENDNAMESPACE
And the code:
#include <NsCore/Delegate.h>
#include <NsDrawing/Rect.h>
#include <NsGui/BaseCommand.h>
#include <NsGui/DragDrop.h>
#include <NsGui/PropertyMetadata.h>
#include <NsGui/UIElementData.h>

#include "System.h"
#include "TargetDragBehavior.h"

using namespace Noesis;

NAMESPACE(SimLabs)

TargetDragBehavior::TargetDragBehavior()
{
}

TargetDragBehavior::~TargetDragBehavior()
{
}

const Noesis::Point& TargetDragBehavior::GetDragStartOffset() const
{
	return	GetValue<Point>(DragStartOffsetProperty);
}

void TargetDragBehavior::SetDragStartOffset(const Noesis::Point& _offset)
{
	SetValue<Point>(DragStartOffsetProperty, _offset);
}

Noesis::BaseCommand* TargetDragBehavior::GetStartDragCommand() const
{
	return	GetValue<Ptr<BaseCommand>>(StartDragCommandProperty);
}

void TargetDragBehavior::SetStartDragCommand(Noesis::BaseCommand* _pCommand)
{
	SetValue<Ptr<BaseCommand>>(StartDragCommandProperty, _pCommand);
}

Noesis::BaseCommand* TargetDragBehavior::GetEndDragCommand() const
{
	return	GetValue<Ptr<BaseCommand>>(EndDragCommandProperty);
}

void TargetDragBehavior::SetEndDragCommand(Noesis::BaseCommand* _pCommand)
{
	SetValue<Ptr<BaseCommand>>(EndDragCommandProperty, _pCommand);
}

Noesis::Ptr<Noesis::Freezable> TargetDragBehavior::CreateInstanceCore() const
{
	return	*new TargetDragBehavior();
}
		
void TargetDragBehavior::OnAttached()
{
	ParentClass::OnAttached();

	FrameworkElement*	pElement	= GetAssociatedObject();

	pElement->GiveFeedback()				+= MakeDelegate(this, &TargetDragBehavior::OnGiveFeedback);
	pElement->PreviewMouseLeftButtonDown()	+= MakeDelegate(this, &TargetDragBehavior::OnMouseDown);
	pElement->PreviewMouseLeftButtonUp()	+= MakeDelegate(this, &TargetDragBehavior::OnMouseUp);
	pElement->PreviewMouseMove()			+= MakeDelegate(this, &TargetDragBehavior::OnMouseMove);
}
		
void TargetDragBehavior::OnDetaching()
{
	FrameworkElement*	pElement	= GetAssociatedObject();

	pElement->GiveFeedback()				-= MakeDelegate(this, &TargetDragBehavior::OnGiveFeedback);
	pElement->PreviewMouseLeftButtonDown()	-= MakeDelegate(this, &TargetDragBehavior::OnMouseDown);
	pElement->PreviewMouseLeftButtonUp()	-= MakeDelegate(this, &TargetDragBehavior::OnMouseUp);
	pElement->PreviewMouseMove()			-= MakeDelegate(this, &TargetDragBehavior::OnMouseMove);

	ParentClass::OnDetaching();
}


void TargetDragBehavior::OnGiveFeedback(Noesis::BaseComponent* _pSender, const Noesis::GiveFeedbackEventArgs& _event)
{
	_event.useDefaultCursors	= false;
	_event.handled				= true;
}

void TargetDragBehavior::OnMouseDown(Noesis::BaseComponent* _pSender, const Noesis::MouseButtonEventArgs& _event)
{
	m_bMouseClicked	= true;

	SimLabs::CourseDataObject*	pCourseData	= System::getCourseData();

	if (pCourseData != NULL)
	{
		Noesis::Image*	pImage	= reinterpret_cast<Noesis::Image*>(_pSender);

		if (pImage != NULL)
		{
			pCourseData->setDraggedItemInfo(Noesis::Rect(0.0f, 0.0f, 62.0f, 62.0f));
		}
	}
}

void TargetDragBehavior::OnMouseUp(Noesis::BaseComponent* _pSender, const Noesis::MouseButtonEventArgs& _event)
{
	m_bMouseClicked	= false;
}

void TargetDragBehavior::OnMouseMove(Noesis::BaseComponent* _pSender, const Noesis::MouseEventArgs& _event)
{
	if (true == m_bMouseClicked)
	{
		m_bMouseClicked	= false;

		FrameworkElement*	pElement	= GetAssociatedObject();
		
		if (pElement != NULL)
		{
			BaseComponent*	pItem	= pElement->GetDataContext();
	
			if (pItem != NULL)
			{
				BaseCommand*	pStartDrag	= GetStartDragCommand();

				if (pStartDrag != NULL && true == pStartDrag->CanExecute(pItem))
				{
					SetDragStartOffset(pElement->PointFromScreen(_event.position));

					pStartDrag->Execute(pItem);
				}

				DragDrop::DoDragDrop(pElement, pItem, DragDropEffects_Move, [this](DependencyObject*, BaseComponent*, UIElement*, const Point&, uint32_t _effects)
				{
		            Ptr<BaseComponent>	dragSuccess	= Boxing::Box<bool>(_effects != DragDropEffects_None);

					BaseCommand*	pEndDrag	= GetEndDragCommand();
						
					if (pEndDrag != NULL && true == pEndDrag->CanExecute(dragSuccess))
					{
						pEndDrag->Execute(dragSuccess);
					}
				});
			}
		}
	}
}

NS_BEGIN_COLD_REGION

NS_IMPLEMENT_REFLECTION(TargetDragBehavior)
{
	NsMeta<TypeId>("SimLabs.TargetDragBehavior");

	UIElementData*	pData	= NsMeta<UIElementData>(TypeOf<SelfClass>());

	pData->RegisterProperty<Point>(DragStartOffsetProperty, "DragStartOffset", PropertyMetadata::Create(Point(0.0f, 0.0f)));
	pData->RegisterProperty<Ptr<BaseCommand>>(StartDragCommandProperty, "StartDragCommand", PropertyMetadata::Create(Ptr<BaseCommand>()));
	pData->RegisterProperty<Ptr<BaseCommand>>(EndDragCommandProperty, "EndDragCommand", PropertyMetadata::Create(Ptr<BaseCommand>()));
}

const Noesis::DependencyProperty*	TargetDragBehavior::DragStartOffsetProperty;
const Noesis::DependencyProperty*	TargetDragBehavior::StartDragCommandProperty;
const Noesis::DependencyProperty*	TargetDragBehavior::EndDragCommandProperty;

ENDNAMESPACE
The drag adorner is the same except for the namespace.

This is all contained in a Main Window:
<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:local="clr-namespace:SimLabs"
	x:Class="SimLabs.MainWindow"
	Background="{StaticResource WindowBackground}"
	Title="Open Range" Width="1280" Height="720">
	<Grid>
		<local:InstructionEdit x:Name="InstructionEdit" Visibility="Hidden"/>
		<local:MediaSelection x:Name="MediaSelection" Visibility="Hidden"/>
		<local:StageEdit x:Name="StageEdit" Visibility="Hidden"/>
		<local:StageSelection x:Name="StageSelection" Visibility="Hidden"/>
		<local:TargetEdit x:Name="TargetEdit" Visibility="Hidden"/>
		<local:CourseSelection x:Name="CourseSelection"/>
		<Border x:Name="ConfirmationDialog" Background="{StaticResource WindowBackground}" CornerRadius="6" BorderBrush="Gray" BorderThickness="2" Padding="8" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">
			<StackPanel VerticalAlignment="Center" MaxWidth="1200">
				<TextBlock x:Name="ConfirmationMessage" HorizontalAlignment="Center" Foreground="#FFFFFF" Margin="0,4,0,10">Message</TextBlock>
				<WrapPanel HorizontalAlignment="Center" Margin="0,10,0,4">
					<Button x:Name="ConfirmationYesButton" Click="OnYesClick" Margin="4,0,4,0">Yes</Button>
					<Button x:Name="ConfirmationNoButton" Click="OnNoClick" Margin="4,0,4,0">No</Button>
				</WrapPanel>
			</StackPanel>
		</Border>
		<Border x:Name="InfoDialog" Background="{StaticResource WindowBackground}" CornerRadius="6" BorderBrush="Gray" BorderThickness="2" Padding="8" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">
			<StackPanel VerticalAlignment="Center" MaxWidth="1200">
				<TextBlock x:Name="InfoMessage" HorizontalAlignment="Center" Foreground="#FFFFFF" Margin="0,4,0,10">Message</TextBlock>
				<Button x:Name="InfoButton" Click="OnInfoButtonClick" Margin="4,0,4,0">OK</Button>
			</StackPanel>
		</Border>
		<Border x:Name="InputDialog" Background="{StaticResource WindowBackground}" CornerRadius="6" BorderBrush="Gray" BorderThickness="2" Padding="8" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">
			<StackPanel VerticalAlignment="Center" MaxWidth="1200">
				<TextBlock x:Name="InputMessage" HorizontalAlignment="Center" Foreground="#FFFFFF" Margin="0,4,0,10">Message</TextBlock>
				<TextBox x:Name="InputText" Foreground="#FFFFFF" Margin="0,4,0,10"/>
				<WrapPanel HorizontalAlignment="Center" Margin="0,10,0,4">
					<Button x:Name="ConfirmationOKButton" Click="OnOKClick" Margin="4,0,4,0">OK</Button>
					<Button x:Name="ConfirmationCancelButton" Click="OnCancelClick" Margin="4,0,4,0">Cancel</Button>
				</WrapPanel>
			</StackPanel>
		</Border>
	</Grid>
</Window>
 
User avatar
sfernandez
Site Admin
Posts: 3014
Joined: 22 Dec 2011, 19:20

Re: Troubleshooting Drag and Drop?

30 Dec 2020, 12:55

Thanks for all the files, I understand now what is happening. The DragAdornerBehavior was designed to be set on the root of the UI (in your window), but looking at your files I see the possibilities of allowing localized drag&drop in a setup like yours. In order to achieve that the adorner behavior needs to hook to the events of root element instead of its associated object:
class DragAdornerBehavior final: public NoesisApp::BehaviorT<Noesis::FrameworkElement>
{
public:
    const Noesis::Point& GetDragStartOffset() const;
    void SetDragStartOffset(const Noesis::Point& offset);

    float GetDraggedItemX() const;
    float GetDraggedItemY() const;

public:
    static const Noesis::DependencyProperty* DragStartOffsetProperty;
    static const Noesis::DependencyProperty* DraggedItemXProperty;
    static const Noesis::DependencyProperty* DraggedItemYProperty;

protected:
    Noesis::Ptr<Freezable> CreateInstanceCore() const override;
    void OnAttached() override;
    void OnDetaching() override;

private:
    void OnLoaded(Noesis::BaseComponent* sender, const Noesis::RoutedEventArgs& e);
    void OnUnloaded(Noesis::BaseComponent* sender, const Noesis::RoutedEventArgs& e);

    void OnDragOver(Noesis::BaseComponent* sender, const Noesis::DragEventArgs& e);
    void OnDrop(Noesis::BaseComponent* sender, const Noesis::DragEventArgs& e);

    void SetDraggedItemX(float x);
    void SetDraggedItemY(float y);

private:
    Noesis::FrameworkElement* mRoot;

    NS_DECLARE_REFLECTION(DragAdornerBehavior, Behavior)
};
DragAdornerBehavior::DragAdornerBehavior(): mRoot(nullptr)
{
}
...
void DragAdornerBehavior::OnAttached()
{
    ParentClass::OnAttached();

    FrameworkElement* element = GetAssociatedObject();
    element->Loaded() += MakeDelegate(this, &DragAdornerBehavior::OnLoaded);
    element->Unloaded() += MakeDelegate(this, &DragAdornerBehavior::OnUnloaded);
}

void DragAdornerBehavior::OnDetaching()
{
    FrameworkElement* element = GetAssociatedObject();
    element->Loaded() -= MakeDelegate(this, &DragAdornerBehavior::OnLoaded);
    element->Unloaded() -= MakeDelegate(this, &DragAdornerBehavior::OnUnloaded);

    ParentClass::OnDetaching();
}

void DragAdornerBehavior::OnLoaded(BaseComponent*, const RoutedEventArgs&)
{
    mRoot = GetAssociatedObject()->GetView()->GetContent();
    mRoot->SetAllowDrop(true);
    mRoot->DragOver() += MakeDelegate(this, &DragAdornerBehavior::OnDragOver);
    mRoot->Drop() += MakeDelegate(this, &DragAdornerBehavior::OnDrop);
}

void DragAdornerBehavior::OnUnloaded(BaseComponent*, const RoutedEventArgs&)
{
    mRoot->ClearLocalValue(UIElement::AllowDropProperty);
    mRoot->DragOver() -= MakeDelegate(this, &DragAdornerBehavior::OnDragOver);
    mRoot->Drop() -= MakeDelegate(this, &DragAdornerBehavior::OnDrop);
    mRoot = nullptr;
}
...
Please try with that modified adorner behavior and let me know if it is working as expected.
 
cdoty
Topic Author
Posts: 13
Joined: 30 Nov 2017, 13:34

Re: Troubleshooting Drag and Drop?

30 Dec 2020, 13:46

Yes.. that did fix it. Or at least enough to start fixing it. Thank you.
 
User avatar
jsantos
Site Admin
Posts: 3939
Joined: 20 Jan 2012, 17:18
Contact:

Re: Troubleshooting Drag and Drop?

31 Dec 2020, 10:38

Thank you, marking as solved

Who is online

Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 2 guests