Is there any way to determine (and if possible programmatically set) which groups are collapsed and which are not in a list view. Here's how the listview grouping is set up:
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(LvMslInfoTable.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("GroupObject");
view.GroupDescriptions.Add(groupDescription);
view.SortDescriptions.Add(new SortDescription("GroupObjectSortOrder", ListSortDirection.Ascending));
It took me a while to get to work back at that project. The Link from #Oleg was indeed helpful in that it pointed to the Expander control. The only thing missing was to get into the visual tree and find that element. From then on it was relatively easy to find the missing parts. Here is the code I used in the end. The GetDescendantByType method was copied from somewhere here in Stackoverflow as well. The rest was storing the expanded state in conjunction with a group name (expandedStateStatus is a Dictionary containing those entries).
StackPanel sp = (StackPanel)GetDescendantByType(LvMslInfoTable, typeof(StackPanel));
foreach (var gi in sp.Children.Cast<GroupItem>())
{
var tb = (TextBlock)GetDescendantByType(gi, typeof(TextBlock));
if (tb == null) continue;
Expander exp = (Expander)GetDescendantByType(gi, typeof(Expander));
if (exp == null) continue;
if (!expandedStateStatus.ContainsKey(tb.Text))
{
expandedStateStatus.Add(tb.Text, exp.IsExpanded);
}
}
private static Visual GetDescendantByType(Visual element, Type type)
{
if (element == null) return null;
if (element.GetType() == type) return element;
Visual foundElement = null;
if (element is FrameworkElement frameworkElement)
{
frameworkElement.ApplyTemplate();
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type);
if (foundElement != null)
{
break;
}
}
return foundElement;
}
Related
I am trying to get added user control in a list box, but couldn't find proper way to get it.
I am using below for finding user controls in binded listbox, but out of 10 items it could not find 3 or 4.
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = System.Windows.Media.VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
return (T)child;
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
Code for button click to find user control.
private void Button_Click(object sender, RoutedEventArgs e)
{
if (!downloadClicked)
{
downloadClicked = true;
SuraWithProgressBar prg = null;
ListBoxItem item = null;
for (int rowIndex = 0; rowIndex < lsbQuranData.Items.Count; rowIndex++)
{
item = this.lsbQuranData.ItemContainerGenerator.ContainerFromIndex(rowIndex) as ListBoxItem;
if (item != null)
{
prg = FindFirstElementInVisualTree<SuraWithProgressBar>(item);
if (prg != null)
{
//Do Somthing
prg.addButtonClickInterface(this);
}
}
}
}
else
MessageBox.Show("Please wait, downloading...");
}
As I mentioned out of 10, It cannot find 3-4 items. I am looking for proper way to find my user control inside listbox.
Thanks!
After a lot of headache I find out the solution in two below links:
WP7 - VisualTreeHelper to loop through all ListBox Items
http://msdn.microsoft.com/en-us/library/windows/apps/jj709920.aspx
Add template to your listbox and change SerializeStackPanel to StackPanel and the problem is solved. Please make sure to add this to ItemTemplate section.
I need to access the nodes of a TreeView as a plain list (as if all the nodes where expanded) to be able to do multiselection pressing the Shift key. Is there a way to accomplish this?
Thanks
Here is a method that will retrieve all the TreeViewItems in a TreeView. Please be aware that this is an extremely expensive method to run, as it will have to expand all TreeViewItems nodes and perform an updateLayout each time. As the TreeViewItems are only created when expanding the parent node, there is no other way to do that.
If you only need the list of the nodes that are already opened, you can remove the code that expand them, it will then be much cheaper.
Maybe you should try to find another way to manage multiselection. Having said that, here is the method :
public static List<TreeViewItem> FindTreeViewItems(this Visual #this)
{
if (#this == null)
return null;
var result = new List<TreeViewItem>();
var frameworkElement = #this as FrameworkElement;
if (frameworkElement != null)
{
frameworkElement.ApplyTemplate();
}
Visual child = null;
for (int i = 0, count = VisualTreeHelper.GetChildrenCount(#this); i < count; i++)
{
child = VisualTreeHelper.GetChild(#this, i) as Visual;
var treeViewItem = child as TreeViewItem;
if (treeViewItem != null)
{
result.Add(treeViewItem);
if (!treeViewItem.IsExpanded)
{
treeViewItem.IsExpanded = true;
treeViewItem.UpdateLayout();
}
}
foreach (var childTreeViewItem in FindTreeViewItems(child))
{
result.Add(childTreeViewItem);
}
}
return result;
}
Here is what you asked;
private static TreeViewItem[] getTreeViewItems(TreeView treeView)
{
List<TreeViewItem> returnItems = new List<TreeViewItem>();
for (int x = 0; x < treeView.Items.Count; x++)
{
returnItems.AddRange(getTreeViewItems((TreeViewItem)treeView.Items[x]));
}
return returnItems.ToArray();
}
private static TreeViewItem[] getTreeViewItems(TreeViewItem currentTreeViewItem)
{
List<TreeViewItem> returnItems = new List<TreeViewItem>();
returnItems.Add(currentTreeViewItem);
for (int x = 0; x < currentTreeViewItem.Items.Count; x++)
{
returnItems.AddRange(getTreeViewItems((TreeViewItem)currentTreeViewItem.Items[x]));
}
return returnItems.ToArray();
}
Call with your control as the first parameter e.g.;
getTreeViewItems(treeView1);
How I can find element that contains in App.xaml, grid with name "audioPanel"?
I tried:
Grid found = this.FindChild<Grid>(^*I can't find anything suitable*^, "audioPanel");
How can I find WPF controls by name or type?
UPD: App.xaml http://pastebin.com/KfWbjMV8
UPDATE: You need a combination of both my answer and H.B.'s answer. Use the version of FindChild below, and change your call to FindChild to look like
var grid = FindChild<Grid>(Application.Current.RootVisual, "audioPanel");
Since you're styling the phone application frame, the "control on which it is applied" from H.B.'s comment is pretty likely to be the RootVisual (there may be exceptions to this, I'm not sure).
Also, I'm assuming that the "..." parts of your App.xaml in pastebin have a ContentPresenter in there somewhere, otherwise I don't think your style will work.
END UPDATE
If you're using the accepted answer in the question you linked to (WPF ways to find controls) and your 'audioPanel' grid is nested inside of another grid, then you still won't find it - there's an error in that code. Here's an updated version that will work even if the control is nested:
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null)
{
return null;
}
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
var childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
{
break;
}
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T) child;
break;
}
// Need this in case the element we want is nested
// in another element of the same type
foundChild = FindChild<T>(child, childName);
}
else
{
// child element found.
foundChild = (T) child;
break;
}
}
return foundChild;
}
}
If it is in App.xaml i would assume it to be part of a resource in Application.Resources, as resources that are not used anywhere are not in the visual tree this won't do.
If this is true you can try getting the root of the object from the resources and search from there, e.g.
var root = Application.Current.Resources["MyKey"] as FrameworkElement;
Grid found = this.FindChild<Grid>(root, "audioPanel");
just for completeness, the version of E. Z. Hart has a bug, as found sub childs are overwritten. here is a working version
public static T FindChild<T>(this DependencyObject parent, string childName = null) where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null)
return null;
T foundChild = null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; foundChild == null && i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
var childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
}
else
{
// Need this in case the element we want is nested
// in another element of the same type
foundChild = FindChild<T>(child, childName);
}
}
else
{
// child element found.
foundChild = (T)child;
}
}
return foundChild;
}
Having a tree (logical in DB) with items in the form
List item A
List item B
List item C
List item D
List Item E
List Item F
List Item G
and so on (nesting depth not limited), I want to get the next node down (or up), starting from an arbitrary node.
Let's say, List Item D is given I want to write a function GetNextNode() that would return List Item E.
My idea would be to do some recursion stuff, but maybe there is a more clever way to handle this?
My question:
How would you solve this?
EDIT 1:
The tree can be accessed with functions like:
GetParentNode()
GetChildrenNodes()
GetNextSiblingNode()
etc.
So it's similar to e.g. e Windows Forms TreeView.
I've had to do this several times. From memory:
public Node GetBelowNode()
{
if (GetChildrenNodes().count > 0)
return GetChildrenNodes()[0];
else
if (GetNextSiblingNode() != null)
return GetNextSiblingNode();
else
{
Node curr = this;
Node parent;
while (true)
{
parent = curr.GetParentNode();
if (parent == null)
return null;
else
{
if (parent.GetNextSiblingNode() != null)
return parent.GetNextSiblingNode();
else
curr = parent;
}
}
}
}
You can handle this via recursion or... worst xD
I think there are only 3 basic cases:
private string getNext(TreeNode node)
{
if (node.FirstNode != null)
{
return node.FirstNode.Name;
}
else
{
if (node.NextNode != null)
{
return node.NextNode.Name;
}
else if (node.Parent.NextNode != null)
{
return node.Parent.NextNode.Name;
}
}
return "";
}
This doesn't works for every scenario. You should search the parent's next node too. Thanks to Vincent Vancalbergh for the comment ;-)
public Party Next {
get {
if (this.children.Count > 0) return this.children[0];
Party target = this;
do {
if (target.NextSibling != null) return target.NextSibling;
} while ((target = target.Parent) != null);
return null;
}
}
public Party Previous {
get {
if (Parent != null && Parent.children.Count > 0 && this == Parent.children[0]) {
return Parent;
}
Party target = this;
do {
if (target.PreviousSibling != null) { target = target.PreviousSibling; break; }
} while ((target = target.Parent) != null);
if (target != null) {
while (target.children.Count > 0) {
target = target.children[target.children.Count - 1];
}
}
return target;
}
}
Since I got a great reply for the "down" part, I'll added my own "up" part. Maybe it is for some help of you; the up part is similar to:
Get the previous sibling.
If there is a previous sibling, get the deepest child node of this
sibling.
If there is no previous sibling, get the direct parent.
To get the deepest sibling (2.), I use the following code:
function getDeepestChild( page )
dim result
set result = nothing
dim childPages
set childPages = page.ChildPages
if childPages.Count>0 then
dim p
set p = childPages(childPages.Count-1)
' recurse.
set result = getDeepestChild( p )
else
set result = page
end if
set getDeepestChild = result
end function
(Yes, I do know that this is VBScript; I needed it in fact in this language)
Try this maybe:
TreeNode currentNode = treeView1.SelectedNode;
treeView1.selectedNode = currentNode.NextNode;
I have a custom row template to show some data, and it doesn't use a SelectiveScrollingGrid in its' template. I don't mind handling the events on my outer elements, but I can't seem to figure out how to cause a "Select" behavior. Typically I've been causing it by raising the MouseLeftButtonDownEvent on the active DataGridCell, but now that I don't actually have any DataGridCell's, I'm a bit perplexed on how to duplicate that behavior with only access to the DataGridRow.
not sure how your template looks like but I guess you can consider selecting the whole row of your grid by setting it's property SelectionUnit="FullRow" and executing the code below; it select the entire row with index 3
int index = 3;
dataGrid.SelectedItem = dataGrid.Items[index];
dataGrid.ScrollIntoView(dataGrid.Items[index]);
DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.Items[index]);
row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
if you still want to select a cell, please check if code below would work for you, it selects a cell with index 2 for the row with index 3
int index = 3;
dataGrid.ScrollIntoView(dataGrid.Items[index]);
DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.Items[index]);
if (row != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(2);
if (cell != null)
{
cell.IsSelected = true;
cell.Focus();
}
}
GetVisualChild procedure implementation:
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
hope this helps, regards
This is what I ended up getting to work, it's ugly but gets the job done. These elements only highlight upon a left or right click, so I'm having to force a redraw as well, seems ugly to me but it works.
var row = (DataGridRow)((FrameworkElement)sender).TemplatedParent;
var element = (FrameworkElement)sender;
var parentGrid = this.GetGridFromRow((DataGridRow)element.TemplatedParent);
parentGrid.SelectedItems.Clear();
row.IsSelected = true;
element.InvalidateVisual();
parentGrid.UpdateLayout();