Programmatically aded items are not visible in RadTreeView.

14 posts, 0 answers
  1. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 26 Apr Link to this post

    Hi. I write WPF MVVM Prism 6 modular application where I try to use RadTreeView. I add items to RadTreeView through ObservableCollection (to which RadTreeView is bound) using the "Add" method of the collection. I begin to add items to root item of the tree. After completion of code of adding of a child item to tree root item the "+" appears on the left of this root item. When I click on this "+" by the mouse it turns into "-" but added item is not visible in the RadTreeView. Below is the complete code of the class representing the type of element added (as child) to the tree root item:

    public class Group : ProfileElementType, IEditableObject
    {
        /// <summary>
        /// Device profile elements group structure.
        /// </summary>
        struct GroupData
        {
            /// <summary>
            /// Group name (for example, "RealtimeClock).
            /// </summary>
            internal string name;
            /// <summary>
            /// Brief description
            /// </summary>
            internal string description;
            /// <summary>
            /// Child elements collection.
            /// </summary>
            internal ObservableCollection<ProfileElementType> _childProfileElenents;
        }
     
        #region Fields
        /// <summary>
        /// Group data after their changing.
        /// </summary>
        private GroupData _newGroupData;
        /// <summary>
        /// Old data buffer.
        /// </summary>
        private GroupData _backupGroupData;
        /// <summary>
        /// Edit mode flag.
        /// </summary>
        private bool _isEditMode = false;
         
        #endregion
     
        #region Constructor
        /// <summary>
        /// Creates instance of Group.
        /// </summary>
        public Group()
        {
            this.ElementType = this.GetType();
            this.ElementTypeName = this.ElementType.Name;
            this._newGroupData = new GroupData();
            this._newGroupData.name = string.Empty;
            this._newGroupData.description = string.Empty;
            this.ChildProfileElenents = new ObservableCollection<ProfileElementType>();
        }
        #endregion
     
        #region Properties
        /// <summary>
        /// Gets or sets group name.
        /// </summary>
        [Display(Description = "The name of the group."]
        public string Name
        {
            get { return this._newGroupData.name; }
            set { this._newGroupData.name = value; }
        }
        /// <summary>
        /// Brief description of the group.
        /// </summary>
        [Display(Description = "Brief description of group."]
        public string Description
        {
            get { return this._newGroupData.description; }
            set { this._newGroupData.description = value; }
        }
        /// <summary>
        /// Group child elements collection.
        /// </summary>
        [Browsable(false)]
        public ObservableCollection<ProfileElementType> ChildProfileElenents { get; set; }
        #endregion
     
        #region Methods
        /// <summary>
        /// String representation of instance of Group.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringWriter stringWriter = new StringWriter();
            stringWriter.Write(this.ElementTypeName);
            stringWriter.Write(": ");
            stringWriter.Write(this.Name);
            stringWriter.Write(" ");
            stringWriter.Write(this.Description);
            return stringWriter.ToString();
        }
        #endregion
     
        #region IEditableObject implementation
        /// <summary>
        /// Begins to edit.
        /// </summary>
        public void BeginEdit()
        {
            if(!this._isEditMode)
            {
                this._backupGroupData = this._newGroupData;
                this._isEditMode = true;
            }
        }
        /// <summary>
        /// Cancels edit results.
        /// </summary>
        public void CancelEdit()
        {
            if (this._isEditMode)
            {
                this._newGroupData = this._backupGroupData;
                this._isEditMode = false;
            }
        }
        /// <summary>
        /// Accepts edit results.
        /// </summary>
        public void EndEdit()
        {
            if (this._isEditMode)
            {
                this._backupGroupData = new GroupData();
                this._isEditMode = false;
            }
        }
        #endregion
    }

    Below isProfileElementType base class. It is base for both Gruop and Register classes.

    public class ProfileElementType : IProfileElementType
    {
       #region Fields
       /// <summary>
       /// Profile element type - Register or Group (група).
       /// </summary>
       private Type _elementType;
       /// <summary>
       /// Profile element type name - "Register" or "Group".
       /// </summary>
       private string _elementTypeName;
       #endregion
     
       #region Properties
       /// <summary>
       /// Gets or sets profile element type name string - "Register" or "Group".
       /// </summary>
       [Browsable(false)]
       public string ElementTypeName
       {
           get { return this._elementTypeName; }
           set { this._elementTypeName = value; }
       }
       #endregion
     
       #region IProfileElementType implementation
       /// <summary>
       /// Gets or sets profile element type - Register or Group.
       /// </summary>
       [Browsable(false)]
       public Type ElementType
       {
          get { return this._elementType; }
          set { this._elementType = value; }
       }
        #endregion
     }

    Below is definition of instance of group (root item element) in view model:

    /// <summary>
    /// The tree root element. Its child elements collection is for organization of the device profile tree.
    /// </summary>
    private Group _profileTreeRoot;
    public Group ProfileTreeRoot
    {
       get { return this._profileTreeRoot; }
       set { this.SetProperty(ref this._profileTreeRoot, value); }
    }
    // This code in view model constructor:
    this.ProfileTreeRoot = new Group();

    In my application I have two classes of tree nodes: "Group" and "Register". I won't show Register class definition (because I'm experimenting with Group class now) but ItemStyleSelector class is below:

    public class ItemStyleSelector : StyleSelector
    {
       public override Style SelectStyle(object item, DependencyObject container)
       {
          if (item is Group)
             return this.GroupStyle;
          else if (item is Register)
             return this.RegisterStyle;
          return null;
       }
     
       public Style GroupStyle { get; set; }
       public Style RegisterStyle { get; set; }
    }

    Below is XAML of view in wich RadTreView is located (this XAML is shortened here in the post):

            <Grid.Resources>
                <!--Profile elements group style-->
                <Style x:Key="GroupItemStyle" TargetType="telerik:RadTreeViewItem">
                    <Setter Property="Foreground" Value="Black" />
                    <Setter Property="FontStyle" Value="Normal" />
                    <Setter Property="FontWeight" Value="Normal"/>
                    <Setter Property="DefaultImageSrc" Value="/Images/Group.png" />
                    <Style.Triggers>
                        <Trigger Property="IsExpanded" Value="True">
                            <Trigger.Setters>
                                <Setter Property="Foreground" Value="ForestGreen" />
                                <Setter Property="FontStyle" Value="Italic" />
                                <Setter Property="FontWeight" Value="SemiBold"/>
                            </Trigger.Setters>
                        </Trigger>
                    </Style.Triggers>
                </Style>
                <!--Device register style (just in case only)-->
                <Style x:Key="RegisterItemStyle" TargetType="telerik:RadTreeViewItem">
                    <Setter Property="Foreground" Value="Black" />
                    <Setter Property="FontStyle" Value="Normal" />
                    <Setter Property="DefaultImageSrc" Value="/Images/File.png" />
                </Style>
                <!--Profile elements group template-->
                <HierarchicalDataTemplate DataType="{x:Type model:Group}" ItemsSource="{Binding ChildProfileElenents}">
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
                <!--Register template (just in case only)-->
                <DataTemplate DataType="{x:Type model:Register}">
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
                <!--Style selector-->
                <local:ItemStyleSelector x:Key="ItemStyleSelector"
                                           GroupStyle="{StaticResource GroupItemStyle}"
                                           RegisterStyle="{StaticResource RegisterItemStyle}"/>
            </Grid.Resources>
     
    <!--Below is RadTreeView markup itself:-->
    <telerik:RadTreeView x:Name="DeviceProfileTree" Grid.Row="1" Grid.Column="0">
        <telerik:RadTreeViewItem Header="{Binding RootHeader}"
                                         ItemContainerStyleSelector="{StaticResource ItemStyleSelector}"
                                         ItemsSource="{Binding ProfileTreeRoot.ChildProfileElenents}"/>
        <telerik:EventToCommandBehavior.EventBindings>
            <telerik:EventBinding
                Command="{Binding HandleProfileElementSelectionCommand}"
                EventName="Selected"
                PassEventArgsToCommand="True"/>
        </telerik:EventToCommandBehavior.EventBindings>
    </telerik:RadTreeView>

    Below is RootHeader definition in the view model:

    private string _rootHeader;
    public string RootHeader
    {
       get { return this._rootHeader; }
       set { this.SetProperty(ref this._rootHeader, value); }
    }
    // This code in the view model constructor:
    this.RootHeader = "Flow Meter";

    Below is HandleProfileElementSelectionCommand command method (though here it does not matter as it seems to me):

    private void handleProfileElementSelection(object parameter)
    {
        if (!this._isProfileElementSelected)
           this._isProfileElementSelected = true;
        RadRoutedEventArgs args = parameter as RadRoutedEventArgs;
        if ((args.Source as RadTreeViewItem).IsRootItem)
           this._isRootItemSelected = true;
        else
        {
           if (this._isRootItemSelected)
              this._isRootItemSelected = false;
        }
    }

    Below is command method for adding of new group:

    private void addNewGroup(object parameter)
    {
       // If parent element was selected in the RadTreeView:
       if (this._isProfileElementSelected)
       {
          // Set property (to which RadPropertyGrid is bound in view) to new Group.
          this.ProfileElement = new Group();
          this._isAddNewItem = true;
       }
    }

    Below is ProfileElement definition in the view model:

    /// <summary>
    /// Device profile element. Can be as Group or as Register that are inherited from it.
    /// </summary>
    private ProfileElementType _profileElement;
    public ProfileElementType ProfileElement
    {
       get { return this._profileElement; }
       set { this.SetProperty(ref this._profileElement, value); }
    }

    Below is command method for saving added Group instance in the RadTreeView:

    private void saveChanges(object parameter)
    {
        // If new profile element is added to the device profile tree.
        if(this._isAddNewItem)
        {
            // If this element is added as the child of the tree root element.
            if (this._isRootItemSelected)
            {
                if (this.ProfileElement.ElementTypeName == "Group")
                   this.ProfileTreeRoot.ChildProfileElenents.Add(this.ProfileElement as Group);
                else
                   this.ProfileTreeRoot.ChildProfileElenents.Add(this.ProfileElement as Register);
            }
            else
            {
                // Add to selected group inside root element (not implemented now)
            }
            this._isAddNewItem = false;
        }
        else
        {
            // Save changes for existed selected device profile element (not implemented now).
        }
    }

    As you can see adding to RadTreeView performs via ObservableCollection to which root node of RadTreeView is bound. Why added child element is not visible? Please help me to eliminate this error. In attach files there are solution structure (in Snapshot_10.png file), RadTreeView in collapsed status after adding the element (in Collapsed_Tree.png file) and RadTreeView in unwrapped status (in Unwrapped_Tree.png file). I hope for your help very much.

  2. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 26 Apr in reply to Eugene Link to this post

    Here I'd like to add that each Group in the profile tree can involve both Register instances and Group instances. That is, the depth of nesting in the each group may be arbitrary.
  3. UI for WPF is Visual Studio 2017 Ready
  4. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 27 Apr in reply to Eugene Link to this post

    I add a little again. My application is modular. DeviceProfile project in the picture in Snapshot.png is Prism.Module, ConnectDisconnect is Prism Module too, CommonClassLibrary is common library in the solution, FlowmeterConfigurator is the Shell project in the solution.
  5. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 27 Apr Link to this post

    Please help!
  6. Ivan
    Admin
    Ivan avatar
    44 posts

    Posted 27 Apr Link to this post

    Hello Eugene,

    With the code you've sent us I created a new project but I couldn't reproduce the issue on my side. You can refer to the attached project for a working sample. If you select the root item, then type some text in the text box at the bottom and click the button, the new node will be added to the selected parent and visualized as expected.

    In case you are using implicit styles can you check if the custom styles that targets RadTreeViewItem are based on the default styles from the theme?
    <Style x:Key="GroupItemStyle" TargetType="telerik:RadTreeViewItem" BasedOn="{StaticResource RadTreeViewItemStyle}">
    <Style x:Key="RegisterItemStyle" TargetType="telerik:RadTreeViewItem" BasedOn="{StaticResource RadTreeViewItemStyle}">
    For more information you can check the Basing styles on theme style section from the implicit styles help article

    If the attached project does not help you to resolve the issue, can you modify it and send it back demonstrating the undesired behavior?

    Regards,
    Ivan
    Telerik
    Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
  7. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 27 Apr in reply to Ivan Link to this post

    Thank you very much, Ivan. I'll test the solution from your "TreeView.zip" archive and reply you more circumstantially via this post tomorrow 28.04.2016. Thank you very much for your help and support. So tomorrow I'll write you.

    Sincerely yours    Eugene. 

  8. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 28 Apr in reply to Eugene Link to this post

    Hi, Ivan. Your application works nice. But there is one thing that leads me to suspicion. This is the following: You use the standart WPF Window to placement RadTreeView  (see the partial snippet below):

    <Window x:Class="wpf_TreeView_1030116.MainWindow"
            xmlns:local="clr-namespace:wpf_TreeView_1030116"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
     
        <FrameworkElement.DataContext>
            <local:MainViewModel />
        </FrameworkElement.DataContext>
    . . . . . . . . . . . . . . . . . . . .
    </Window>

    This window is the application main window. It is in the main started project of the solution. In its turn, I use Prism UserControl(WPF) as view for placement of RadTreeView (please see the partial snippet below):

    <UserControl x:Class="DeviceProfile.Views.DeviceProfileView"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:prism="http://prismlibrary.com/"            
                 xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                 xmlns:local="clr-namespace:DeviceProfile"
                 xmlns:model="clr-namespace:DeviceProfile.Models"
                 prism:ViewModelLocator.AutoWireViewModel="True">

    . . . . . . . . . . . . . . . . . . . . . . . . .

    </UserControl>

    This Prism UserControl(WPF) is defined in separate Prism Module (separate project) in my application solution not in the main started project where applic ation main window is. So, applied to our case, my solution has two projects: Shell (main window) project (that is the main started project in solution) and Prism Module project (where abovementioned Prism UserControl is). During application lifetime this Prism UserControl(WPF) is placed in Shell (application main window that is telerik:RadWindow) region that specified as MainContentRegion. Below is the partual snippet from Shell (main window) markup:

    <telerik:RadWindow x:Class="FlowmeterConfigurator.Views.Shell"
            xmlns:prism="http://prismlibrary.com/"
            xmlns:views="clr-namespace:FlowmeterConfigurator.Views"
            prism:ViewModelLocator.AutoWireViewModel="True"
            Header="{Binding Title}" Height="350" Width="525"
            WindowStartupLocation="CenterScreen">
    . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
         <ContentControl prism:RegionManager.RegionName="MainContentRegion"
                Grid.Column="1" Grid.Row="2" Margin="5,0,5,5" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"/>
    . . . . . . . . . .
    </telerik:RadWindow>

    So I'm afraid that the placement of RadTreeView not in main window directly prevents of displaing of its child elements. However, other telerik components (such as: RadChartView, RadCombobox, RadPropertyGrid, RadButton etc.) are displayed nice in Prism UserControl(WPF) controls that are in separate Prism Modules in my application. What do you think, can placement of RadTreView not in the Shell but in Prism UserControl(WPF) prevent of displaing of child elements of RadTreView? Please reply.

  9. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 28 Apr in reply to Eugene Link to this post

    Here is my App.xaml:

    <Application x:Class="FlowmeterConfigurator.App"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                 xmlns:local="clr-namespace:FlowmeterConfigurator"
                 xmlns:views="clr-namespace:FlowmeterConfigurator.Views">
        <Application.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/System.Windows.xaml" />
                    <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.xaml" />
                    <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.Navigation.xaml" />
                    <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.Docking.xaml" />
                </ResourceDictionary.MergedDictionaries>
                <Style TargetType="views:Shell" BasedOn="{StaticResource RadWindowStyle}" />
            </ResourceDictionary>
        </Application.Resources>
    </Application>

    Should I correct something in Prism UserControl XAML markup where RadTreView is?

  10. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 28 Apr in reply to Eugene Link to this post

    And one more thing. In my application ProfileElement property

    private ProfileElementType _profileElement;
    public ProfileElementType ProfileElement
    {
       get { return this._profileElement; }
       set { this._profileElement = value; }
    }

    that I add as child element to RadTreeView root element' collection

    private void addNewGroup(object parameter)
    {
       if (this._isProfileElementSelected)
       {
          this.ProfileElement = new Group();
          this._isAddNewItem = true;
       }
    }
     
    private void saveChanges(object parameter)
    {
       if(this._isAddNewItem)
       {
          if (this._isRootItemSelected)
          {
             if (this.ProfileElement.ElementTypeName == "Group")
                this.ProfileTreeRoot.ChildProfileElenents.Add(this.ProfileElement as Group);
             else
                this.ProfileTreeRoot.ChildProfileElenents.Add(this.ProfileElement as Register);
          }
          . . . . . .
          this._isAddNewItem = false;
       }
       . . . . . .
    }

    is binding source for RadPropertyGrid (I set properties values of new added Group via RadPropertyGrid). And I endeavor to add this element to RadTreeView as child. May be this fact (that ProfileElement property is another element' binding source) prevents  its visualizing in RadTreeView after adding there?

  11. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 28 Apr in reply to Eugene Link to this post

    Just now I check in debug mode if added items are in RadTreeView' root
    item. Yes they are there. HasItems property of RadTreeView' root item
    has 'true' value and Items property has the collection of added items
    (Group instances). But these added items are not displayed on the
    screen.
  12. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 28 Apr in reply to Eugene Link to this post

    I use RadtreeViewItemStyle as you adviced and it helped me partly.
    Partly - because now in child elements of the tree only captions are
    displayed (without icons) as you can see in "Groups.png" attached file.
    But how to display icons for child elements?
  13. Ivan
    Admin
    Ivan avatar
    44 posts

    Posted 02 May Link to this post

    Hi Eugene,

    I am glad to hear that you managed to resolve this and now the items are visible.  As for the missing images there are few things that you can double check on your side.
    • Check if the missing image is included in the project and if its Build Action is set to Resource.
    • Check if the UI virtualization of the treeview is enabled. In this case, setting a path directly in xaml won't work. If this is your case you can define the path to the image in the view model of the tree node and bind it to the DefaultImageSrc in the style.
    • If the treeview is inside a user control from another assembly, the relative path won't work either. In this case you can use slightly different syntax to set the image path. 

      DefaultImageSrc="/MyOtherProject;component/Images/File.png".

    Regards,
    Ivan
    Telerik
    Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
  14. Eugene
    Eugene avatar
    83 posts
    Member since:
    Jan 2016

    Posted 02 Jun in reply to Ivan Link to this post

    You've written:   DefaultImageSrc="/MyOtherProject;component/Images/File.png". 'component' - what does it mean here?
  15. Dinko
    Admin
    Dinko avatar
    206 posts

    Posted 07 Jun Link to this post

    Hi Eugene,

    The ;component  in the URIs path, specifies that the assembly being referred to is referenced from the local assembly. You can take a look at the Pack URIs in WPF MSDN help article where you can find more information.

    Regards,
    Dinko
    Telerik
    Do you need help with upgrading your AJAX, WPF or WinForms project? Check the Telerik API Analyzer and share your thoughts.
Back to Top
UI for WPF is Visual Studio 2017 Ready