WPF TreeView ContainerFromItem returns null

Somebody asked me about this on StackOverflow, and I gave him the wrong answer at first. He told me, bro, listen, this thing returns null, seriously. I tried it and he was right.

Turns out it’s not quite exactly supported, in the way a naive person might expect: A TreeView in WPF is a gaggle of nested TreeViewItems (derived from HeaderedItemsControl), within a TreeView (derived from ItemsControl).

Each has its own ItemContainerGenerator.

A child item can have a different child type than its parent does, and there’s obviously no entirely transparent way to get a child item’s parent even if it’s consistent throughout the item tree. It just gets weird. They just didn’t bother.

So what you need is recursion (or a Rube Goldberg horror where you maintain the stack by hand — but That’s Not Who We Are). And I’ve got what you need.

namespace HollowEarth.ExtensionMethods
{
    public static class ItemContainerGeneratorExtensionMethods
    {
        /// <summary>
        /// We extend ItemContainerGenerator rather than TreeView itself because there 
        /// are other hierarchical ItemsControls, like MenuItem. 
        /// </summary>
        /// <typeparam name="TItem">The type of the item displayed in the treeview 
        /// (or whatever). If your items are of multiple types, use a common base class 
        /// and/or write a Byzantine itemParentSelector</typeparam>
        /// <param name="rootContainerGenerator"></param>
        /// <param name="item">Item to find the container for</param>
        /// <param name="itemParentSelector">Lambda to return the parent property 
        /// value from a TItem</param>
        /// <returns></returns>
        public static ItemsControl ContainerFromItem<TItem>(
            this ItemContainerGenerator rootContainerGenerator,
            TItem item,
            Func<TItem, TItem> itemParentSelector)
        {
            //  Caller can pass in treeView.SelectedItem as TItem in cases where 
            //  treeView.SelectedItem is null. Seems to me correct behavior there is 
            //  "No container". 
            if (item == null)
            {
                return null;
            }

            if (itemParentSelector == null)
            {
                throw new ArgumentNullException(nameof(itemParentSelector));
            }

            var parentItem = itemParentSelector(item);

            //  When we run out of parents, we're a root level node so we query the 
            //  rootContainerGenerator itself for the top level child container, and 
            //  start unwinding back to the item the caller gave us. 
            if (parentItem == null)
            {
                //  Our item is the parent of our caller's item. 
                //  This is the parent of our caller's container. 
                return rootContainerGenerator.ContainerFromItem(item) as ItemsControl;
            }
            else
            {
                //  This gets parents by unwinding the stack back down from root
                var parentContainer =
                    ContainerFromItem<TItem>(rootContainerGenerator,
                                             parentItem, itemParentSelector);

                return parentContainer.ItemContainerGenerator.ContainerFromItem(item)
                    as ItemsControl;
            }
        }
    }
}

Here’s what it looks like in use. This is the code the guy from StackOverflow was trying to get to work. I don’t know why you’d want to prevent people from selecting TreeViewItems, but that’s his problem. There are plenty of other reasons to want the selected TreeViewItem from a TreeView.

using HollowEarth.ExtensionMethods;

// ... snip ...

        private void TreeView_SelectedItemChanged_1(object sender, 
            RoutedPropertyChangedEventArgs<object> e)
        {
            //  Another way to do this would be to bind TreeViewItem.IsSelected to 
            //  a viewmodel IsSelected property in ItemContainerStyle, and have the 
            //  viewmodel property do the BeginInvoke() call. That's simpler, but 
            //  it's not appropriate for the viewmodel to be responsible for 
            //  worrying about how or if selection state is displayed in the UI. 

            var treeView = sender as TreeView;
            var selectedNode = treeView.SelectedItem as Node;

            TreeViewItem tvi = treeView.ItemContainerGenerator
                .ContainerFromItem(selectedNode, n => n.Parent) as TreeViewItem;

            Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(() =>
            {
                if (tvi == null)
                    return;
                tvi.IsSelected = false;
                tvi.Focus();
            }));

        }

