WPF - How to implement DataTemplate with complex logic? - wpf

I am currently transferring my app from WinForms to WPF.
Since I'm new in WPF, I stucked at creating DataTemplates for my treeView items. The screenshot shows how my treeview looked in WinForms version, and I need to get close result in WPF.
(My WinForms treeview)
As you can see, my DataTemplate's logic should take into account these factors:
Node type / defines which icon and fields combination will be displayed for particular item (node). App has about 7-8 node types. Type stored in separate node's field.
Variable values / I need to replace with text if null, etc
Numeric variable values / e.g.: set gray color if zero, etc.
Other properties / e.g.: adding textblocks depending on boolean fields.
And so on...
All these factors result into huge amount of possible item params combinations.
Also I'm using DevComponents WPF DotNetBar AdvTree to divide item properties into columns. I presume I should create 'sub templates' for different field sets and compose from them the entire DataTemplate for each column.
I've learned about triggers, and have to say that implementing my logic with triggers will make my subtemplates huge anyway.
(Current state of my WPF treeview)
So here are my questions:
Are there any ways to dynamically compose complex templates with C# code (without creating raw XAML and loading it at runtime)?
Maybe I should use completely different way (instead of using DataTemplate)? In Winforms I just used OwnerDraw mode, so the task was MUCH easier than in WPF :(
And how to display nested properties inside template? e.g.: Item.Prop.Subprop1.Subprop2.Targetprop.
PS: English is not my first language, sorry for your eyes.

1) The answer is yes.
For exemple if you want to define a template in your window for a simple string
public MainWindow()
{
InitializeComponent();
DataTemplate template = new DataTemplate(typeof(string));
FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
borderFactory.SetValue(Border.PaddingProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderThicknessProperty, new Thickness(1));
borderFactory.SetValue(Border.BorderBrushProperty, Brushes.Red);
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
textFactory.SetBinding(TextBlock.TextProperty, new Binding
{
Mode = BindingMode.OneWay
});
borderFactory.AppendChild(textFactory);
template.VisualTree = borderFactory;
myControl.ContentTemplate = template;
}
And in the Window you just put something like
<ContentControl x:Name="myControl" Content="Test text" Margin="10"/>
Your content control will render the string surrounded by a red border.
But as you anc see it is really complex to define your templates in this way.
The only scenario where i could imagine this approache is for some kind of procedurally generated templates.
Another way is to generate a string for the template and then load it with XamlReader:
string xaml = "<Ellipse Name=\"EllipseAdded\" Width=\"300.5\" Height=\"200\"
Fill=\"Red\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"/>";
object ellipse = XamlReader.Load(xaml);
2) I don't really see the need to generate templates in code behind. For exemple for this kind of data structure:
public class User
{
public string Name { get; set; }
public User Friend { get; set; }
}
public class RootNode
{
public string Title { get; set; }
public User User { get; set; }
public List<Node> Nodes { get; set; }
}
public class Node
{
public string Title { get; set; }
public List<SubNode> SubNodes { get; set; }
}
public class SubNode
{
public string Title { get; set; }
}
You can define this type of template:
<Window
...
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:RootNode}" ItemsSource="{Binding Nodes}">
<StackPanel x:Name="spContainer" Orientation="Horizontal">
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding User}" Value="{x:Null}">
<Setter TargetName="spContainer" Property="Background" Value="Yellow"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding SubNodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:SubNode}" ItemsSource="{Binding Nodes}">
<TextBlock Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding RootNodes}"/>
</Grid>
</Window>
As you can see you can define a template by data type, you can also use triggers to modify the behavior in specific cases, you could also use som binding converters...
3) You can bind to nested properties just like to normal ones :
<TextBlock Text="{Binding User.Friend.Friend.Name}"/>
However in some cases more than two level bindings could fail (fail to resolve or fail to update when property changes, ...)

Related

CRUD operations with an unknown type

