Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to find code or a pre-packaged control that takes an object graph and displays the public properties and values of the properties (recursively) in a TreeView. Even a naive implementation is ok, I just need something to start with.

The solution must be in WPF, not winforms or com, etc...

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
149 views
Welcome To Ask or Share your Answers For Others

1 Answer

So I took parts from Chris Taylor's example and the structure of a codeproject article and merged them into this:

TreeView xaml:

<TreeView Name="tvObjectGraph" ItemsSource="{Binding FirstGeneration}" Margin="12,41,12,12" FontSize="13" FontFamily="Consolas">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <Setter Property="FontWeight" Value="Normal" />
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="FontWeight" Value="Bold" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Text="{Binding Name}" Grid.Column="0" Grid.Row="0" Padding="2,0" />
                <TextBlock Text="{Binding Type}" Grid.Column="1" Grid.Row="0" Padding="2,0" />
                <TextBlock Text="{Binding Value}" Grid.Column="2" Grid.Row="0" Padding="2,0" />
            </Grid>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

Wire-up code

void DisplayObjectGraph(object graph)
{
    var hierarchy = new ObjectViewModelHierarchy(graph);
    tvObjectGraph.DataContext = hierarchy;
}

ObjectViewModel.cs:

public class ObjectViewModel : INotifyPropertyChanged
{
    ReadOnlyCollection<ObjectViewModel> _children;
    readonly ObjectViewModel _parent;
    readonly object _object;
    readonly PropertyInfo _info;
    readonly Type _type;

    bool _isExpanded;
    bool _isSelected;

    public ObjectViewModel(object obj)
        : this(obj, null, null)
    {
    }

    ObjectViewModel(object obj, PropertyInfo info, ObjectViewModel parent)
    {
        _object = obj;
        _info = info;
        if (_object != null)
        {
            _type = obj.GetType();
            if (!IsPrintableType(_type))
            {
                // load the _children object with an empty collection to allow the + expander to be shown
                _children = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { new ObjectViewModel(null) });
            }
        }
        _parent = parent;
    }

    public void LoadChildren()
    {
        if (_object != null)
        {
            // exclude value types and strings from listing child members
            if (!IsPrintableType(_type))
            {
                // the public properties of this object are its children
                var children = _type.GetProperties()
                    .Where(p => !p.GetIndexParameters().Any()) // exclude indexed parameters for now
                    .Select(p => new ObjectViewModel(p.GetValue(_object, null), p, this))
                    .ToList();

                // if this is a collection type, add the contained items to the children
                var collection = _object as IEnumerable;
                if (collection != null)
                {
                    foreach (var item in collection)
                    {
                        children.Add(new ObjectViewModel(item, null, this)); // todo: add something to view the index value
                    }
                }

                _children = new ReadOnlyCollection<ObjectViewModel>(children);
                this.OnPropertyChanged("Children");
            }
        }
    }

    /// <summary>
    /// Gets a value indicating if the object graph can display this type without enumerating its children
    /// </summary>
    static bool IsPrintableType(Type type)
    {
        return type != null && (
            type.IsPrimitive ||
            type.IsAssignableFrom(typeof(string)) ||
            type.IsEnum);
    }

    public ObjectViewModel Parent
    {
        get { return _parent; }
    }

    public PropertyInfo Info
    {
        get { return _info; }
    }

    public ReadOnlyCollection<ObjectViewModel> Children
    {
        get { return _children; }
    }

    public string Type
    {
        get
        {
            var type = string.Empty;
            if (_object != null)
            {
                type = string.Format("({0})", _type.Name);
            }
            else
            {
                if (_info != null)
                {
                    type = string.Format("({0})", _info.PropertyType.Name);
                }
            }
            return type;
        }
    }

    public string Name
    {
        get
        {
            var name = string.Empty;
            if (_info != null)
            {
                name = _info.Name;
            }
            return name;
        }
    }

    public string Value
    {
        get
        {
            var value = string.Empty;
            if (_object != null)
            {
                if (IsPrintableType(_type))
                {
                    value = _object.ToString();
                }
            }
            else
            {
                value = "<null>";
            }
            return value;
        }
    }

    #region Presentation Members

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (_isExpanded != value)
            {
                _isExpanded = value;
                if (_isExpanded)
                {
                    LoadChildren();
                }
                this.OnPropertyChanged("IsExpanded");
            }

            // Expand all the way up to the root.
            if (_isExpanded && _parent != null)
            {
                _parent.IsExpanded = true;
            }
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                this.OnPropertyChanged("IsSelected");
            }
        }
    }

    public bool NameContains(string text)
    {
        if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Name))
        {
            return false;
        }

        return Name.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
    }

    public bool ValueContains(string text)
    {
        if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Value))
        {
            return false;
        }

        return Value.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}

ObjectViewModelHierarchy.cs:

public class ObjectViewModelHierarchy
{
    readonly ReadOnlyCollection<ObjectViewModel> _firstGeneration;
    readonly ObjectViewModel _rootObject;

    public ObjectViewModelHierarchy(object rootObject)
    {
        _rootObject = new ObjectViewModel(rootObject);
        _firstGeneration = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { _rootObject });
    }

    public ReadOnlyCollection<ObjectViewModel> FirstGeneration
    {
        get { return _firstGeneration; }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...