By the way, WordPress’s handling of pre-formatted text with < and > is hilariously broken if you go from “HTML” mode in the editor back to “Visual” mode. It converts the character entities to angle brackets, and then makes an insane attempt to rewrite your C# code as HTML.

Advertisements

ViewModel Property Snippets, C#6

cmdprop and cmdpropparam assume that you have a DelegateCommand class. cmdprop assumes that you DelegateCommand has a constructor that takes Action and Func<bool> parameters, and cmdpropparam assumes that it has a constructor that takes Action<T> and Func<T, bool>. The other assumption is that your viewmodel classes raise PropertyChanged via a method named OnPropertyChanged, which uses CallerMemberNameAttribute. All of this can be altered easily.

All the snippets use the $end$ element to leave the insertion point in front of the private backing field name, so that I can type Ctrl+R,Ctrl+R and rename it from _Identifier to _identifier.

These aren’t too hard to install; Google knows how.

 <?xml version="1.0" encoding="utf-8"?>
 <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
   <CodeSnippet Format="1.0.0">
     <Header>
       <Title>inotprop</Title>
       <Author>McNamara</Author>
       <Description>INotifyPropertyChanged Property</Description>
       <HelpUrl></HelpUrl>
       <Shortcut>inotprop</Shortcut>
     </Header>
     <Snippet>
       <Declarations>
         <Literal Editable="true">
           <ID>PropName</ID>
           <Type />
           <ToolTip />
           <Default>PropertyName</Default>
           <Function />
         </Literal>
         <Literal Editable="true">
           <ID>PropType</ID>
           <Type />
           <ToolTip />
           <Default>String</Default>
           <Function />
         </Literal>
       </Declarations>
       <Code Language="csharp" Kind="method decl" Delimiter="$">
         <![CDATA[
         #region $PropName$ Property
         private $PropType$ $end$_$PropName$ = default($PropType$);
         public $PropType$ $PropName$
         {
             get { return _$PropName$; }
             set
             {
                 if (value != _$PropName$)
                 {
                     _$PropName$ = value;
                     OnPropertyChanged();
                 }
             }
         }
         #endregion $PropName$ Property
 ]]></Code>
     </Snippet>
   </CodeSnippet>
   <CodeSnippet Format="1.0.0">
     <Header>
       <Title>cmdprop</Title>
       <Author>McNamara</Author>
       <Description>DelegateCommand Property</Description>
       <HelpUrl></HelpUrl>
       <Shortcut>cmdprop</Shortcut>
     </Header>
     <Snippet>
       <Declarations>
         <Literal Editable="true">
           <ID>CommandName</ID>
           <Type />
           <ToolTip />
           <Default>NewCommand</Default>
           <Function />
         </Literal>
       </Declarations>
       <Code Language="csharp" Kind="method decl" Delimiter="$"><![CDATA[
         #region $CommandName$Command Property
         private ICommand $end$_$CommandName$Command = null;
         public ICommand $CommandName$Command
         {
             get {
                 if (null == _$CommandName$Command)
                 {
                     _$CommandName$Command = new DelegateCommand($CommandName$, () => Can$CommandName$);
                 }
                 return _$CommandName$Command;
             }
         }
 
         public bool Can$CommandName$ {
             get { return true; }
         }
 
         protected void $CommandName$()
         {
         }
         #endregion $CommandName$Command Property
 ]]>
       </Code>
     </Snippet>
   </CodeSnippet>
 
   <CodeSnippet Format="1.0.0">
     <Header>
       <Title>cmdpropparam</Title>
       <Author>McNamara</Author>
       <Description>DelegateCommand Property</Description>
       <HelpUrl></HelpUrl>
       <Shortcut>cmdpropparam</Shortcut>
     </Header>
     <Snippet>
       <Declarations>
         <Literal Editable="true">
           <ID>CommandName</ID>
           <Type />
           <ToolTip />
           <Default>NewCommand</Default>
           <Function />
         </Literal>
         <Literal Editable="true">
           <ID>ParamType</ID>
           <Type />
           <ToolTip />
           <Default>String</Default>
           <Function />
         </Literal>
       </Declarations>
       <Code Language="csharp" Kind="method decl" Delimiter="$"><![CDATA[
         #region $CommandName$Command Property
         private ICommand $end$_$CommandName$Command = null;
         public ICommand $CommandName$Command
         {
             get {
                 if (null == _$CommandName$Command)
                 {
                     _$CommandName$Command = new DelegateCommand<$ParamType$>($CommandName$, Can$CommandName$);
                 }
                 return _$CommandName$Command;
             }
         }
 
         public bool Can$CommandName$($ParamType$ param) {
             return true;
         }
 
         protected void $CommandName$($ParamType$ param)
         {
         }
         #endregion $CommandName$Command Property
 ]]></Code>
     </Snippet>
   </CodeSnippet>
 </CodeSnippets>

WPF: MarkupExtension Value Converter

I found this idea on StackOverflow. My impression is that MVVMLight does it. It’s simple enough not to need a whole framework just for this one thing.

Ordinarily, a value converter is instantiated as a resource and then used via the StaticResource markup extension. If you want to give it a parameter, you can give the binding a ConverterParameter. You can’t bind anything to ConverterParameter, it’s always an Object, there’s only one parameter, and there’s no Intellisense support.

If you inherit your value converters from MarkupExtension, you still can’t bind anything, but you can give it multiple parameters with names and types that Intellisense knows about. You can (indeed, must) also skip the minor inconvenience of instantiating the converter as a resource. You can also define the constructor so as to require certain (unnamed) parameters, but that’s not enforced at build time, only at runtime.

It looks like this:

namespace HollowEarth.Extensions.Converters
{
    public enum Operation { Add, Subtract, Multiply, Divide };

    public class Arithmetic : IValueConverter, MarkupExtension
    {
        public Operation Operation { get; set; }
        public double Operand { get; set; }

        public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var d = System.Convert.ToDouble(value);

            switch (Operation) {
                case Operation.Add: 
                    return d + Operand;
                case Operation.Subtract: 
                    return d - Operand;
                case Operation.Multiply:
                    return d * Operand;
                case Operation.Divide:
                    return d / Operand;
            }
        }

        public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }
}

And here’s now you might use it:

    <StackPanel 
        xmlns:hcnv="clr-namespace:HollowEarth.Extensions.Converters"
        Width="{Binding ActualWidth, 
                ElementName=SomeOtherStackPanel,
                Converter={hcnv:Arithmetic Operator=Multiply, Operand=2}}"
     >
         <!-- stuff -->
     </StackPanel>

You can fill in error checking and targetType conversion yourself.

And here’s a set of base classes:

namespace HollowEarth.Extensions.Converters
{
    public class ValueConverterMarkupExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return this;
        }
    }

    public class ValueConverterBase : ValueConverterMarkupExtension, IValueConverter
    {
        public virtual object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }

        public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }

    public class MultiValueConverterBase : ValueConverterMarkupExtension, IMultiValueConverter
    {
        public virtual object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.FirstOrDefault();
        }

        public virtual object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

 

 

 

WPF/MVVM: Introduction

Note: This needs substantial revision

The MVVM pattern in WPF is very powerful, but it differs in a lot of respects from other ways you may have written UI in the past. For one thing, you’ll be doing more things declaratively and writing less code. There’s a lot of loose coupling and a lot of things that happen automagically.

What I am about to describe is not the only possible way to work with WPF, but it’s the way it’s designed to be used, so it’s a way you should learn and master before driving yourself insane trying to prove that you know a better one (spoiler: You really, really don’t).

At its simplest, there are a few big pieces to contend with. There is a staggering number of pieces out there. I’m writing this to tell you which are the key pieces that you must use make it work, and to give you a minimal grounding in using them.

“MVVM” stands for “Model/View/ViewModel”.

The Model is the bare information your program deals with: Say, a list of names. That’s all it is. It doesn’t do much. It can be omitted in simpler cases.

