TreeViewItem expaning like a chain-event - c#

In my WPF application, if I open one of the treeviewitems to display all the child items, it goes on a chain event and slowly starts opening all the parent items. For example if I opened the Red, it'd slowly open the blue's until they are all open;
My TreeView Code;
<TreeView x:Name="tvTagList" Margin="15, 40, 15, 50" SelectedItemChanged="tvTagList_SelectedTagChanged" ItemsSource="{Binding}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="{x:Null}" BorderBrush="{DynamicResource ExtryzeAccentBrushSecondary}" BorderThickness="2" ScrollViewer.CanContentScroll="True" Foreground="White"
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagClass}" ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagClassMagic}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type DataBind:TagEntry}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Text="{Binding TagFileName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
My classes;
public class TagClass
{
public string TagClassMagic { get; set; }
public ITagClass RawClass { get; set; }
public List<TagEntry> TagEntries = new List<TagEntry>();
public IList Children
{
get
{
return new CompositeCollection()
{
new CollectionContainer() { Collection = TagEntries }
};
}
}
}
public class TagEntry
{
public string TagFileName { get; set; }
public ITagEntry RawTag { get; set; }
}

I think this is because Children every time returns a new CompositeCollection which is propably expanded by default. Try returning TagEntries directly or return a new CompositeCollection at first time only.

Turns out we had to change;
VirtualizingStackPanel.VirtualizationMode="Recycling"
to
VirtualizingStackPanel.VirtualizationMode="Standard"

Related

Displaying two lists of the same type at the same level in the treeview

I need to build a tree that consists of operations.
Each operation has a name and two internal lists consisting of input and output GIDs (states). The problem is that both lists come with the same type [GID]. I am having trouble creating the hierarchical tree correctly.
I would like my tree to look like this:
|Operation 1
|IN
|Item-1
|Item-2
|OUT
|Item-1 1/2
|Item-1 2/2
|Item-2
Here is my model:
public class Operation
{
public Operation(string code, string name, List<GID> inState, List<GID> outState)
{
OpCode = code;
OpName = name;
InState = new ObservableCollection<GID>(inState);
OutState = new ObservableCollection<GID>(outState);
}
public string OpCode { get; }
public string OpName { get; }
public ObservableCollection<GID> InState { get; set; }
public ObservableCollection<GID> OutState { get; set; }
}
public class GID
{
public GID(string code, string name)
{
Code = code;
Name = name;
}
public virtual string Code { get; set; }
public virtual string Name { get; set; }
}
Here is my ViewModel:
public class MainViewModel
{
Operations = new ObservableCollection<Operation>();
Operations.Add(new Operation("O-1", "Operation-1", new List<GID>
{
new GID("I-1","Item-1"),
new GID("I-2","Item-2")
},
new List<GID>
{
new GID("I-1 1/2","Item-1 1/2"),
new GID("I-1 2/2","Item_1 2/2"),
new GID("I-2","Item-2")
}
));
public ObservableCollection<Operation> Operations { get; set; }
}
Here is my View:
<Window.Resources>
<HierarchicalDataTemplate x:Key="state" DataType="{x:Type local:GID}">
<StackPanel>
<TextBlock Text="{Binding Code}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="in" DataType="{x:Type local:Operation}"
ItemTemplate="{StaticResource state}" ItemsSource="{Binding InState}">
<TreeViewItem Header="IN">
<TextBlock Text="{Binding Path=Code}" TextWrapping="Wrap"/>
</TreeViewItem>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="out" DataType="{x:Type local:Operation}"
ItemTemplate="{StaticResource state}" ItemsSource="{Binding OutState}">
<TreeViewItem Header="OUT">
<TextBlock Text="{Binding Path=Code}" TextWrapping="Wrap"/>
</TreeViewItem>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="root"
ItemTemplate="{StaticResource ???}"
ItemsSource="{Binding Path=. ???}">
<StackPanel>
<TextBlock Text="{Binding OpName}" TextWrapping="Wrap"/>
<!--<ContentControl ContentTemplate="{StaticResource in}"/>
<ContentControl ContentTemplate="{StaticResource out}"/>-->
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView Height="200"
ItemsSource="{Binding Operations}"
ItemTemplate="{StaticResource root}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="IsSelected" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
The following code returns only the first node Operation-1
Can you give me a hint on how to properly display the nodes of a tree?
EDIT: PROBLEM SOLVED
I ended by changing my viewmodel to:
public class Operation
{
public Operation(string code, string name, IList<GID> inState, IList<GID> outState)
{
OpCode = code;
OpName = name;
States = new ObservableCollection<State>();
States.Add(new State("IN", inState));
States.Add(new State("OUT", outState));
}
public string OpCode { get; }
public string OpName { get; }
public ObservableCollection<State> States { get; set; }
}
public class State
{
public State(string stateName, IList<GID> stateCollection)
{
StateName = stateName;
StateCollection = new ObservableCollection<GID>(stateCollection);
}
public string StateName { get; set; }
public ObservableCollection<GID> StateCollection { get; set; }
}
and then modify my view
<Window.Resources>
<HierarchicalDataTemplate x:Key="gid" DataType="{x:Type local:GID}">
<StackPanel>
<TextBlock Text="{Binding Code}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="state" DataType="{x:Type local:State}"
ItemsSource="{Binding StateCollection}" ItemTemplate="{StaticResource gid}">
<TextBlock Text="{Binding Path=StateName}" TextWrapping="Wrap"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="root" ItemsSource="{Binding States}" ItemTemplate="{StaticResource state}">
<StackPanel>
<TextBlock Text="{Binding OpCode}" TextWrapping="Wrap"/>
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>

WPF ItemsSource binding show only last item of list

How to show only last item of the list in itemssource binding ?
below is my current code.
xaml
<ItemsControl ItemsSource="{Binding UpgradeTicketStorage}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Width="800">
<TextBlock Text="{Binding TicketTitle}" Style="{StaticResource TicketSelectionSubTitle}" TextAlignment="Left" />
<TextBlock Text="{Binding TicketDescription}" TextWrapping="Wrap" Style="{StaticResource TicketSelectionSubTitle2}" FontSize="19" TextAlignment="Left"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
from class binding i do have added 2 records which is ticketA and ticketB, how can i just display ticketB information ? instead of A and B
class
public class UpgradeTicketDescription : ViewModelBase
{
public string TicketTitle { get; set; }
public string TicketDescription { get; set; }
}
List<UpgradeTicketDescription> _UpgradeTicketStorage;
public List<UpgradeTicketDescription> UpgradeTicketStorage
{
get { return _UpgradeTicketStorage; }
set { _UpgradeTicketStorage = value; OnPropertyChanged("UpgradeTicketStorage"); }
}
UpgradeTicketStorage.Add(new UpgradeTicketDescription { TicketTitle = "TicketA", TicketDescription = "Observation DeckA (Single Ticket)"});
UpgradeTicketStorage.Add(new UpgradeTicketDescription { TicketTitle = "TicketB", TicketDescription = "Observation DeckB (Single Ticket)"});
If you want to bind to a specific item in a list you can go about it by creating a public variable you will bind to. Using what you have provided I've created an example that works for the last item in the list, all I did is create a new variable called LastItem and changed how the binding is in the project. This is just one of many ways of going about this.
xaml
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Width="800">
<TextBlock Text="{Binding LastItem.TicketTitle}" TextAlignment="Left" />
<TextBlock Text="{Binding LastItem.TicketDescription}" TextWrapping="Wrap" FontSize="19" TextAlignment="Left"/>
</StackPanel>
class
public UpgradeTicketDescription LastItem
{
get { return UpgradeTicketStorage.Last(); }
}
This provides this output:

Cannot bind to ListBox child of ListBox

I'm binding to a ListBox, and within the ListBox, another ListBox.
My model looks like
public interface ICriteriaDetail
{
string Title { get; }
int NumberOfEvents { get; }
ICriteriaDetail ChildCriteria { get; }
}
So, I'm using a recursive approach for the children (not a list).
When I bind to my ListView, I get the items in a list, one under the other.
The issue is, the child item is not showing at all!
The dummy data
public static IEnumerable<ICriteriaDetail> GetCriteriaList()
{
var list = new List<CompoundCriteria.Ui.Model.Interfaces.ICriteriaDetail>();
list.Add(GetCriteriaDetail("My Title", 5, false));
list.Add(GetCriteriaDetail("Other", 3, false));
list.Add(GetCriteriaDetail("Biggy", 8, true));
var child = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("childAgain", 43, null);
var child2 = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("childAgainAgain", 13, child);
list.Add(new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("Really big", 86, new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("smaller", 15, child2)));
return list;
}
private static ICriteriaDetail GetCriteriaDetail(string title, int events, bool hasChild)
{
if (!hasChild)
return new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail(title, events, null);
var child = new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail("child" + title, 13 + events, null);
return new CompoundCriteria.Ui.Model.CriteriaDetail.CriteriaDetail(title, events, child);
}
The ViewModel
public MainWindowViewModel()
{
this.Criterias = DatasourceMockup.GetCriteriaList();
}
private IEnumerable<ICriteriaDetail> _criterias;
public IEnumerable<ICriteriaDetail> Criterias
{
get { return _criterias; }
set { _criterias = value; }
}
And the ListBox in the XAML
<ListBox ItemsSource="{Binding Criterias}" >
<ListBox.Resources>
<ControlTemplate x:Key="con">
<StackPanel Orientation="Horizontal">
<StackPanel Margin="15">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="Events: ">
<Run Text="{Binding NumberOfEvents}" />
</TextBlock>
</StackPanel>
<ListBox ItemsSource="{Binding ChildCriteria} Visibility="{Binding ChildCriteria, Converter={StaticResource HideIfNull}}">">
<ListBox.Resources>
<DataTemplate DataType="{x:Type cDetail:CriteriaDetail}">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
</StackPanel>
</ControlTemplate>
<HierarchicalDataTemplate DataType="{x:Type cDetail:CriteriaDetail}">
<Control Template="{StaticResource con}" />
</HierarchicalDataTemplate>
</ListBox.Resources>
</ListBox>
After reading TreeView, HierarchicalDataTemplate and recursive Data it appears that HierarchicalDataTemplate will suffice (no need for a DataTemplate as well) but, I'm lost as to why I'm not seeing the result I expected (even if the children are not in the desired place, I'd still hope to see them)
EDIT
I have just added a converter which makes the ListBox (the one inside the ControlTemplate) hidden if the bound data is null. I have an outline (an empty ListBox) for just 1 of the 3 (which is correct) but it doesn't bind any of the content (the Title). The output window shows nothing of any use...
Please note, there is no limit of chidren. In the example above, there is a parent child relationship, but it could be parent-child-child-child etc
1) It is hard to understand your purpose , why ListBox, if you have ChildCriteria in model, not IEnumerable<ICriteriaDetail>?
2) Also you have binding errors, i changed xaml to
<ListBox ItemsSource="{Binding Criterias}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Margin="15">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="Events: ">
<Run Text="{Binding NumberOfEvents}" />
</TextBlock>
</StackPanel>
<TextBox Text="{Binding Path=DataContext.ChildCriteria.Title,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem},
AncestorLevel=1}}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and got
EDIT1:
1) I changed data model to
public class CriteriaDetail : ICriteriaDetail
{
public CriteriaDetail(string title, int numberOfEvents, IEnumerable<ICriteriaDetail> childCriteria)
{
Title = title;
NumberOfEvents = numberOfEvents;
ChildCriteria = childCriteria;
}
public string Title { get; set; }
public int NumberOfEvents { get; set; }
public IEnumerable<ICriteriaDetail> ChildCriteria { get; set; }
}
2) Changed xaml to
<TreeView ItemsSource="{Binding Criterias}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ChildCriteria}">
<TextBlock Foreground="Red" Text="{Binding Title}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
3) Changed GetCriteriaDetail to
private static ICriteriaDetail GetCriteriaDetail(string title, int events, bool hasChild)
{
if (!hasChild)
return new CriteriaDetail(title, events, null);
var child3 = new CriteriaDetail("child3" + title, 13 + events, null);
var child2 = new CriteriaDetail("child2" + title, 13 + events, new ICriteriaDetail[] { child3 });
var child1 = new CriteriaDetail("child1" + title, 13 + events, new ICriteriaDetail[] { child2 });
return new CriteriaDetail(title, events, new ICriteriaDetail[] {child1});
}
and got this. I think it looks more like you want

