BENBUN Coder
BENBUN Coder

Reputation: 4881

Treeview with multi colour node text

I have a project that requires the use of a treeview control. The control must have the ability for the text on each node to be formatted so the text can be multi coloured. This is best shown by the treeview used in outlook - see pic )

I historically have a windows forms control that I created to do this, my question is how easy is this to do in WPF without having to use 3rd party controls?

enter image description here

Upvotes: 2

Views: 2201

Answers (1)

Fede
Fede

Reputation: 44048

I historically have a windows forms control that I created to do this

Forget winforms, it's a dinosaur technology that has not been improved since 2007, it is not intended to create Rich UIs (only poor ones), and that does not support anything and forces you to write too much code and achieve less. It does not support any kind of customization and is slow as hell.

All the horrible hacks required in winforms to do anything (such as "owner draw" and "P/Invoke", whatever that means) are completely irrelevant and unneeded in WPF.

I dont really want to invest a lot of time moving of winforms to wpf if what I want to do is either not possible or too difficult

People are doing things like this in WPF, which are completely impossible in winforms, so what you're talking about here is really a "piece of cake" for WPF.

First of all, if you're getting into WPF, you must forget the traditional too-much-code-for-anything winforms approach and understand and embrace The WPF Mentality.

Here is how you implement that in WPF:

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <TreeView ItemsSource="{Binding}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                <DockPanel>

                    <!-- this Image Control will display the "icon" for each Tree item -->
                    <Image Width="18" Height="16" Source="{Binding ImageSource}"
                           DockPanel.Dock="Left" Margin="2"/>

                    <!-- this TextBlock will show the main "Caption" for each Tree item -->
                    <TextBlock Text="{Binding DisplayName}" FontWeight="Bold"
                               VerticalAlignment="Center"
                               x:Name="DisplayName" Margin="2"/>

                    <!-- this TextBlock will show the Item count -->
                    <TextBlock Text="{Binding ItemCount, StringFormat='({0})'}"
                               VerticalAlignment="Center" Margin="2" x:Name="ItemCount">
                        <TextBlock.Foreground>
                            <SolidColorBrush Color="{Binding ItemCountColor}"/>
                        </TextBlock.Foreground>
                    </TextBlock>
                </DockPanel>

                <HierarchicalDataTemplate.Triggers>
                    <!-- This DataTrigger will hide the ItemCount text 
                         and remove the Bold font weight from the DisplayName text 
                         when ItemCount is zero -->
                    <DataTrigger Binding="{Binding ItemCount}" Value="0">
                        <Setter TargetName="ItemCount" Property="Visibility" Value="Collapsed"/>
                        <Setter TargetName="DisplayName" Property="FontWeight" Value="Normal"/>
                    </DataTrigger>
                </HierarchicalDataTemplate.Triggers>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
</Window>

Code Behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = DataSource.GetFolders();
    }
}

Data Item:

public class Folder
{
    public Folder(string displayName)
    {
        ImageSource = DataSource.Folder1;
        Children = new List<Folder>();
        ItemCountColor = "Blue";
        DisplayName = displayName;
    }

    public Folder(string displayName, int itemCount): this(displayName)
    {
        ItemCount = itemCount;
    }

    public string DisplayName { get; set; }

    public int ItemCount { get; set; }

    public List<Folder> Children { get; set; }

    public string ItemCountColor { get; set; }

    public string ImageSource { get; set; } 
}

DataSource class (A lot of boilerplate code that generates the tree entries and is not really part of the WPF side of things):

public static class DataSource
{
    public const string Folder1 = "/folder1.png";
    public const string Folder2 = "/folder2.png";
    public const string Folder3 = "/folder3.png";
    public const string Folder4 = "/folder4.png";

    public static List<Folder> GetFolders()
    {
        return new List<Folder>
            {
                new Folder("Conversation History"),
                new Folder("Deleted Items",102)
                    {
                        ImageSource = Folder2,
                        Children =
                            {
                                new Folder("Deleted Items #1"),
                            }
                    },
                new Folder("Drafts",7)
                    {
                        ImageSource = Folder3,
                        ItemCountColor = "Green",
                    },
                new Folder("Inbox",7)
                    {
                        ImageSource = Folder4,
                        Children =
                            {
                                new Folder("_file")
                                    {
                                        Children =
                                            {
                                                new Folder("__plans"),
                                                new Folder("_CEN&ISO", 5),
                                                new Folder("_DDMS", 1)
                                                {
                                                    Children =
                                                        {
                                                            new Folder("Care Data Dictionary"),
                                                            new Folder("HPEN"),
                                                            new Folder("PR: Data Architecture"),
                                                            new Folder("PR: Hospital DS", 2),
                                                            new Folder("RDF"),
                                                            new Folder("Schemas"),
                                                            new Folder("Subsets"),
                                                        }
                                                },
                                                new Folder("_Interop")
                                                {
                                                    Children =
                                                        {
                                                            new Folder("CDSA", 1),
                                                            new Folder("CPIS", 2),
                                                            new Folder("DMIC"),
                                                            new Folder("EOL"),
                                                            new Folder("... And so on..."),
                                                        }
                                                }

                                            }
                                    }
                            }
                    }

            };
    }
}

Result:

enter image description here

  • As you can see, this full working sample consists of 30 lines of XAML, 1 line of C# code behind, and a simple POCO class that represents the folder structure, which consists of string, bool, int and List<T> properties and does not have any dependencies on the UI framework at all, plus the DataSource boilerplate, that does not have anything to do with WPF anyways.
  • Notice how my C# code is clean and simple and beautiful and does not have any horrible "owner draw" stuff or anything like that.
  • Also notice how the Data/Logic are completely decoupled from the UI, which gives you a HUGE amount of flexibility, scalability and maintainability. You could completely rewrite the UI into a totally different thing without changing a single line of C# code.
  • There is 1 line of Code behind, which sets the DataContext to a List<Folder>, the rest is achieved via DataBinding into the HierarchicalDataTemplate that defines the Visual structure of the Tree items.
  • This is the WPF way, to use DataBinding to simplify your life instead of a bunch of useless boilerplate piping to pass data between the UI and the Data Model.
  • Keep in mind that you can put literally anything inside the DataTemplate, not just text, not just read-only content. You can even put editable controls or even Video inside each tree item. the WPF Content Model does not suffer from the huge limitations imposed by other technologies.
  • WPF Rocks - just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself, you will need to add the png files and set their Build Action to Resource in your project:

enter image description here

Once you know WPF, XAML and MVVM, you will NEVER want to go back to winforms again, and you'll realize how much valuable time you've lost all these years using dead technology.

Upvotes: 2

Related Questions