The ViewModel has what the model has, but it adds some wiring that helps the user interface interact with it. If your program needs to keep track of a list of names, the ViewModel will have a collection of names. If you’ll need to let the user concentrate on a particular name at runtime, it might have a SelectedName property. A ViewModel can also expose “commands” which let a consumer do things more abstract than altering a property value: If you’ve got a public collection of names, a consumer can add a name to the list. But how does a consumer tell you to save the names to a file on disk? With a command. Rather than paste a mess of code into a button’s click handler, you write the operation as a Command, and bind a Button’s Command property to that command.

The ViewModel contains information, like the Model. You would  characteristically load a model from a disk or database, then initialize a ViewModel with that model. The ViewModel lets people do things that information, but it has no opinion about how that information will be communicated to the user. It doesn’t know the user interface exists.

One more important thing the ViewModel adds: Your ViewModel must fire notifications when its properties change. It does that by implementing the INotifyPropertyChanged interface, and using ObservableCollection to expose collections of values.

That brings us to the View.

You write a View in XAML. In the XAML, you “bind” properties of a viewmodel to properties of controls in the XAML. If you do a few things right on both ends, then when you change your ViewModel, those changes will be reflected in the user interface. When the user does things to the user interface, those changes will be reflected in the ViewModel.

The View kind of knows the ViewModel exists, in a vague way, but the coupling is loose. It may know that the ViewModel has a Name property and a Children collection; it doesn’t care about the exact type of the ViewModel, and it can be extremely open-minded about the exact types of Name and Children. This is what they call “duck typing“. “It should have a property called Name; get the value of that, whatever it is, turn it into a string, and stuff it in a TextBlock”.

The properties of the View and of the ViewModel are two completely different kinds of properties. To “bind” means that you are using XAML to create an instance of the Binding class, which climbs onto the control like an alien brain slug and acts as an intermediary between the ViewModel and the View. It does this with the help of two magic ponies named Framework Sparkle and Notification Rainbow.

XAML controls have a few special features:

  1. DependencyProperties. Dependency properties are not conventional C# properties. They have a lot more wiring behind the scenes. They’re a feature of any class which derives from DependencyObject. We’ll go into a lot more detail about these at some point in the future, but for now, the imporant point is that you can put a binding on them in XAML. Here, TextBox.Text is a DependencyProperty. You can put that Binding on it, and Framework Sparkle will get to work behind the scenes listening for updates to the Name property. She’ll also reach out and update the name property when the user types new text into that text box.Next question: Name property who? Name what? Where does the stupid pony go to look for Name?
    <TextBox Text="{Binding Name}" />
  2. FrameworkElement.DataContext. A Binding has a Source property. That’s where the Binding above goes to look for Name. But if you put a binding on a property of any object derived from FrameworkElement, it will by default use the DataContext property as the Source. DataContext is of type Object; it can be anything. The Binding will use reflection to find that “Name” property — any property called Name, just so long as it has a getter (it can’t be a field).And DataContext is inherited down the XAML visual tree. In the example above, that TextBlock’s DataContext is its parent’s DataContext, and so on. You can set a DataContext on your Window, and it’ll propagate down.Binding can use a path to a property of a property, as well. If Name is a string, it has a Length property. If there is no Name property or Name doesn’t have a Length property of its own, your program won’t crash. You’ll just see some exceptions written to the Output window in Visual Studio.
    <TextBlock Text="{Binding Name.Length}" />

Finally, Notification Rainbow gets into the act. We mentioned notifications above. If Name is a property of your ViewModel, and if your viewmodel properly implements INotifyPropertyChanged, then you can give Name a new value in your ViewModel and the UI will be updated.

public class ViewModel : INotifyPropertyChanged
{
    private String _name = "";

    public String Name {
        get { return _name; }
        set {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(String propName) {
        var handler = PropertyChanged;
        if (handler != null) {
           handler(this, new PropertyChangedEventArgs(propName));
        }
   }
}