0
votes

I wanna to bind two or more class of data as TreeViewItem. For a directly example: file and folder contained in another folder but you don't know the struct at first. I wanna realise it with WPF treeview. But treeview can just bind only one type of node with HierarchicalDataTemplate(Such as my code).

<DockPanel SizeChanged="FrameworkElement_OnSizeChanged">
    <DockPanel.Resources>
        <HierarchicalDataTemplate x:Key="NodeDataTemplate" 
                                  DataType="{x:Type sideList:SideListNode}" 
                                  ItemsSource="{Binding Path=Children}">

            <StackPanel Orientation="Horizontal">
                <TextBlock VerticalAlignment="Center" x:Name="DisplayName"  Text="{Binding DisplayName}" ></TextBlock>
            </StackPanel>
            <HierarchicalDataTemplate.Triggers>
                <DataTrigger Binding="{Binding Path=}" Value="0">
                    <Setter TargetName="DisplayName" Property="Background" Value="Red">
                    </Setter>
                </DataTrigger>
            </HierarchicalDataTemplate.Triggers>
        </HierarchicalDataTemplate>
    </DockPanel.Resources>

    <TreeView  
        x:Name="SideList" DockPanel.Dock="Left"  MinWidth="200" MaxWidth="250"  Background="Aqua"
              ItemTemplate="{StaticResource NodeDataTemplate}">

    </TreeView>

But of course there some different between folder and file. Some distingct property for each others. Now we need bind two type for create nodes. One for file and one for folder. How we can do that?

2
can you give a sample of actual data? - RadioSpace

2 Answers

1
votes

all possible classes could derive from an interface such as

public Interface IGeneralInfo
{
    string info1{get;}
    string info2{get;}
    string info3{get;}
}

then bind to interface type

EDIT

here is all I can do with out more detail as to your actual data

in MainWindow.XAML

<Window x:Class="DUALBIND.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">
<Grid>
    <StackPanel>
        <Button x:Name="button1" Click="button1_Click" Content="ADD (this is set up for you to test)" FontFamily="Courier New" FontSize="16"/>
    <ListBox ItemsSource="{Binding}" x:Name="infolist">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Info1}"/>
                        <TextBlock Text=" :: "/>
                        <TextBlock Text="{Binding Info2}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>

    </ListBox>
    </StackPanel>
</Grid>
</Window>

in MainWindows.XAML.cs (did not take time to remove usings from project)

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;//<-------- notice this
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace DUALBIND
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    static Random r = new Random();//best use of random

    ObservableCollection<IGeneralInfo> generalinfo = new ObservableCollection<IGeneralInfo>();
    public MainWindow()
    {
        InitializeComponent();

        infolist.DataContext = generalinfo;

        generalinfo.Add(new OBJA());
        generalinfo.Add(new OBJA());
        generalinfo.Add(new OBJA());
        generalinfo.Add(new OBJB(7));
        generalinfo.Add(new OBJB(6));
        generalinfo.Add(new OBJB(12345));

    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        /*this was set up for you to play with*/           

        MessageBox.Show("You get to mess with this");
        generalinfo.Add(new OBJB(MainWindow.r.Next()));
    }
}
}

in IGeneralInfo.cs

using System;

namespace DUALBIND
{
interface IGeneralInfo
{

    string Info1 {get; }
    string Info2 { get; }
}

class OBJA : IGeneralInfo
{

    public string Info1
    {
        get { return "Info1 is used"; }
    }

    public string Info2
    {
        get { return "Info2 is used"; }
    }
}

class OBJB : IGeneralInfo 
{
    int x;

    public OBJB(int X)
    {
        x = X;
    }

    public string Info1
    {
        get { return "" + x; }
    }

    public string Info2
    {
        get { return ""; /*handle this how you want*/}
    }
}
}

all working code on my end (unless copy and paste failed me)

0
votes

The alternative to having a common base class would be using some ItemTemplateSelector. I have written a generic one:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Collections;
using System.Windows.Media;

namespace Utils
{
    /// <summary>
    /// DataType cannot be null -> define Null type
    /// </summary>
    public class Null
    {
    }

    /// <summary>
    /// Generic template selector selecting the template according to the type
    /// </summary>
    [ContentProperty("Items")]
    public class GenericDataTemplateSelector : DataTemplateSelector
    {
        /// <summary>
        /// Select a template according to the type of the object.
        /// </summary>
        /// <param name="item">The item for which a template shall be selected.</param>
        /// <param name="container">The container, in which the Template shall be used.</param>
        /// <returns>The appropriate template, if found; else null.</returns>
        public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
        {
            DataTemplate defaultTemplate = null;
            if (item != null)
            {
                foreach (var dataTemplate in Items.OfType<DataTemplate>())
                {
                    if ((Type)dataTemplate.DataType == null || (Type)dataTemplate.DataType == typeof(Null))
                    {
                        defaultTemplate = dataTemplate;
                    }
                    else if (((Type)dataTemplate.DataType).IsInstanceOfType(item))
                    {
                        return dataTemplate;
                    }
                }
            }
            return defaultTemplate;
        }

        /// <summary>
        /// The list of templates.
        /// </summary>
        public IList Items
        {
            get
            {
                if (_items == null)
                {
                    _items = new ArrayList();
                }
                return _items;
            }
            set
            {
                Items.Clear();
                foreach (var item in value)
                {
                    if (item is DataTemplate)
                    {
                        _items.Add(item);
                    }
                }
            }
        }
        IList _items;
    }
}

Usage:

    <utils:GenericDataTemplateSelector x:Key="TreeViewTemplateSelector">
        <HierarchicalDataTemplate DataType="{x:Type Type1}">
             ...
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type Type2}">
             ...
        </HierarchicalDataTemplate>

        <DataTemplate DataType="{x:Type utils:Null}">
             ...
        </DataTemplate>

    </utils:GenericDataTemplateSelector>