@@ -92,7 +92,17 @@ public void HandleItemChange()
9292 HookupItemChangeListener ( null , Item ) ;
9393 UpdateExpansionState ( ) ;
9494 ReevaluateSelection ( ) ;
95- CanDrag = Item ? . GetType ( ) . GetProperty ( "Path" ) ? . GetValue ( Item ) is string path && Path . IsPathRooted ( path ) ;
95+
96+ if ( Item is IDraggableSidebarItemModel draggableItem )
97+ {
98+ CanDrag = draggableItem . DropPath is not null && System . IO . Path . IsPathRooted ( draggableItem . DropPath ) ;
99+ UseReorderDrop = ! IsGroupHeader && CanDrag && draggableItem . IsReorderDropItem ;
100+ }
101+ else
102+ {
103+ CanDrag = false ;
104+ UseReorderDrop = false ;
105+ }
96106 }
97107
98108 private void HookupOwners ( )
@@ -140,30 +150,48 @@ private void HookupItemChangeListener(ISidebarItemModel? oldItem, ISidebarItemMo
140150
141151 private void SidebarItem_DragStarting ( UIElement sender , DragStartingEventArgs args )
142152 {
143- if ( Item ? . GetType ( ) . GetProperty ( "Path" ) ? . GetValue ( Item ) is not string dragPath || ! Path . IsPathRooted ( dragPath ) )
153+ if ( Item is not IDraggableSidebarItemModel draggableItem || draggableItem . DropPath is not string dragPath || ! System . IO . Path . IsPathRooted ( dragPath ) )
144154 return ;
145155
146- args . Data . SetData ( StandardDataFormats . Text , dragPath ) ;
147- args . Data . RequestedOperation = DataPackageOperation . Move | DataPackageOperation . Copy | DataPackageOperation . Link ;
148- args . Data . SetDataProvider ( StandardDataFormats . StorageItems , async request =>
156+ try
149157 {
150- var deferral = request . GetDeferral ( ) ;
151- try
158+ args . Data . SetData ( StandardDataFormats . Text , dragPath ) ;
159+ args . Data . RequestedOperation = DataPackageOperation . Move | DataPackageOperation . Copy | DataPackageOperation . Link ;
160+ args . Data . SetDataProvider ( StandardDataFormats . StorageItems , async request =>
152161 {
153- if ( Directory . Exists ( dragPath ) )
162+ var deferral = request . GetDeferral ( ) ;
163+ try
154164 {
155- var folder = await StorageFolder . GetFolderFromPathAsync ( dragPath ) ;
156- request . SetData ( new IStorageItem [ ] { folder } ) ;
165+ if ( Directory . Exists ( dragPath ) )
166+ {
167+ var folder = await StorageFolder . GetFolderFromPathAsync ( dragPath ) ;
168+ request . SetData ( new IStorageItem [ ] { folder } ) ;
169+ }
157170 }
158- }
159- catch
160- {
161- }
162- finally
163- {
164- deferral . Complete ( ) ;
165- }
166- } ) ;
171+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
172+ {
173+ // External OLE drag payload became stale while resolving StorageFolder — ignore.
174+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale external drag payload while resolving StorageFolder in data provider." ) ;
175+ }
176+ finally
177+ {
178+ try
179+ {
180+ deferral . Complete ( ) ;
181+ }
182+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
183+ {
184+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale OLE deferral during drag data provider completion." ) ;
185+ }
186+ }
187+ } ) ;
188+ }
189+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
190+ {
191+ // OLE channel was already closed before drag started
192+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale OLE drag payload on DragStarting, cancelling drag." ) ;
193+ args . Cancel = true ;
194+ }
167195 }
168196
169197 private void SetFlyoutOpen ( bool isOpen = true )
@@ -394,21 +422,64 @@ private async void ItemBorder_DragOver(object sender, DragEventArgs e)
394422 IsExpanded = true ;
395423 }
396424
397- var insertsAbove = DetermineDropTargetPosition ( e ) ;
398- if ( insertsAbove == SidebarItemDropPosition . Center )
425+ DragOperationDeferral ? deferral = null ;
426+ try
399427 {
400- VisualStateManager . GoToState ( this , "DragOnTop" , true ) ;
428+ deferral = e . GetDeferral ( ) ;
401429 }
402- else if ( insertsAbove == SidebarItemDropPosition . Top )
430+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
403431 {
404- VisualStateManager . GoToState ( this , "DragInsertAbove" , true ) ;
432+ // OLE pipeline already torn down before DragOver deferral was obtained — abort gracefully.
433+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale OLE drag payload on GetDeferral during DragOver." ) ;
434+ VisualStateManager . GoToState ( this , "Normal" , true ) ;
435+ return ;
405436 }
406- else if ( insertsAbove == SidebarItemDropPosition . Bottom )
437+
438+ try
407439 {
408- VisualStateManager . GoToState ( this , "DragInsertBelow" , true ) ;
409- }
440+ var insertsAbove = DetermineDropTargetPosition ( e ) ;
441+
442+ if ( Owner is not null )
443+ await Owner . RaiseItemDragOverAsync ( this , insertsAbove , e ) ;
410444
411- Owner ? . RaiseItemDragOver ( this , insertsAbove , e ) ;
445+ if ( ! e . Handled || e . AcceptedOperation == DataPackageOperation . None )
446+ {
447+ VisualStateManager . GoToState ( this , "Normal" , true ) ;
448+ return ;
449+ }
450+
451+ if ( insertsAbove == SidebarItemDropPosition . Center )
452+ {
453+ VisualStateManager . GoToState ( this , "DragOnTop" , true ) ;
454+ }
455+ else if ( insertsAbove == SidebarItemDropPosition . Top )
456+ {
457+ VisualStateManager . GoToState ( this , "DragInsertAbove" , true ) ;
458+ }
459+ else if ( insertsAbove == SidebarItemDropPosition . Bottom )
460+ {
461+ VisualStateManager . GoToState ( this , "DragInsertBelow" , true ) ;
462+ }
463+ }
464+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
465+ {
466+ // External OLE drag payload became stale during DragOver processing
467+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale external drag payload during sidebar DragOver processing." ) ;
468+ e . AcceptedOperation = DataPackageOperation . None ;
469+ VisualStateManager . GoToState ( this , "Normal" , true ) ;
470+ }
471+ finally
472+ {
473+ try
474+ {
475+ deferral ? . Complete ( ) ;
476+ }
477+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
478+ {
479+ // Deferral completion failed because OLE channel was torn down — ignore.
480+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale OLE deferral on DragOver completion." ) ;
481+ }
482+ }
412483 }
413484
414485 private void ItemBorder_ContextRequested ( UIElement sender , Microsoft . UI . Xaml . Input . ContextRequestedEventArgs args )
@@ -425,7 +496,17 @@ private void ItemBorder_DragLeave(object sender, DragEventArgs e)
425496 private void ItemBorder_Drop ( object sender , DragEventArgs e )
426497 {
427498 UpdatePointerState ( ) ;
428- Owner ? . RaiseItemDropped ( this , DetermineDropTargetPosition ( e ) , e ) ;
499+ try
500+ {
501+ Owner ? . RaiseItemDropped ( this , DetermineDropTargetPosition ( e ) , e ) ;
502+ }
503+ catch ( Exception ex ) when ( DragDropExceptionHelper . IsExpectedStaleDragData ( ex ) )
504+ {
505+ // External OLE drag payload became stale on drop — no reorder commit, reset state.
506+ DragDropExceptionHelper . LogStaleDrag ( ex , "Stale external drag payload during sidebar Drop — drop discarded." ) ;
507+ e . AcceptedOperation = DataPackageOperation . None ;
508+ e . Handled = true ;
509+ }
429510 }
430511
431512 private SidebarItemDropPosition DetermineDropTargetPosition ( DragEventArgs args )
0 commit comments