I am trying to make a program that gives you a CRUD interface for List of any objects you give it. That includes:
Showing all of their properties inside a ListBox
The ability to insert a new object
The ability to update an object
The ability to delete an object
Keep in mind that, at the compile-time, I have no idea what kind of objects I am getting. For example, I want to have a TextBlock for each of the properties simply listed inside ListBox's DataTemplate. So how would I do the data binding if I don't know the name of the property? Also, how would I generate an insertion form when I don't know property names?
And finally, is it possible to do it using pure MVVM Pattern, without any Code-Behind?
Thanks
One option: Wrap PropertyInfo in a PropertyInfoViewModel so you can bind to it's value:
class PropertyInfoViewModel
{
Object CRUDObject { get; set; }
PropertyInfo PropertyInfo { get; set; }
Object Value {
get
{
return PropertyInfo.GetValue(CRUDObject);
}
set
{
PropertyInfo.SetValue(CRUDObject, value);
}
}
}
You could have an ObservableCollection in your CRUDObjectViewModel, populated when you create it or change the CRUD it's attached to (Look up reflection if confused by this).
Use a template selector to choose a particular editor to display for the PropertyInfoViewModel:
public class PropertyTypeTemplateSelector : DataTemplateSelector
{
public DataTemplate BooleanTemplate { get; set; }
public DataTemplate GuidTemplate { get; set; }
public DataTemplate StringTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
PropertyInfo propertyInfo = (item as PropertyInfoViewModel).PropertyInfo;
if (propertyInfo.PropertyType == typeof(Boolean))
{
return BooleanTemplate;
}
else if (propertyInfo.PropertyType == typeof(Guid))
{
return GuidTemplate;
}
else if (propertyInfo.PropertyType == typeof(String))
{
return StringTemplate;
}
return null;
}
}
You could use it like this:
<ListBox ItemsSource="{Binding Properties}">
<ListBox.Resources>
<DataTemplate x:Key="BooleanTemplate">
<CheckBox Content="{Binding PropertyInfo.Name}" IsChecked="{Binding Value}"/>
</DataTemplate>
<DataTemplate x:Key="GuidTemplate">
<StackPanel>
<TextBox Text="{Binding PropertyInfo.Name}"/>
<TextBox Text="{Binding Value, ValueConverter={StaticResources MyGuidConverter}}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="StringTemplate">
<StackPanel>
<TextBox Text="{Binding PropertyInfo.Name}"/>
<TextBox Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="Null"/>
</ListBox.Resources>
<ListBox.ItemTemplateSelector>
<helpers:PropertyTypeTemplateSelector BooleanTemplate="{StaticResource BooleanTemplate}"
GuidTemplate="{StaticResource GuidTemplate}"
StringTemplate="{StaticResource StringTemplate}"/>
</ListBox.ItemTemplateSelector>
</ListBox>
Might have to think about how to deal with changes/updates though, as this isn't using NotifyPropertyChanged to keep the UI up to date.
I've not tested any of this, but it should work, I think.
This control WPFCrudControl may fit with your problem.
A generic WPF CrudControl implemented based on the MVVM pattern. It gives a huge productivity boost for straightforward CRUD operations (Add, Edit, Delete, Validate, Listing with sorting, paging and searching). The control abstracts both the UI and business logic, so it requires relatively minimal coding effort, while keeping it possible to customize its behavior.

Binding on dynamically-added elements

