Cant see controls inside User Control in the VisualTreeHelper - wpf

I have UserControl in wpf 4.0 which contains buttons , labels , textboxes etc....
I want to loop those controls and when I get a buuton , I want to take it's name and save it to my list . Basically , all I want to do is to create a Names_list of all my buttons in the UserControl.
I have a method that iterates all the controls and if it finds a button , it saves it's name -
public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
Button _button = childVisual as Button;
if (_button != null)
{
Class_Button _newButtonClass = new Class_Button();
if (_button.Name != null)
{
_newButtonClass.ButtonName = _button.Name;
}
ButtonsList.Add(_newButtonClass);
}
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
I always get an empty list.
When I enter in to the code by debugging it and I watch the VisualTree of my UserControl , I see all the Panels and GroupBoxes and Grids but I dont see buttons , labels and texboxes although every control has a x:Name and every control is x:FieldModifier="public". This is very odd....And I cant understand the reason for that as well as how to solve this problem...
can anyone tell what I am doing wrong?
thanks

As suggested by #GazTheDestroyer you want to make sure the control template has been applied before trying to use VisualTreeHelper. Try:
public void EnumVisual(Visual myVisual)
{
if(myVisual is FrameworkElement)
((FrameworkElement)myVisual).ApplyTemplate();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
Button _button = childVisual as Button;
if (_button != null)
{
Class_Button _newButtonClass = new Class_Button();
if (_button.Name != null)
{
_newButtonClass.ButtonName = _button.Name;
}
ButtonsList.Add(_newButtonClass);
}
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}

You can use a tool like Snoop
or WPF Inspector
to examine the visual tree of your control.
If these tools are able to do so, the error must be somewhere in your code, right?

Related

Visual Tree Finder is returning null while searching for Data Grid

I have Tab Control which has many tab items, I am checking Data Grid Items Count while closing tab items. For the first time it works fine(I mean in first iteration). After closing one tab item, in second iteration sellDtg is null. Does anyone know why it is happening? I am concerning that this is UI problem, layout is not being refreshed. Please help me or show direction.
while (tc.HasItems)
{
TabItem ti = tc.SelectedItem as TabItem;
if (ti.Header == "Продажа")
{
Microsoft.Windows.Controls.DataGrid sellDtg = FindChild<Microsoft.Windows.Controls.DataGrid>(tc, "SellDataGrid");
if (sellDtg.Items.Count > 0)
{
Sell sl = new Sell();
if (Sell.basketfromSellDateListBox == false)
{
sl.ClearBasket(sellDtg);
Sell.ClearFromSellBasket((int)sellDtg.Tag);
}
}
}
if (ti != null)
tc.Items.Remove(ti);
}
Thanks in advance!!!
I've written a simple FindChildLogical function in analogy for LogicalTreeHelper below:
public static T FindChildLogical<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
if (parent == null) return null;
var child = LogicalTreeHelper.FindLogicalNode(parent, childName);
return (T)child;
}
and you call it as:
Microsoft.Windows.Controls.DataGrid sellDtg = FindChildLogical<Microsoft.Windows.Controls.DataGrid>(ti, "SellDataGrid");
I hope it gets you where you intend to.
I am going to assume your FindChild method uses the VisualTreeHelper to find its children.
In the first iteration, the TabItem's Content has been through a layout pass, and is visible. This means that the TabItem's Content will be in the visual tree.
However, for the other tab items, their Content hasn't been through a layout pass (it is only added to the visual tree when it's parent gets selected, and this has to then go through a layout/render pass), and won't be in the visual tree.
There are a couple of ways to get the child content of a TabItem that hasn't been through a layout pass as the selected tab:
1) You can try using the LogicalTreeHelper to find the Grid you're looking for (and you will likely have to search the Content of the TabItem, not the TabControl).
2) You can take your code out of the while loop, and do a callback on the dispatcher at the Loaded priority:
void RemoveAllItems()
{
if (!tc.HasItems) return;
TabItem ti = tc.SelectedItem as TabItem;
if (ti.Header == "Продажа")
{
var sellDtg = FindChild<Microsoft.Windows.Controls.DataGrid>(tc, "SellDataGrid");
if (sellDtg.Items.Count > 0)
{
Sell sl = new Sell();
if (Sell.basketfromSellDateListBox == false)
{
sl.ClearBasket(sellDtg);
Sell.ClearFromSellBasket((int)sellDtg.Tag);
}
if (ti != null)
tc.Items.Remove(ti);
}
}
Dispatcher.BeginInvoke(new Action(RemoveAllItems), DispatcherPriority.Loaded);
}
If you use the second method, you will likely be able to see the tab items removed one at a time, which may be something you don't want to see.