Simple Nested TreeView Xaml structure?

I am trying to build a WPF TreeView with three layers. CountryReportTitle is a string property and ArticleCategoryTitlesList is a collection, both exposed from my ViewModel. There is no class hierarchy defined. This is the structure I'm looking for:
This is my attempted Xaml but I'm getting an exception in the Xaml at runtime:
{"Item has already been added. Key in dictionary: 'DataTemplateKey(ISESApp.ViewModels.ReportViewModel)' Key being added: 'DataTemplateKey(ISESApp.ViewModels.ReportViewModel)'"}
Xaml:
<TreeView ItemsSource="{Binding CountryReportTitle}">
<TreeView ItemsSource="{Binding CountryReportTitle}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ReportViewModel}"
ItemsSource="{Binding ArticleCategoryTitlesList}">
<TextBlock Text="{Binding CategoryTitle}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ReportViewModel}"
ItemsSource="{Binding ArticleCatagoryTypesList}">
<TextBlock Text="{Binding ArticleTitle}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:ReportViewModel}">
<TextBlock Text="{Binding ArticleTitle}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
</TreeView>
Local: is a namespace to my ViewModel:
xmlns:local="clr-namespace:MyApp.ViewModels"
What am I doing wrong, what is the best approach for this problem?
Here's my go-to example for treeviews.
Use a HierarchicalDataTemplate for elements in the tree. Note that there are three layers, and each layer is its own type. This is for convenience, but you could define one type and use one template or any mix of types for your tree. Having different types represent different things in the tree makes using templates extremely convenient.
The data classes
public class ViewModel
{
public ObservableCollection<ItemA> ItemsA { get; set; }
public ViewModel()
{
ItemsA = new ObservableCollection<ItemA>(new[]{
new ItemA{Name = "A one"},
new ItemA{Name = "A Two"},
new ItemA{Name = "A Three"},
});
}
}
public class ItemA
{
public ObservableCollection<ItemB> ItemsB { get; set; }
public string Name { get; set; }
public ItemA()
{
ItemsB = new ObservableCollection<ItemB>(new[]{
new ItemB{Name = "B one"},
new ItemB{Name = "B Two"},
new ItemB{Name = "B Three"},
});
}
}
public class ItemB
{
public ObservableCollection<ItemC> ItemsC { get; set; }
public string Name { get; set; }
public ItemB()
{
ItemsC = new ObservableCollection<ItemC>(new[]{
new ItemC{Name = "C one"},
new ItemC{Name = "C Two"},
new ItemC{Name = "C Three"},
});
}
}
public class ItemC
{
public string Name { get; set; }
}
And the UI
<TreeView ItemsSource="{Binding ItemsA}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type t:ItemA}"
ItemsSource="{Binding ItemsB}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type t:ItemB}"
ItemsSource="{Binding ItemsC}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type t:ItemC}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
gives you a simple treeview
You need to bind the TreeView Resources inside your TreeView:
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:FirstLayer}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding ChildrenName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:SecondLayer}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding ChildrenName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
I think you'll need to modify your project to bind your Treeview with your ViewModel instead of a list of string
Here is a good Example

