views:

54

answers:

1

So I'm using FileSystemWatcher to populate and update a playlist. I want to replicate many features of Windows Explorer, most importantly:
* inline rename
* slow double click to rename

I'm having quite a hassle doing this, so I thought, maybe there's an easier way than reimplementing the wheel? Maybe I can somehow host a Windows Explorer window in my application as a control?

+2  A: 

Hosting a real Windows Explorer window in your application is possible but fraught with peril: The techniques are different in XP vs Vista vs Win7 and you will be dealing with all sorts of low-level stuff. I would strongly recommend against trying it.

I think your best options are:

  1. Use Microsoft.Win32.OpenFileDialog if it can be easily adapted to your need, or
  2. Code your own functionality, or if you are very brave:
  3. Launch a separate Explorer window, optionally with code that tries to force its position and size to be over your application (this too is hard...)

Notes on inline rename feature

The inline rename and slow double-click to rename features are really quite trivial to implement.

In your view model add:

  • A "NewName" string DependencyProperty
  • A "Renaming" bool DependencyProperty with a PropertyChangedCallback. When "Renaming" goes true, copy Name to NewName. When it goes false, if NewName!=Name rename the file an update Name.

In your DataTemplate add a trigger on "Renaming" that replaces your TextBlock bound to "Name" with a TextBox bound to "NewName".

Add these event handlers: * KeyDown event: If F2 is pressed toggle Renaming. If Enter is pressed and Renaming, set Renaming=false. If Esc is pressed and Renaming copy Name to NewName and set Renaming=false. * LostFocus event: Set Renaming=false * SelectionChanged event on container: Record timestamp of last selection change. * MouseDown event: If left click and selection changed > 0.5 seconds ago, set Renaming = true

Many other features of Explorer view are similarly easy to implement, such as grouping and multiple columns.

Hope this helps.

Ray Burns
The hard part of inline rename is all those small things like the edit box stretching across multiple columns to accommodate the text but not over the edge of the `ListView`.
CannibalSmith
You can use an Adorner to solve this. Create the Adorner, bind its DataContext and add it to the AdornerLayer using an attached property set by the trigger. Use a ControlTemplate instantiate the TextBox in the Adorner. In your adorner's MeasureOverride, search up the visual tree of its target for the first containing ListView, TransformToDescendant its right-hand edge, and subtract to get the the proper width. Use the child's height. In ArrangeOverride redo the same computation and InvalidateMeasure() if it has changed.
Ray Burns
But Adorner doesn't support templating.
CannibalSmith
True. This means you will need a `Control` inside your `Adorner`. I derive most of my adorners from a small class I wrote which creates a `Control` in the constructor and binds its `Template` property to a locally `AddOwnered` "Template" property and stores it in a field, then overrides `VisualChildrenCount` to return 1 and `GetVisualChild()` to return the Control. This allows templating to be easily used with adorners. Note that the `Control` I use is constructed with `new Control()`, in other words it is not a subclass of `Control`.
Ray Burns
The binding to the `AddOwnered` property is like this: `_control.SetBinding(TemplateProperty, new Binding("Template") { Source = this });`
Ray Burns
I managed this with a `ContentPresenter` and a `DataTemplate`. The `TextBox` shows up alright, but it's transparent to mouse clicks. What now? By the way, I appreciate you walking me through this.
CannibalSmith