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.