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.
Related
I come to write my first WPF Control. It contains a DataGrid filled with data. Change the font of some of the cells based by function that I call in the main form.Its here I really hit rock bottom.Anyone has ideas?
=========================================================================
I Looked in the topic above and still can't get my head around how to make it function that is dynamically called from the control in the main form. Here is the Code I am working on so far.
public void PaintCell(int row, int column)
{
DataGridRow rowContainer = GetRow(row);
if (rowContainer != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
CalendarGridView.ScrollIntoView(rowContainer, CalendarGridView.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
// I want to the text in the cell in red
}
}
}
private DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)CalendarGridView.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
CalendarGridView.UpdateLayout();
CalendarGridView.ScrollIntoView(CalendarGridView.Items[index]);
row = (DataGridRow)CalendarGridView.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
public 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;
}
}
Don't said is the best idea since it's my very first experience with WPF.
inside a behavior (connected to windows.interactivity) im trying to find a child of a Listview - specifically Gridviewcolumn- to be able to resize its width, the code is built using MVVM without code-behind.
the problem is simply that using the code below the Gridviewcolumn is not found, meanwhile other children of the Maingrid , f.e. a Button, it would be no problem (see the button example below).
any suggestions where the mistake comes from? thanks in advance!
// _parent is the main Grid of the Window
Button button = GetTemplateChildByName(_parent); // for the button its working and the value is returned
GridViewColumn gridviewcolumn = GetTemplateChildByNames(_parent);// for the GridViewColumn its not working and no value is returned
....
public Button GetTemplateChildByName(DependencyObject parent)
{
int childnum = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childnum; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is Button)
{
return child as Button;
}
else
{
var s = GetTemplateChildByName(child);
if (s != null)
return s;
}
}
return null;
}
public GridViewColumn GetTemplateChildByNames(DependencyObject parent)
{
int childnum = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childnum; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is GridViewColumn)
{
return child as GridViewColumn;
}
else
{
var s = GetTemplateChildByNames(child);
if (s != null)
return s;
}
}
return null;
}
I've managed to bind my data to a datagrid and listbox via ObservableCollection. I've also managed to get the listbox items from the first column of my datagrid using the below code. Can anyone tell me how to access the listboxes within the second and third columns as I'm only getting the first column containing my listboxes? (I thought my FindVisualChild merthod would have pulled ALL listboxes from the datagrid irrespective of which column they were in and I could have separated them via a change in tag name or something - I was wrong I guess...)
private void button1_Click(object sender, RoutedEventArgs e)
{
// Update Job Step Grid ListBox
for (int i = 0; i < dataGridJobSteps.Items.Count; i++)
{
DependencyObject obj = dataGridJobSteps.ItemContainerGenerator.ContainerFromIndex(i);
ListBox _listBox = FindVisualChild<ListBox>(obj);
var selectedItems = _listBox.Items;
foreach (var selectedItem in selectedItems)
{
MessageBox.Show(selectedItem.ToString());
}
}
}
public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
// Search immediate children
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Your FindVisualChild method only returns the first ListBox found, if you want to find all ListBoxes that are in the DataGrid you will need to modify the method to return IEnumerable<childItem>
public static IEnumerable<childItem> FindVisualChildren<childItem>(DependencyObject obj) where childItem : DependencyObject
{
// Search immediate children
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is childItem)
yield return (childItem)child;
else
{
var childOfChild = FindVisualChildren<childItem>(child);
if (childOfChild != null)
{
foreach(var item in childOfChild )
{
yield return item;
}
}
}
}
return null;
}
Note: However if you follow the MVVM pattern in WPF this will be a lot easier than iterating visual elements
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);
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();