How to get instance of Panel that holds content of ItemsControl? - c#

Every ItemsControl has its content stored in Panel right ? We can specify the panel to be used in XAML like this:
<ListView Name="LView">
<ListView.ItemsPanel>
<ItemsPanelTemplate >
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
My question is how to get instance of Panel that is used in the ItemsPanel property (of type ItemsPanelTemplate) of the particular ItemsControl ? For example ListView called LView from above code sample?
I cannot use Name property or x:Name, this must work for any ItemsControl even those using default ItemsPanel.
In the case its not clear please comment, I think there is very simple solution. If it seems to be complicated that's only because I cannot explain it properly.

It's a little tricky since you don't know the name of the Panel so you can't use FindName etc. This will work for most cases where an ItemsPresenter is present
private Panel GetItemsPanel(DependencyObject itemsControl)
{
ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(itemsControl);
Panel itemsPanel = VisualTreeHelper.GetChild(itemsPresenter, 0) as Panel;
return itemsPanel;
}
An implementation of GetVisualChild
private static T GetVisualChild<T>(DependencyObject 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;
}
However, the ItemsPanel isn't always used. See this answer by Ian Griffiths for a great explanation.

I can't provide you with working code, but have a look at
VisualTreeHelper class.
With the VisualTreeHelper class you can traverse the visual tree down to your template and panel.

protected Panel ItemsHost {
get {
return (Panel) typeof (MultiSelector).InvokeMember("ItemsHost",
BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance,
null, this, null);
}
}
This works like a charm in my ItemsControl! That said, it does have IsItemsHost="True" on the Panel inside, but might even work without it.
Trick is from this thread: Can I access ItemsHost of ItemsControl using reflection?

private object FindItemControl(ItemsControl itemsControl, string controlName, object item)
{
ContentPresenter container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
container.ApplyTemplate();
return container.ContentTemplate.FindName(controlName, container);
}

Related

How to take Children of a ListView in Code-Behind

I have a ListView with some buttons(created in code-behind). I want to take all theese buttons and place them in a variable:
Button tg = (Button)sender;
ListView st = (ListView) tg.Parent;
var a = st.Children(this function doesn't work for ListView, but it's similar to what should resolve my problem) ;
foreach(Button btn in a)
Since ListView uses data virtualization to have a better performance. See https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/listview-and-gridview-data-optimization. So the buttons may not rendered when they are not in your viewport. This causes you can't get all buttons of your ListView.
But through some trick, we can disable ListView's data virtualization. Note that doing this may causes your app react slow when you have a mount of items in your ListView.
Suppose you want this way.
First, you need to modify ListView's ItemsPanel to StackPanel, according to doc, https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview#ui-virtualization.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Second, you can get all buttons using a helper function, it returns a List of your controls finded.
public static List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
List<T> list = new List<T>();
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
list.Add((T)child);
}
List<T> childItems = FindVisualChildren<T>(child);
if (childItems != null && childItems.Count() > 0)
{
foreach (var item in childItems)
{
list.Add(item);
}
}
}
}
return list;
}
Third, you can use it this way.
var listButtons = FindVisualChildren<Button>(listView);
foreach (var btn in listButtons )
{
//to do
}
Done!!!

How to increase pivot header height in UWP?

I currently creating apps in UWP (Universal Windows Platform). I was using pivot control and it seems I couldn't change the pivot item header height! It stays at 68 pixels. I have no idea which element should I modify to change it.
See this image for better explanation (click for full image):
I ran into the same problem but didn't want to use the code behind option.
Using the new Live Tree Debugger in Visual Studio I found that the height comes from the PivotHeaderItem default style.
If you add a copy of that style at an appropriate scope you can set the height using Xaml and you won't need any code.
I blogged about this with a sample project.
No need to copy the entire style for PivotHeaderItem, you can just override it directly in your Template for the Pivot. Here is the relevant section you need to modify from the Sample project.
<!-- While used here to remove the spacing between header items, the PivotHeaderItem template can also be used to
display custom 'selected' visuals -->
<Style TargetType="PivotHeaderItem">
<Setter Property="Height" Value="Auto" />
</Style>
I think i use same template than you.
So, to modify height i set Height on HeaderClipper (see screenshot below) on pivot style :
Hope this will help you.
UPDATE: Because pivot header height is static and can not be more than a small size, we have to set height manually after page is loaded
private void Page_Loaded(object sender, RoutedEventArgs e)
{
foreach (PivotHeaderItem phItem in FindVisualChildren<PivotHeaderItem(mainPivot))
{
phItem.Height = 110;
}
}
//Find all children
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
ref: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/8c123ae3-2884-4d40-a5d1-0a22355fcd5f/uwpxamlhow-to-increase-pivot-header-height-in-uwp?forum=wpdevelop

How to access controls inside an ItemTemplate