Scroll to item in listview in Silverlight 3

I have a silverlight 3 app with a textbox on the main window and a childwindow that has a list of all the potential textbox values. When I open that childwindow I want it to scroll to the correct one in the list.
I'm trying to do this with the code below...using the ScrollIntoView. It was not working at all until I add the UpdateLayerout(). However it does not seem to work all the time. At times it scrolls but not all the way to the item, it is a few items higher than it should be. The listbox is in an Accordion and the list items use a ItemTemplate\DataTemplate, not sure if that effects anything but thought I'd mention it.
Any ideas what I'm missing in the code below?
What I would like is to scroll the item to the top of the list ....any ideas how to that?
(Or any other suggestions on how to code this better)
Thanks!
for (int index = 0; index < myList.Items.Count; index++) {
object obj = myList.Items[index];
var listItem= obj as listItemObject;
if (listItemObj != null) {
if (string.Compare(listItemObj.id, _PastedInId, StringComparison.InvariantCultureIgnoreCase) == 0) {
selectThisIndex = index;
scrollToThisItem = obj;
}
}
}
myList.SelectedIndex = selectThisIndex;
if (scrollToThisItem != null){
myList.UpdateLayout();
myList.ScrollIntoView(scrollToThisItem);
}
Consider using the ItemsControlExtensions implementation from the Silverlight Toolkit, available at http://silverlight.codeplex.com/sourcecontrol/network/Show?projectName=Silverlight&changeSetId=47051#637494
This has a ScrollIntoView(FrameworkElement element) method that may help.

AssociatedObject.FindName in Silverlight behavior OnAttached method returns null

I'm making a Silverlight behavior to enable dragging an element by a contained "drag handle" element (rather than the whole element being draggable). Think of it like a window title bar.
In the OnAttached method I am calling: AssociatedObject.FindName(DragHandle)
but this is returning null.
I then tried handling the AssociatedObject's Loaded event and running my code there, but I still get a null returned.
Am I misunderstanding what FindName is able to do? The AssociatedObject is in an ItemsControl (I want a collection of draggable elements). So is there some kind of namescope problem?
Yes, it sounds like a namescope problem. The MSDN documentation on XAML namescopes goes over how namesopes are defined for templates and item controls. Are you using a template for the items in your ItemsControl?
You may just have to walk the visual tree recursively with something like this to find the correct element by name:
private static FrameworkElement FindChildByName(FrameworkElement parent, string name)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (child != null && child.Name == name)
{
return child;
}
else
{
FrameworkElement grandChild = FindChildByName(child, name);
if (grandChild != null)
{
return grandChild;
}
}
}
return null;
}

How to goto particular page number in flowdocument reader through code in WPF?

