I am trying to add in functionality to my TreeView wherein a user can have all of the nodes expand and collapse at the click of a button. Expand works well and fine enough using ExpandSubTree. For whatever reason, there is no CollapseSubTree function. How can I successfully accomplish this task?
Here is my current function:
private void expand_collapse_children(TreeViewItem tvi, bool expand)
{
if (tvi.Items.Count > 0)
{
foreach (TreeViewItem item in tvi.Items)
{
if (expand)
{
item.ExpandSubtree();
}
else
{
expand_collapse_children(item, expand);
item.IsExpanded = false;
}
}
}
}
As a note: isExpanded is half a step above useless. I can set it to false when it is true and it will not collapse anything more than the highest level selected.
Thanks!
I achieve expanding all nodes in a TreeView as follows (I have a similar function to collapse all nodes):
foreach (var treeViewItem in MyTreeView.FindVisualDescendants<TreeViewItem>(e => !e.IsExpanded, true)) {
treeViewItem.IsExpanded = true;
}
Where FindVisualDescendants is a handy extension method:
public static IEnumerable<T> FindVisualDescendants<T>(this Visual parent, Func<T, bool> predicate, bool deepSearch) where T : Visual {
var visualChildren = new List<Visual>();
var visualChildrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var childIndex = 0; childIndex < visualChildrenCount; childIndex++) {
visualChildren.Add((Visual) VisualTreeHelper.GetChild(parent, childIndex));
}
foreach (var child in visualChildren) {
var typedChild = child as T;
if ((typedChild != null) && (predicate == null || predicate.Invoke(typedChild))) {
yield return typedChild;
if (deepSearch) {foreach (var foundVisual in FindVisualDescendants(child, predicate, true)) {
yield return foundVisual;
}
} else {
foreach (var foundVisual in FindVisualDescendants(child, predicate, deepSearch)) {
yield return foundVisual;
}
}
}
yield break;
}
private static void CollapseRecursive(TreeViewItem item)
{
// Collapse item if expanded.
if (item.IsExpanded)
{
item.IsExpanded = false;
}
// If the item has sub items...
if (item.Items.Count > 0)
{
// ... iterate them...
foreach (TreeViewItem subItem in item.Items)
{
// ... and if they themselves have sub items...
if (subItem.Items.Count > 0)
{
// ... collapse the sub item and its sub items.
CollapseRecursive(subItem);
}
}
}
}
If you're interested, here's the implementation (from Reflector) of how ExpandSubTree is actually written. I suppose you could just go the opposite way.
public void ExpandSubtree()
{
ExpandRecursive(this);
}
private static void ExpandRecursive(TreeViewItem item)
{
if (item != null)
{
if (!item.IsExpanded)
{
item.SetCurrentValueInternal(IsExpandedProperty, BooleanBoxes.TrueBox);
}
item.ApplyTemplate();
ItemsPresenter presenter = (ItemsPresenter) item.Template.FindName("ItemsHost", item);
if (presenter != null)
{
presenter.ApplyTemplate();
}
else
{
item.UpdateLayout();
}
VirtualizingPanel itemsHost = item.ItemsHost as VirtualizingPanel;
item.ItemsHost.EnsureGenerator();
int index = 0;
int count = item.Items.Count;
while (index < count)
{
TreeViewItem item2;
if (itemsHost != null)
{
itemsHost.BringIndexIntoView(index);
item2 = (TreeViewItem) item.ItemContainerGenerator.ContainerFromIndex(index);
}
else
{
item2 = (TreeViewItem) item.ItemContainerGenerator.ContainerFromIndex(index);
item2.BringIntoView();
}
if (item2 != null)
{
ExpandRecursive(item2);
}
index++;
}
}
}
To get the ItemHost and call the EnsureGenerator you will need reflection since these are internal :
var panel = (Panel)typeof(MultiSelector).InvokeMember("ItemsHost", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance, null, item, null);
typeof (Panel).InvokeMember("EnsureGenerator", BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance, null, panel, null);
Related
I have a winforms treeview, I can read data automatically, (a node that is equal to key, and a node inside that is equal to value), but when reading object type, the values inside it are not going to be child of object node (key of object), (maybe I couldnt explain well, here is a screenshot and my methods.)
layer0 needs to be inside textures and scale needs to be inside display
My Json:
{
"parent": "builtin/generated",
"textures": {
"layer0": "mm:items/iron_dust"
},
"display": {
"scale": [ 1.7, 1.7, 1.7 ]
}
}
My method to auto detect(not all mine actually)
private void Form1_Load(object sender, EventArgs e)
{
StreamReader reader = new StreamReader(path);
string json = reader.ReadToEnd();
reader.Close();
JObject obj = JObject.Parse(json);
getAllProperties(obj);
}
void getAllProperties(JToken children)
{
TreeNode mainNode = treeView1.Nodes[0];
mainNode.Text = Path.GetFileNameWithoutExtension(path);
foreach (JToken child in children.Children())
{
var property = child as JProperty;
if (property != null)
{
if (property.Value.Type == JTokenType.String)
{
TreeNode keyNode = mainNode.Nodes.Add(property.Name);
keyNode.Nodes.Add(property.Value.ToString());
}
if (property.Value.Type == JTokenType.Array)
{
JArray array = (JArray)property.Value;
TreeNode node = mainNode.Nodes.Add(property.Name);
for (int i = 0; i < array.Count; i++)
{
node.Nodes.Add(array[i].ToString());
}
}
if (property.Value.Type == JTokenType.Object)
{
TreeNode topNode = mainNode.Nodes.Add(property.Name.ToString());
foreach (var item in property)
{
if (item.Type == JTokenType.String)
{
if (property.Value.Type == JTokenType.String)
{
TreeNode keyNode = topNode.Nodes.Add(property.Name);
keyNode.Nodes.Add(property.Value.ToString());
}
if (property.Value.Type == JTokenType.Array)
{
JArray array = (JArray)property.Value;
TreeNode node = topNode.Nodes.Add(property.Name);
for (int i = 0; i < array.Count; i++)
{
node.Nodes.Add(array[i].ToString());
}
}
}
}
}
// Console.WriteLine(property.Name + ":" + property.Value);//print all of the values
}
getAllProperties(child);
}
}
}
I tried to get parent, but it didnt have name and value properties :S.
Any help?
(Sorry for language mistakes)
The problem is that, as you recursively descend the JToken hierarchy, you also need to recursively descend the TreeNode hierarchy you are creating, adding child nodes to the parent node just created, rather than the root node, along the lines of Recursion, parsing xml file with attributes into treeview c#.
Thus if you do:
private void Form1_Load(object sender, EventArgs e)
{
using (var reader = new StreamReader(path))
using (var jsonReader = new JsonTextReader(reader))
{
var root = JToken.Load(jsonReader);
DisplayTreeView(root, Path.GetFileNameWithoutExtension(path));
}
}
private void DisplayTreeView(JToken root, string rootName)
{
treeView1.BeginUpdate();
try
{
treeView1.Nodes.Clear();
var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(rootName))];
tNode.Tag = root;
AddNode(root, tNode);
treeView1.ExpandAll();
}
finally
{
treeView1.EndUpdate();
}
}
private void AddNode(JToken token, TreeNode inTreeNode)
{
if (token == null)
return;
if (token is JValue)
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(token.ToString()))];
childNode.Tag = token;
}
else if (token is JObject)
{
var obj = (JObject)token;
foreach (var property in obj.Properties())
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(property.Name))];
childNode.Tag = property;
AddNode(property.Value, childNode);
}
}
else if (token is JArray)
{
var array = (JArray)token;
for (int i = 0; i < array.Count; i++)
{
var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(i.ToString()))];
childNode.Tag = array[i];
AddNode(array[i], childNode);
}
}
else
{
Debug.WriteLine(string.Format("{0} not implemented", token.Type)); // JConstructor, JRaw
}
}
You will get the following tree view structure:
Here is my crack at it. The output is identical to Notepad++'s JSTool plug-in:
The code is structured as a TreeView extension:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Windows.Forms;
namespace TestDLApp.Utilities.Extensions
{
public static class ObjectToTreeView
{
private sealed class IndexContainer
{
private int _n;
public int Inc() => _n++;
}
private static void FillTreeView(TreeNode node, JToken tok, Stack<IndexContainer> s)
{
if (tok.Type == JTokenType.Object)
{
TreeNode n = node;
if(tok.Parent != null)
{
if(tok.Parent.Type == JTokenType.Property)
{
n = node.Nodes.Add($"{((JProperty)tok.Parent).Name} <{tok.Type.ToString()}>");
}
else
{
n = node.Nodes.Add($"[{s.Peek().Inc()}] <{tok.Type.ToString()}>");
}
}
s.Push(new IndexContainer());
foreach (var p in tok.Children<JProperty>())
{
FillTreeView(n, p.Value, s);
}
s.Pop();
}
else if (tok.Type == JTokenType.Array)
{
TreeNode n = node;
if(tok.Parent != null)
{
if (tok.Parent.Type == JTokenType.Property)
{
n = node.Nodes.Add($"{((JProperty)tok.Parent).Name} <{tok.Type.ToString()}>");
}
else
{
n = node.Nodes.Add($"[{s.Peek().Inc()}] <{tok.Type.ToString()}>");
}
}
s.Push(new IndexContainer());
foreach (var p in tok)
{
FillTreeView(n, p, s);
}
s.Pop();
}
else
{
var name = string.Empty;
var value = JsonConvert.SerializeObject(((JValue)tok).Value);
if (tok.Parent.Type == JTokenType.Property)
{
name = $"{((JProperty)tok.Parent).Name} : {value}";
}
else
{
name = $"[{s.Peek().Inc()}] : {value}";
}
node.Nodes.Add(name);
}
}
public static void SetObjectAsJson<T>(this TreeView tv, T obj)
{
tv.BeginUpdate();
try
{
tv.Nodes.Clear();
var s = new Stack<IndexContainer>();
s.Push(new IndexContainer());
FillTreeView(tv.Nodes.Add("ROOT"), JsonConvert.DeserializeObject<JToken>(JsonConvert.SerializeObject(obj)), s);
s.Pop();
}
finally
{
tv.EndUpdate();
}
}
}
}
You can call it as so:
treeView1.SetObjectAsJson(new MyNeatObject());
so I'm creating a tree structure with my data and I want to avoid nested nested nested repetition. There can be children within children within children and I need to know what data to make collapsible and give a folder icon. Is there a way to simplify this? Thanks in advance.
foreach (var i in mlist)
{
// if this is a matching child
if (i.key == dto.under.ToString())
{
// add this as a child
i.children.Add(m1);
}
//check children also
foreach (var i2 in i.children)
{
if (i2.key == dto.under.ToString())
{
// add this as a child
i2.children.Add(m1);
}
if (i2.children.Count != 0)
{
i2.folder = true;
}
else
{
i2.folder = false;
}
foreach (var i3 in i2.children)
{
if (i3.key == dto.under.ToString())
{
// add this as a child
i3.children.Add(m1);
}
if (i3.children.Count != 0)
{
i3.folder = true;
}
else
{
i3.folder = false;
}
}
}
if (i.children.Count != 0)
{
i.folder = true;
}
else
{
i.folder = false;
}
}
Here's a recursive example of your current loop
public void Traverse(List<Item> items, Item dto, Item m1)
{
foreach (var i in items)
{
// if this is a matching child
if (i.key == dto.under.ToString())
{
// add this as a child
i.children.Add(m1);
}
i.folder = i.children.Count != 0;
Traverse(i.children, dto, m1);
}
}
...
Traverse(mlist, dto, m1);
You need a recursive function
foreach (var i in mlist)
{
checkChildren(i);
}
and then
void checkChildren( List i ) // i is of type List?
{
if (i.key == dto.under.ToString())
{
// add this as a child
i.children.Add(m1);
// what is m1? you may have to pass
// this in as a parameter. I am not
// really sure what it is
}
if (i.children.Count != 0)
{
i.folder = true;
}
else
{
i.folder = false;
}
foreach (var i2 in i.children)
{
checkChildren(i2);
// this will call the same function again,
// but this time on the next level of your hierarchy
}
}
In my WPF 4.0 project, I have an ObservableCollection that contain some selected Visual3D from the view :
public ObservableCollection<Visual3D> SelectedElements
{
get { return _selectedElements; }
set
{
if (Equals(_selectedElements, value))
{
return;
}
_selectedElements = value;
RaisePropertyChanged(() => SelectedElements);
}
}
Visual3D elements are selected by clicking and the source-code in the VM is :
public HitTestResultBehavior HitTestDown(HitTestResult result)
{
var resultMesh = result as RayMeshGeometry3DHitTestResult;
if (resultMesh == null)
return HitTestResultBehavior.Continue;
// Obtain clicked ModelVisual3D.
var vis = resultMesh.VisualHit as ModelVisual3D;
if (vis != null)
{
Type visType = vis.GetType();
if (visType.Name == "TruncatedConeVisual3D" || visType.Name == "BoxVisual3D")
{
var geoModel = resultMesh.ModelHit as GeometryModel3D;
if (geoModel != null)
{
var selecteMat = geoModel.Material as DiffuseMaterial;
if (selecteMat != null) selecteMat.Brush.Opacity = selecteMat.Brush.Opacity <= 0.7 ? 1 : 0.7;
}
// Otherwise it's a chair. Get the Transform3DGroup.
var xformgrp = vis.Transform as Transform3DGroup;
// This should not happen, but play it safe anyway.
if (xformgrp == null)
{
return HitTestResultBehavior.Stop;
}
// Loop through the child tranforms.
foreach (Transform3D t in xformgrp.Children)
{
// Find the TranslateTransform3D.
var trans =
t as TranslateTransform3D;
if (trans != null)
{
// Define an animation for the transform.
var anima = new DoubleAnimation();
if (trans.OffsetY == 0)
{
DependencyProperty prop = TranslateTransform3D.OffsetZProperty;
if (Math.Abs(trans.OffsetZ) < 2)
{
anima.To = 20.5*Math.Sign(trans.OffsetZ);
Debug.Assert(SelectedElements != null, "SelectedElements != null");
}
else
{
anima.To = 1*Math.Sign(trans.OffsetZ);
SelectedElements.Add(vis);
}
// Start the animation and stop the hit-testing.
trans.BeginAnimation(prop, anima);
return HitTestResultBehavior.Stop;
}
}
}
}
}
return (HitTestResultBehavior) HitTestFilterBehavior.Continue;
}
and I want to delete one or all this Visual3D element
Thank you in advance
I have implemented this methode but it's not work :
private void UnloadProduct()
{
if (CanUnload)
{
foreach (Visual3D selectedElement in SelectedElements)
{
if (selectedElement.DependencyObjectType.Name == "TruncatedConeVisual3D")
{
var test = (TruncatedConeVisual3D) selectedElement;
int id = test.VisualId;
ElementsCollection.RemoveAt(id);
RaisePropertyChanged(() => ElementsCollection);
}
else
{
var test = (BoxVisual3D) selectedElement;
int id = test.VisualId;
ProductsCollection.RemoveAt(id);
}
}
}
}
I have a function which should return true if all items pass the test. If only one item fails, then the function should return false. If there's no item in the collection the function should return false. Here is the code:
private bool TestAll()
{
bool finalResult = false;
bool itemResult = false;
foreach (var item in Items)
{
itemResult = Test(item);
if (!finalResult && itemResult)
finalResult = true;
else if (finalResult && !itemResult)
finalResult = false;
}
return finalResult;
}
How can I simplify the logic into one if statement using just one bool variable?
You can use the IEnumerable.All extension method to test all items, which fails on the first instance of the Test method failing.
private bool TestAll()
{
return Items.All(Test);
}
If you still need all items to be tested, you could probably use the AND assignment operator:
if (!Items.Any()) return false;
bool result = true;
foreach (var item in Items)
{
result &= Test(item);
}
return result;
If all the tests need to run, you can do it like this without LINQ:
private bool TestAll()
{
var allTestsPassed = true;
foreach (var item in Items)
{
allTestsPassed = Test(item) && allTestsPassed;
}
return allTestsPassed;
}
You can do it like this with LINQ:
private bool TestAll()
{
return Items.Count(Test) == Items.Count();
}
Update: returning false if there are no tests to run
private bool TestAllWithoutLinq()
{
if (Items.Count == 0) { // or something equivalent
return false;
}
var allTestsPassed = true;
foreach (var item in Items)
{
allTestsPassed = Test(item) && allTestsPassed;
}
return allTestsPassed;
}
private bool TestAllWithLinq()
{
return Items.Any() && Items.Count(Test) == Items.Count();
}
I realize this has been answered, and the shortest answer is the LINQ answer. Similar to others, but it requires a split second of thought:
private bool TestAll()
{
var passed = true;
foreach (var item in Items)
{
if ( ! Test(item))
{
passed = false;
}
}
return passed && Items.Count != 0;
}
Seems like an odd requirement that it should return false if only one item fails. Am I reading the question correctly? If so you can use the following
private bool TestAll() {
int failCount = 0;
foreach (var item in Items) {
if (!Test(item)) {
failCount++;
}
}
return failCount != 1;
}
private bool TestAll()
{
foreach (var item in Items)
{
if(!(Test(item)) || Items.Count == 0)
{
return false;
}
}
return true;
}
This will run all tests, return false if there are no tests, and return true only if all the tests pass:
private bool TestAll() {
if (Items.Count == 0) return false;
bool passed = true;
foreach(var item in Items) {
if (!Test(item))
passed = false;
}
return passed;
}
I have the following codes and I would like to write it in a way that I have minimum duplication of codes.
if (Categories != null)
{
bool flag=false;
foreach (dynamic usableCat in Category.LoadForProject(project.ID))
{
foreach (dynamic catRow in Categories)
{
if (usableCat.ID == catRow.ID)
flag = true;
}
if (!flag)
{
int id = usableCat.ID;
Category resolution = Category.Load(id);
resolution.Delete(Services.UserServices.User);
}
}
}
if (Priorities != null)
{
bool flag = false;
foreach (dynamic usableCat in Priority.LoadForProject(project.ID))
{
foreach (dynamic catRow in Priorities)
{
if (usableCat.ID == catRow.ID)
flag = true;
}
if (!flag)
{
int id = usableCat.ID;
Priority resolution = Priority.Load(id);
resolution.Delete(Services.UserServices.User);
}
}
}
Please note that Category and priority do not have a common base type or interface that includes ID.
void DeleteUsable<Ttype>(IEnumerable<Ttype> usables, IEnumerable<Ttype> collection, Func<int, Ttype> load)
{
bool flag = false;
foreach (dynamic usableCat in usables)
{
foreach (dynamic catRow in collection)
{
if (usableCat.ID == catRow.ID)
flag = true;
}
if (!flag)
{
int id = usableCat.ID;
Ttype resolution = load(id);
resolution.Delete(Services.UserServices.User);
}
}
}
Edit:
call it:
if (Categories != null)
DeleteUsable(Category.LoadForProject(project.ID), Categories, Categoriy.Load);
if (Priorities != null)
DeleteUsables(Priority.LoadForProject(project.ID), Priorities, Priority.Load);
Let me suggest an alternative approach: Instead of factoring out the flag thing, use LINQ to remove the need for the flag loop:
if (Categories != null)
{
foreach (var usableCat in Category.LoadForProject(project.ID))
{
if (!Categories.Any(row => usableCat.ID == row.ID))
Category.Load(usableCat.ID).Delete(Services.UserServices.User);
}
}
if (Priorities != null)
{
foreach (var usablePri in Priority.LoadForProject(project.ID))
{
if (!Priorities.Any(row => usablePri.ID == row.ID))
Priority.Load(usablePri.ID).Delete(Services.UserServices.User);
}
}
I'd recommend a method like this (since you have access to dynamic types):
void DeleteUsables(dynamic usablesResource, dynamic usablesCatalog)
{
bool flag = false;
foreach (dynamic usableCat in usablesCatalog.LoadForProject(project.ID))
{
foreach (dynamic catRow in usablesResource)
{
if (usableCat.ID == catRow.ID)
flag = true;
}
if (!flag)
{
int id = usableCat.ID;
dynamic resolution = usablesCatalog.Load(id);
resolution.Delete(Services.UserServices.User);
}
}
}
which you would then call like this:
if (Categories != null)
{
DeleteUsables(Categories, Category)
}
if (Priorities != null)
{
DeleteUsables(Priorities, Priority)
}