Using Windows Phone 8, C#.
What I've done is basically done is edited the pivot item. I've named it MainPivot and inside that I've edited the Pivot Item Title and added a TextBlock inside it called PivotTitletxt. XAML for that is:
<DataTemplate x:Key="DataTemplate3">
<TextBlock x:Name="PivotTitletxt" Height="34" TextWrapping="Wrap" Text="{Binding}" Width="447"/>
</DataTemplate>
How can I access this e.g. when setting opacity or changing foreground? so that I can use it on my MainPage like e.g. PivotTitletxt.Opacity = 30; ...
Thanks!
The link #Sankarann gave you is a pretty good example.
I'll try to put it on your scenario:
Your MainPivot has PivotItems right? So What you have to do on the Loaded event is:
var _mainPivot = MainPivot as Pivot
foreach (var _pivotItem in _mainPivot.Items)
{
var _container = _mainPivot.ItemContainerGenerator.ContainerFromItem(_pivotItem);
var _children = AllChildren(_container)
var _name = "PivotTitletxt";
var _control = (TextBlock)_Children.first(x=>x.Name == _name);
_control.Opacity = 30;
}
Then copy the AllChildren method exactly as the it is in the site.
The code above, might have a few adjustments because I've done it without VS...
Hope it helps.
Regards,
============ new answer ==============
Find all controls in WPF Window by type
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Then try :
TextBlock xx = FindVisualChildren<TextBlock>(mainPivot).FirsOrDefault(x=>x.name=="PivotTitletxt");
if(xx!=null)
xx.opacity = 30
Once again, this come might need some correction...i'm doing it by heart, without VS.
Try it out

Detect, if ScrollBar of ScrollViewer is visible or not

I have a TreeView. Now, I want to detect, if the vertical Scrollbar is visible or not.
When I try it with
var visibility = this.ProjectTree.GetValue(ScrollViewer.VerticalScrollBarVisibilityProperty)
(where this.ProjectTree is the TreeView)
I get always Auto for visibility.
How can I do this to detect, if the ScrollBar is effectiv visible or not?
Thanks.
You can use the ComputedVerticalScrollBarVisibility property. But for that, you first need to find the ScrollViewer in the TreeView's template. To do that, you can use the following extension method:
public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject obj)
{
foreach (var child in obj.GetChildren())
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
Use it like this:
var scrollViewer = ProjectTree.GetDescendants().OfType<ScrollViewer>().First();
var visibility = scrollViewer.ComputedVerticalScrollBarVisibility;
ComputedVerticalScrollBarVisibility instead of VerticalScrollBarVisibility
VerticalScrollBarVisibility sets or gets the behavior, whereas the ComputedVerticalScrollBarVisibility gives you the actual status.
http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer.computedverticalscrollbarvisibility(v=vs.110).aspx
You cannot access this property the same way you did in your code example, see Thomas Levesque's answer for that :)
Easiest approach I've found is to simply subscribe to the ScrollChanged event which is part of the attached property ScrollViewer, for example:
<TreeView ScrollViewer.ScrollChanged="TreeView_OnScrollChanged">
</TreeView>
Codebehind:
private void TreeView_OnScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.OriginalSource is ScrollViewer sv)
{
Debug.WriteLine(sv.ComputedVerticalScrollBarVisibility);
}
}
For some reason IntelliSense didn't show me the event but it works.

Get Children from ListBox can't get children that haven't been viewed

I've got a listbox in WPF like the following XAML. It's full of ListBoxItems that have a checkbox and a label inside of them. One of my items at the top is a "select all" option. When I click the select all option, I have a handler that iterates through all listbox items and it's supposed to check all the checkboxes on all the other listbox children. The problem is that it's only doing the visible children and when it hits the non-visible listboxitems, the VisualTreeHelper seems to be returning null when looking for objects of a specific type (like CheckBox). It seems that VisualTreeHelper seems to be problematic here. Am I using it wrong? Any help appreciated. One other detail - if I scroll and view all listboxitems at least once, it works fine.
mj
XAML - A simple listbox with a ton of children (only the 1st child displayed for brevity)
<ListBox Grid.Row="0" Margin="0,0,0,0" Name="CharacterListBox">
<ListBoxItem>
<StackPanel Orientation="Horizontal">
<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" Click="AllCharactersClicked"></CheckBox>
<Label Padding="5">All Characters</Label>
</StackPanel>
</ListBoxItem>
C# - Two functions, the first is a helper method which walks the object tree using VisualTreeHelper (I found this on some website). The second function is the click handler for the "select all" listboxitem. It iterates through all children and attempts to check all checkboxes.
private T FindControlByType<T>(DependencyObject container, string name) where T : DependencyObject
{
T foundControl = null;
//for each child object in the container
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(container); i++)
{
//is the object of the type we are looking for?
if (VisualTreeHelper.GetChild(container, i) is T && (VisualTreeHelper.GetChild(container, i).GetValue(FrameworkElement.NameProperty).Equals(name) || name == null))
{
foundControl = (T)VisualTreeHelper.GetChild(container, i);
break;
}
//if not, does it have children?
else if (VisualTreeHelper.GetChildrenCount(VisualTreeHelper.GetChild(container, i)) > 0)
{
//recursively look at its children
foundControl = FindControlByType<T>(VisualTreeHelper.GetChild(container, i), name);
if (foundControl != null)
break;
}
}
return foundControl;
}
private void AllCharactersClicked(object sender, RoutedEventArgs e)
{
MainWindow.Instance.BadChars.Clear();
int count = 0;
foreach (ListBoxItem item in CharacterListBox.Items)
{
CheckBox cb = FindControlByType<CheckBox>(item, null);
Label l = FindControlByType<Label>(item, null);
if (cb != null && l != null)
{
count++;
cb.IsChecked = true;
if (cb.IsChecked == true)
{
string sc = (string)l.Content;
if (sc.Length == 1)
{
char c = Char.Parse(sc);
MainWindow.Instance.BadChars.Add(c);
}
}
}
}
}
Those visual tree walking methods floating around all over the place are a plague. You should almost never need any of that.
Just bind the ItemsSource to a list of objects containing properties for the CheckBoxes, create a data template (ItemTemplate) and bind that property to the CheckBox. In code just iterate over the collection bound to ItemsSource and set the porperty.

Categories

Resources