There is a pagenumber property in flowdocument reader.But that property is readonly. Is there any way to goto particular page number in flowdocument reader.Please help.
Thanks.
If you keep track of the Blocks on the FlowDocument contained in the FlowDocumentReader,
than you can simply use:
// Getting a block by index
YourReader.Document.Blocks.ElementAt(index).BringIntoView();
// Showing Last Block
YourReader.Document.Blocks.LastBlock.BringIntoView();
// Showing the last Inline
(YourReader.Document.Blocks.LastBlock as Paragraph).Inlines.LastInline.BringIntoView();
This works only on the page ViewingModes of the FlowDocumentReader.
if you whould like to do so on the scroll mode, you must go down the visual tree and search for the ScrollViewer,
somthing like this:
public static ScrollViewer FindScroll(Visual visual)
{
if (visual is ScrollViewer)
return visual as ScrollViewer;
ScrollViewer searchChiled = null;
DependencyObject chiled;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
chiled = VisualTreeHelper.GetChild(visual, i);
if (chiled is Visual)
searchChiled = FindScroll(chiled as Visual);
if (searchChiled != null)
return searchChiled;
}
return null;
}
ScrollViewer scroller = FindScroll(YourReader as Visual);
if (scroller != null)
(scroller as ScrollViewer).ScrollToBottom();
If you are willing to restrict your users to paged display, use FlowDocumentPageViewer instead: this has a GoToPage() method. For some reason GoToPage() doesn't seem to be offered on FlowDocumentReader; I'd guess this is because FlowDocumentReader isn't always in a mode where paging is meaningful (the user can select a continuous scrolling view), and provides its own UI for this when it is meaningful.
You could try sending it the NavigationCommands.GoToPage command, but this is only documented as working on FlowDocumentPageViewer and DocumentViewer; I haven't tested it on FlowDocumentReader.

How do I bring an item to the front in wpf?

I simply have two grid on top of one another. Given one state of the world, I want grid A to be on top, given another state of the world, I want grid B to be on top. In the old days we could just call grid.BringToFront(), but that doesn't exist anymore, and I can't figure out any way to make that happen.
The best I can figure, I need to create my own custom classes to allow this functionality, but that seems like major overkill for something that used to be so simple.
You can use the Panel.ZIndex property to change the display order of elements in a panel
You have to use the Z index property, and because there are no built-in function to do what you want, I made my own.
The higher the Z value, the 'closer' to front the control is.
So you want to put your control on top without having to set an arbitrary high Z value.
So here is a small function I wrote for myself to do exactly that.
Note: this assume that you are using a Canvas and UserControls.
So you might need to adapt it a little bit if that's not your case.
Basically it will get the index of the control to move, then any control currently above it will go down by 1 and the control to move will be put on top (to maintain hierarchy).
static public void BringToFront(Canvas pParent, UserControl pToMove)
{
try
{
int currentIndex = Canvas.GetZIndex(pToMove);
int zIndex = 0;
int maxZ = 0;
UserControl child;
for (int i = 0; i < pParent.Children.Count; i++)
{
if (pParent.Children[i] is UserControl &&
pParent.Children[i] != pToMove)
{
child = pParent.Children[i] as UserControl;
zIndex = Canvas.GetZIndex(child);
maxZ = Math.Max(maxZ, zIndex);
if (zIndex > currentIndex)
{
Canvas.SetZIndex(child, zIndex - 1);
}
}
}
Canvas.SetZIndex(pToMove, maxZ);
}
catch (Exception ex)
{
}
}
To whom it may concern:
ZIndex property is 0 by default, so if you have (like me) a Canvas with more than 1 element (>4000 Shapes in my case), all will have ZIndex = 0, so changing the ZIndexes with this method will have no effect.
For this to work, I set the ZIndexes to a known value after creating all the elements, so they can be ordered after.
int zIndex = 0;
for (int i = 0; i < canvas.Children.Count; i++) {
UIElement child = canvas.Children[i] as UIElement;
if (canvas.Children[i] is UIElement) Canvas.SetZIndex(child, zIndex++);
}
Instead of stacking the two grids, change the visibility properties so the grid you aren't using is collapsed.
Expanding on the answer from #PicMickael, this will do exactly as they described but with less instructions:
public void BringToFront<T>(T uiElement, Canvas canvas)
{
try
{
foreach (UIElement s in canvas.Children)
{
Canvas.SetZIndex(s, 1);
}
Canvas.SetZIndex(uiElement as UIElement, 2);
}
catch (Exception ex)
{
WriteLog.Error(ex);
}
}

Resources