TPTB have decided that our app must run in a single window, popping up new windows in modal mode is not allowed.
And naturally, we have a UI design that involves popping up modal dialogs all over the place.
So I added a top-level Grid to the Window. In that Grid I defined no rows or columns, so everything draws in Row 0/Column 0.
The first element in the Grid was another Grid that contained everything that was normally displayed in the Window. The second was a full-sized Border with a gray, semi-transparent Background. The rest were Borders with wide Margins and white Backgrounds, containing the various UserControls that needed to be displayed as popups. All but the first had Visibility="Collapsed".
And then, when I needed to show a popup, I'd set Visibility="Visible" on the gray background and on the appropriate UserControl. The result was a nice shadowbox effect that worked fine.
Until somebody decided that the popups needed to be able to display popups. In a non-predictable order.
The limitation of the method I had implemented, using Visibility="Collapsed" elements in a Grid was that their order was fixed. UserControlB would always be displayed on top of UserControlA, even if it was UserControlB that asked to have UserControlA displayed. And that's not acceptable.
So my next attempt was to define the various UserControls in Window.Resources, and to add them to the Grid in code:
this.masterGrid.Children.Add(this.Resources["userControlA"] as UserControlA);
And that almost works. But the bindings are all messed up.
As an example, one of the controls is supposed to bind a Property to the CurrentItem of a collection in a member object of the Window's viewmodel. When I had the control defined as an invisible item in the Grid, it worked fine. But when I defined it as a Resource, the Property was null - it was never bound.
So I tried binding it in code, after I added it to the grid:
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
And that compiles and runs just fine, but I'm not binding to the right object.
The first time I display the UserControl, I see the right object bound to it. But when I close it, and move the CurrentItem in the collection to a different object, and display the UserControl again, I still see the first object bound. If I close it again, and open it a third time, then I will see the right object bound to the control.
I've checked in code, and the CurrentItem that I'm binding to is right, every time, but it only seems to take every other time.
So I tried explicitly clearing the binding, first:
BindingOperations.ClearBinding(userControlA, UserControlA.myProperty);
userControlA.SetBinding(UserControlA.myProperty, new Binding()
{ Source = this.viewModel.myCollection.CurrentItem });
But that doesn't seem to have made any difference.
In all, it feels like I'm running down a rabbit hole, chasing deeper and deeper into complexity, to solve what should be a fairly simple problem.
Does anyone have any suggestions as to:
How to get binding to work on dynamically-added elements, or
How to get arbitrarily-ordered popups to display, as shadowboxes, without using dynamically-ordered elements?
Thanks in advance.
While it seems really odd for me that you can't create new Windows, I would definitely recommend not to complicate it too much by doing unnecesary things such as storing your views in the MainWindow's resources.
It would be better if you just added new instances of these elements into an ObservableCollection:
XAML:
<Window x:Class="WpfApplication4.Window8"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="Window8" Height="300" Width="300">
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel1}">
<StackPanel Background="Green">
<TextBlock Text="This is ViewModel1!!"/>
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel2}">
<StackPanel Background="Blue" HorizontalAlignment="Center">
<TextBlock Text="This is ViewModel2!!"/>
<TextBlock Text="{Binding Text2}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModel3}">
<StackPanel Background="Red" VerticalAlignment="Center">
<TextBlock Text="This is ViewModel3!!"/>
<TextBlock Text="{Binding Text3}"/>
<TextBox Text="{Binding Text3}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<Button Width="100" Content="Add" Click="Add_Click" DockPanel.Dock="Top"/>
<Button Width="100" Content="Remove" Click="Remove_Click" DockPanel.Dock="Top"/>
<ListBox ItemsSource="{Binding ActiveWidgets}" SelectedItem="{Binding SelectedWidget}">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter/>
</ControlTemplate>
</ListBox.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter ContentSource="Content"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DockPanel>
</Window>
Code Behind:
using System.Linq;
using System.Windows;
using System.Collections.ObjectModel;
using System;
namespace WpfApplication4
{
public partial class Window8 : Window
{
private WidgetsViewModel Widgets { get; set; }
public Window8()
{
InitializeComponent();
DataContext = Widgets = new WidgetsViewModel();
}
private Random rnd = new Random();
private int lastrandom;
private void Add_Click(object sender, RoutedEventArgs e)
{
var random = rnd.Next(1, 4);
while (random == lastrandom)
{
random = rnd.Next(1, 4);
}
lastrandom = random;
switch (random)
{
case 1:
Widgets.ActiveWidgets.Add(new ViewModel1() {Text = "This is a Text"});
break;
case 2:
Widgets.ActiveWidgets.Add(new ViewModel2() { Text2 = "This is another Text" });
break;
case 3:
Widgets.ActiveWidgets.Add(new ViewModel3() { Text3 = "This is yet another Text" });
break;
}
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
private void Remove_Click(object sender, RoutedEventArgs e)
{
Widgets.ActiveWidgets.Remove(Widgets.SelectedWidget);
Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
}
}
public class WidgetsViewModel: ViewModelBase
{
public ObservableCollection<ViewModelBase> ActiveWidgets { get; set; }
private ViewModelBase _selectedWidget;
public ViewModelBase SelectedWidget
{
get { return _selectedWidget; }
set
{
_selectedWidget = value;
NotifyPropertyChange(() => SelectedWidget);
}
}
public WidgetsViewModel()
{
ActiveWidgets = new ObservableCollection<ViewModelBase>();
}
}
public class ViewModel1: ViewModelBase
{
public string Text { get; set; }
}
public class ViewModel2: ViewModelBase
{
public string Text2 { get; set; }
}
public class ViewModel3: ViewModelBase
{
public string Text3 { get; set; }
}
}
Just copy and paste my code in a File - New - WPF Application and see the results for yourself.
Since the Grid always places the last UI Element added to it topmost, you will see that Adding items to the observablecollection makes these "different widgets" always appear on top of each other, with the topmost being the last one added.
The bottom line is, when WidgetA requests to open WidgetB, just create a new WidgetBViewModel and add it to the ActiveWidgets collection. Then, when WidgetB is no longer needed, just remove it.
Then, it's just a matter of putting your UserControls inside a proper DataTemplate for each ViewModel. I strongly suggest you keep a separate ViewModel for each of your Widgets, and if you need to share data between them, just share data between the ViewModels.
Don't attempt to do things like ListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" unless you have a good reason to.
This way you no longer have to deal with Panel.ZIndex stuff. Maybe you can create a couple of attached properties to deal with things like focus and whatnot, but this approach is dead simple, and by far more performant than the Visibility and the Resources approaches.

How do I bind a Listview SelectedItem to a Textbox using the TwoWay mode?

I am very new to WPF and testing some things that I would like to include in an application that I will be working on. I have a 2 row ListView (bound to a textbox) with the names Scott Guthrie and Jon Skeet in it. I am trying to select "Scott Guthrie" in the ListView and have it populate the TextBox. I want to be able to edit the text and tab off and have the ListView updated.
Edit:I removed the code since that really didn't add anything to the question.
Wow, that's really complicated what you've got there.
This can be accomplished in a very simple way. You need a model to represent the programmer, a view model to hold a list of programmers, and simple binding to take care of the rest.
The model:
public sealed class Programmer
{
public string Name { get; set; }
}
Its very simple. An object representing a programmer with a name. We must encapsulate the name within an object because strings are immutable in .NET. If you tried binding against a single string in a list of strings, changes wouldn't propagate.
The collection of programmers is kept in a ViewModel. In this case, I call it ViewModel, because I have no imagination. This view model contains everything that the view binds against. In this case, its the list of programmers.
public sealed class ViewModel
{
public ObservableCollection<Programmer> Programmers { get; private set; }
public ViewModel()
{
Programmers = new ObservableCollection<Programmer>();
}
}
The ViewModel is set as the DataContext of our view. The DataContext flows down the visual tree, and we can bind against it at any point.
public MainWindow()
{
var vm = new ViewModel();
vm.Programmers.Add(new Programmer { Name = "Jon Skeet" });
vm.Programmers.Add(new Programmer { Name = "Scott Guthrie" });
DataContext = vm;
InitializeComponent();
}
You can set the DataContext in any way you want; I'm doing it here for simplicity's sake.
In the UI, I simply bind the ListView against the list of Programmers in the ViewModel (the DataContext, unless otherwise stated, is the root of the binding path). I then bind the TextBox against the SelectedItem of the ListBox. You select a Programmer from the list, which then becomes the SelectedItem, which I can then change the Name of.
<Window
x:Class="Programmers.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:t="clr-namespace:Programmers"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox
x:Name="list"
ItemsSource="{Binding Programmers}"
DisplayMemberPath="Name" />
<TextBox
Grid.Column="1"
VerticalAlignment="Top"
Text="{Binding SelectedItem.Name, ElementName=list}" />
</Grid>
</Window>
Simple, once you get the hang of it.
This works (except that you need to validate the textbox since you can enter any text.. a dropdown might be a better choice).
View:
<TabItem x:Name="RightTabPage" Header="RightModel" DataContext="{Binding Right}">
<StackPanel>
<TextBox Text="{Binding SelectedGuru}"/>
<ListView SelectedItem="{Binding SelectedGuru}" ItemsSource="{Binding Gurus}"/>
</StackPanel>
</TabItem>
ViewModel:
public class RightViewModel
{
public RightViewModel()
{
Gurus = new[] {"Scott Guthrie", "Jon Skeet"};
SelectedGuru = Gurus.First();
}
public string SelectedGuru { get; set; }
public IEnumerable<string> Gurus{ get; set; }
}

What techniques can I employ to create a series of UI Elements from a collection of objects using WPF?

I'm new to WPF and before I dive in solving a problem in completely the wrong way I was wondering if WPF is clever enough to handle something for me.
Imagine I have a collection containing objects. Each object is of the same known type and has two parameters. Name (a string) and Picked (a boolean).
The collection will be populated at run time.
I would like to build up a UI element at run time that will represent this collection as a series of checkboxes. I want the Picked parameter of any given object in the collection updated if the user changes the selected state of the checkbox.
To me, the answer is simple. I iterate accross the collection and create a new checkbox for each object, dynamically wiring up a ValueChanged event to capture when Picked should be changed.
It has occured to me, however, that I may be able to harness some unknown feature of WPF to do this better (or "properly"). For example, could data binding be employed here?
I would be very interested in anyone's thoughts.
Thanks,
E
FootNote: The structure of the collection can be changed completely to better fit any chosen solution but ultimately I will always start from, and end with, some list of string and boolean pairs.
I would strongly recommend the ItemsControl, its behaviour is as close as you can get to the ASP.Net repeater control so it is very flexible.
Declare the item control as:
<ItemsControl Name="YourItemsControl"
ItemsSource="{Binding Path=YourCollection}"
ItemTemplate="{StaticResource YourTemplate}">
</ItemsControl>
Then you can use the datatemplate to organise the data into a display format for the user
<DataTemplate x:Key="ProjectsTemplate">
<StackPanel Margin="0,0,0,10">
<Border CornerRadius="2,2,0,0" Background="{StaticResource ItemGradient}" d:LayoutOverrides="Width, Height">
<local:ItemContentsUserControl Height="30"/>
</Border>
...
Useful ItemsControl Links
http://drwpf.com/blog/itemscontrol-a-to-z/
http://www.galasoft.ch/mydotnet/articles/article-2007041201.aspx
I hope this helps you.
You can use Data Templates. Here's a good post about it.
This is exactly the kind of scenario WPF simplifies. Event-handlers- bah! Data-binding and data templates make this a cinch. I have constructed an example illustrating how you can do this.
Here is the code-behind, which declares a class to represent your items- PickedItem. I then create a collection of these items and populate it with some samples.
public partial class DataBoundCollection : Window
{
public DataBoundCollection()
{
Items = new ObservableCollection<PickedItem>();
Items.Add(new PickedItem("Item 1"));
Items.Add(new PickedItem("Item 2"));
Items.Add(new PickedItem("Item 3"));
InitializeComponent();
}
public ObservableCollection<PickedItem> Items
{
get;
set;
}
}
public class PickedItem
{
public PickedItem(string name)
{
Name = name;
Picked = false;
}
public string Name
{
get;
set;
}
public bool Picked
{
get;
set;
}
}
Now, let's look at the XAML mark-up for this window:
<Window x:Class="TestWpfApplication.DataBoundCollection"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBoundCollection" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Picked}" Margin="5"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I create a ListBox to hold the items, and bind its ItemsSource property to the collection I created in the code-behind. Then, I provide the ListBox with an ItemTemplate, which determines how each PickedItem will be rendered. The DataTemplate in this case is as simple as a check-box and some text, both bound to the member variables on PickedItem. Now, when I check any of these items, the data in the underlying collection is modified, in real-time, with no event handlers needed. Ta-da!
alt text http://img405.imageshack.us/img405/1083/databoundcollection.png

WPF HiercharchicalDataTemplate.DataType: How to react on interfaces?

Problem
I've got a collection of IThings and I'd like to create a HierarchicalDataTemplate for a TreeView. The straightforward DataType={x:Type local:IThing} of course doesn't work, probably because the WPF creators didn't want to handle the possible ambiguities.
Since this should handle IThings from different sources at the same time, referencing the implementing class is out of question.
Current solution
For now I'm using a ViewModel which proxies IThing through a concrete implementation:
public interface IThing {
string SomeString { get; }
ObservableCollection<IThing> SomeThings { get; }
// many more stuff
}
public class IThingViewModel
{
public IThing Thing { get; }
public IThingViewModel(IThing it) { this.Thing = it; }
}
<!-- is never applied -->
<HierarchicalDataTemplate DataType="{x:Type local:IThing}">
<!-- is applied, but looks strange -->
<HierarchicalDataTemplate
DataType="{x:Type local:IThingViewModel}"
ItemsSource="{Binding Thing.SomeThings}">
<TextBox Text="{Binding Thing.SomeString}"/>
</HierarchicalDataTemplate>
Question
Is there a better (i.e. no proxy) way?
Another alternative (similar to jing's solution): If you only have one type of item, you can set the ItemTemplate directly. Then you don't need to set a key or a datatype.
In your ViewModel:
public ObservableCollection<IThing> Thingies { get; private set; }
In the View:
<TreeView ItemsSource="{Binding Thingies}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SomeThings}">
<TextBox Text="{Binding SomeString}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The reason for this is that the default template selector supports only concrete types, not interfaces. You need to create a custom DataTemplateSelector and apply it to the ItemTemplateSelector property of the TreeView. I can't find the URL where I found an example of it, but hopefully with this info, you can Google it.
Another solution is you give a key to the HierarchicalDataTemplate and put it in the Windows.Resources, and manually reference to it in the TreeView. <TreeView ItemDataTemplate={StaticResource templateKey}/>
But that limits the autoselection of data template according to data type, which is provided by WPF TreeView.

Resources