DataBinding to WP8 Toolkit ExpanderView

I'm attempting to databind to a Windows Phone 8 Toolkit Expander view with the following XAML and C# class. I know that the DataContext is set properly because the Headers have the proper text. However, the rest of the items aren't set properly (except for the ExpanderTemplate)
<phone:PanoramaItem Header="Skill Sheet">
<ListBox Name="SkillSheet" ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:ExpanderView Header="{Binding}"
ItemsSource="{Binding}"
IsNonExpandable="False">
<toolkit:ExpanderView.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding groupName}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" LineHeight="{StaticResource LongListSelectorGroupHeaderFontSize}" />
</DataTemplate>
</toolkit:ExpanderView.HeaderTemplate>
<toolkit:ExpanderView.ExpanderTemplate>
<DataTemplate>
<TextBlock Text="Test" />
</DataTemplate>
</toolkit:ExpanderView.ExpanderTemplate>
<!--This is the area that is not getting databound-->
<toolkit:ExpanderView.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding skillNames}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding skill}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</toolkit:ExpanderView.ItemTemplate>
</toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</phone:PanoramaItem>
And here are the classes that the XAML is getting bound to:
public class TreeMapSkill
{
public string skill { get; set; }
}
public class TreeMapping
{
public string groupName { get; set; }
public List<TreeMapSkill> skillNames { get; set; }
public TreeMapping()
{
skillNames = new List<TreeMapSkill>();
}
}
public class TreeMappingList
{
public List<TreeMapping> mapping { get; set; }
public TreeMappingList() { }
public TreeMappingList(Dictionary<string, List<string>> map)
: base()
{
this.mapping = new List<TreeMapping>();
foreach (string key in map.Keys)
{
TreeMapping tMap = new TreeMapping();
tMap.groupName = key;
foreach (string val in map[key])
tMap.skillNames.Add(new TreeMapSkill() { skill = val });
this.mapping.Add(tMap);
}
}
The Dictionary in the constructor is simply a list of skills associated to a specific group. I can also provide a sample object if it's needed for additional reference.
Why are you adding a ListBox inside the Expander's ItemTemplate? It is already a controls collection so you don't need a ListBox in there. Just put your DataTemplate inside.
<toolkit:ExpanderView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding skill}" />
</DataTemplate>
</toolkit:ExpanderView.ItemTemplate>
The second thing is you need to specify the property path on the binding of the ItemSource property for the expander.
<toolkit:ExpanderView Header="{Binding}"
ItemsSource="{Binding skillNames}"
IsNonExpandable="False">

Categories

Resources