Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The problem with the <code>GridViewRowPresenter</code> solution is that the tree is inextricable from the other columns. I figure you need it to be separate so that you can put the horizontal-only <code>ScrollViewer</code> around the columns, and I doubt this is easy (if possible) to do to the project in the article you linked.</p> <p>This project that I slapped together to figure something out is quite rough around the edges. There are a number of issues you would need to work out separately that I didn't fine-tune:</p> <ol> <li>Templates and styling so that lines match up, and other visual tweaks.</li> <li>Re-introducing the <code>GridView</code> aspects of the linked project for headers and the columns.</li> <li>A splitter for adjusting the size of the first column (containing the tree).</li> </ol> <p>Like the article project, I used a tree of <code>Type</code> objects as the data source.</p> <p>The crux of getting this to work was wrapping the data objects in an <code>ExpandingContainer</code> object. The important things about this INPC class is the <code>IsExpanded</code> property (for binding) and the collection of children:</p> <pre><code>public class ExpandingContainer : INotifyPropertyChanged { public object Payload { get; private set; } public ObservableCollection&lt;ExpandingContainer&gt; Children { get; private set; } public ExpandingContainer( object payload ) { ... } private bool _isexpanded; public bool IsExpanded { get { return _isexpanded; } set { if ( value == _isexpanded ) return; _isexpanded = value; PropertyChanged.Notify( () =&gt; IsExpanded ); } } public event PropertyChangedEventHandler PropertyChanged = (o,e) =&gt; {}; } </code></pre> <p>As for the XAML, first let's get some resources out of the way:</p> <pre><code>&lt;!-- bind ExpandingContainer.IsExpanded to TreeViewItem.IsExpanded --&gt; &lt;Style TargetType="TreeViewItem"&gt; &lt;Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /&gt; &lt;/Style&gt; &lt;!-- for binding ExpandingContainer.IsExpanded to visibility later --&gt; &lt;BooleanToVisibilityConverter x:Key="boolvis" /&gt; &lt;!-- the TreeViewItems should display the Type's name --&gt; &lt;HierarchicalDataTemplate DataType="{x:Type loc:ExpandingContainer}" x:Key="treeViewSide" ItemsSource="{Binding Children}"&gt; &lt;TextBlock Text="{Binding Payload.Name}" /&gt; &lt;/HierarchicalDataTemplate&gt; &lt;!-- the column side are naively simple, the ItemsControl of children has its visibility bound to ExpandingContainer, but the "columns" are just StackPanels of TextBlocks --&gt; &lt;HierarchicalDataTemplate DataType="{x:Type loc:ExpandingContainer}" x:Key="columnSide"&gt; &lt;StackPanel&gt; &lt;StackPanel.Resources&gt; &lt;Style TargetType="TextBlock"&gt; &lt;Setter Property="Margin" Value="10,0" /&gt; &lt;/Style&gt; &lt;/StackPanel.Resources&gt; &lt;StackPanel Orientation="Horizontal"&gt; &lt;TextBlock Text="{Binding Payload.IsAbstract}" /&gt; &lt;TextBlock Text="{Binding Payload.Namespace}" /&gt; &lt;TextBlock Text="{Binding Payload.GUID}" /&gt; &lt;/StackPanel&gt; &lt;ItemsControl ItemsSource="{Binding Children}" Visibility="{Binding IsExpanded, Converter={StaticResource boolvis}}" /&gt; &lt;/StackPanel&gt; &lt;/HierarchicalDataTemplate&gt; &lt;!-- a style can't refer to itself, so this was just to apply it to all ItemsControls --&gt; &lt;Style TargetType="ItemsControl"&gt; &lt;Setter Property="ItemTemplate" Value="{StaticResource columnSide}" /&gt; &lt;/Style&gt; </code></pre> <p>I originally tried nesting the horizontal-only <code>ScrollViewer</code> containing the right columns inside the vertical-only <code>ScrollViewer</code> that was responsible for the <code>TreeView</code>, but that produced the strange requirement that you had to scroll to the bottom to scroll horizontally. So I separated them further, placing the <code>ScrollViewer</code>s side-by-side.</p> <p>To keep the vertical scrollbar on the far right, I hid both scrollbars around the <code>TreeView</code> and use only the scrollbars around the columns. Syncing the vertical scrolling is done in code-behind, but for a more MVVM way to do it, you could make an attached behavior to facilitate binding them to each other.</p> <pre><code>&lt;DockPanel&gt; &lt;ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" DockPanel.Dock="Left" Name="treescroller"&gt; &lt;TreeView ItemsSource="{Binding Items}" ItemTemplate="{StaticResource treeViewSide}" Padding="0,0,0,20"&gt; &lt;/TreeView&gt; &lt;/ScrollViewer&gt; &lt;ScrollViewer Name="columnscroller" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" ScrollChanged="columnscroller_ScrollChanged"&gt; &lt;ItemsControl ItemsSource="{Binding Items}" /&gt; &lt;/ScrollViewer&gt; &lt;/DockPanel&gt; </code></pre> <p>And lastly, the important bit of the code-behind (minus making the data objects and setting the <code>DataContext</code> property):</p> <pre><code>private void columnscroller_ScrollChanged( object sender, ScrollChangedEventArgs e ) { treescroller.ScrollToVerticalOffset( columnscroller.VerticalOffset ); } </code></pre> <p>Hope it helps, or at least provides a different perspective.</p> <p><em>If I really needed a good one that filled every need I could think of for a hybrid <code>TreeView</code>+<code>ListView</code>, I'd probably look at professional controls first before spending the necessary time to polish a home-grown solution. This kind of thing is better when the requirements for such display are simple.</em></p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload