I need to find a ScrollViewer inside current TabItem and then find a WrapPanel inside that ScrollViewer
I tried this:
TabItem ti = tabControl.SelectedItem as TabItem;
foreach (ScrollViewer sv in ti.Content)
{
foreach (WrapPanel wp in sv.Content) {}
}
and this
TabItem ti = tabControl.SelectedItem as TabItem;
foreach (ScrollViewer sv in ti.Children)
{
foreach (WrapPanel wp in sv.Children) {}
}
But doesn't work
If your tab item contains your scrollviewer directly, you could do the following :
TabItem ti = tabControl.SelectedItem as TabItem;
ScrollViewer sv = ti?.Content as ScrollViewer;
WrapPanel wp = scrollViewer?.Content as WrapPanel;
Another way of accessing your WrapPanel would be to use a function that returns a child/content of a specific type. For instance
public T FindVisualChildOrContentByType<T>(DependencyObject parent)
where T : DependencyObject
{
if(parent == null)
{
return null;
}
if(parent.GetType() == typeof(T))
{
return parent as T;
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if(child.GetType() == typeof(T))
{
return child as T;
}
else
{
T result = FindVisualChildOrContentByType<T>(child);
if (result != null)
return result;
}
}
if(parent is ContentControl contentControl)
{
return this.FindVisualChildOrContentByType<T>(contentControl.Content as DependencyObject);
}
return null;
}
Then you would be able to do
WrapPanel wp = this.FindVisualChildOrContentByType<WrapPanel>(tabItem);
If this is not working, feel free to post your XAML so i can reproduce your exact scenario.
Related
I have a series of controls that can be passed around and on occasion they are added as a child to another control.
In many cases I cannot know for certain if the control is currently the child of another control.
For example :
Label lbl = new Label( );
/*Stuff happens; lbl may be assigned or not; whatever.*/
//If the label has already been assigned as a child to a viewbox
if ( lbl.Parent is Viewbox )
return lbl.Parent;
else {
if (lbl.Parent != null )
//Of course this fails because there is no lbl.Parent.Child property.
lbl.Parent.Child = null;
return new Viewbox( ) { Child = lbl, Stretch = Stretch.Uniform };
}
It is entirely possible I am completely misinterpreting the function of the Control.Parent property.
Is it possible to orphan a control from it's parent through the control itself?
void RemoveElementFromItsParent(FrameworkElement el)
{
if (el.Parent == null)
return;
var panel = el.Parent as Panel;
if (panel != null)
{
panel.Children.Remove(el);
return;
}
var decorator = el.Parent as Decorator;
if (decorator != null)
{
decorator.Child = null;
return;
}
var contentPresenter = el.Parent as ContentPresenter;
if (contentPresenter != null)
{
contentPresenter.Content = null;
return;
}
var contentControl = el.Parent as ContentControl;
if (contentControl != null)
contentControl.Content = null;
}
source: https://stackoverflow.com/a/19318405/1271037
i'm starting with WPF and i have a problem. Please help me. Thanks, sorry for my bad English!
I have added ContentControl to a Canvas, and i want to remove/delete it.
Draw ContentControl code:
ContentControl cc = new ContentControl();
cc.Content = shape;
cc.Height = h;
cc.Width = w;
Style s = myCanvas.FindResource("DesignerItemStyle") as Style;
cc.Style = s;
Canvas.SetLeft(cc, x);
Canvas.SetTop(cc, y);
myCanvas.Children.Add(cc);
I use HitTest to remove it but i can remove only shape
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((Canvas)sender);
HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);
if (result != null)
{
myCanvas.Children.Remove(result.VisualHit as Shape); //it works with shape
// i have changed it into myCanvas.Children.Remove(result.VisualHit as ContentControl);
//but it didn't work with ContentControl
}
}
It is so because the ContentControl is the parent of the Shape, and the Canvas's children contains the ContentControl that hosts the shape.
You could do this to fix your issue :)
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
DependencyObject ob = FindAncestor<ContentControl>((DependencyObject)e.Source);
if (ob.GetType() == typeof(ContentControl))
myCanvas.Children.Remove(ob as ContentControl);
}
public T FindAncestor<T>(DependencyObject dependencyObject)
where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindAncestor<T>(parent);
}
I suggest you the next solution:
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var dObj = sender as DependencyObject;
if(dObj == null) return;
var ob = dObj.GetChildOfType<ContentControl>();
if (ob != null)
{
myCanvas.Children.Remove(ob);
}
}
Helper code:
public static T GetChildOfType<T>(this DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
just put the helper inside the public static class as it is an extension method
regards
You can add the control, for example:
Rectangle r = new Rectangle() { Name = "MyName" };
and to remove from the canvas:
UIElement element = StationLayout.FindName ("MyName") as UIElement;
StationLayout.Children.Remove(element);
I am trying to add HubSections dynamically (C# code) - that works.
Then from the same code I want to add ListBoxes to each of 'em - and apparently I have no idea how to do that.
I found several examples like adding:
ContentTemplate = new DataTemplate() { VisualTree = ... }
... to HubSection constructor but there is no VisualTree in DataTemplate.
Please, ask for any details if my problem description is too vague - I am a WP8.1 newbie so I could skip some important info.
The best way I found is to create DataTemplate like this:
public class ViewSection : HubSection {
public ViewSection(View view) {
string xaml = "<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><StackPanel /></DataTemplate>";
ContentTemplate = XamlReader.Load(xaml) as DataTemplate;
this.Loaded += ViewSection_Loaded;
}
private void ViewSection_Loaded(object sender, RoutedEventArgs e) {
base.OnApplyTemplate();
StackPanel stackPanel = findStackPanelInSubtree(this);
...
< adding stuff to stack panel >
...
this.Loaded -= ViewSection_Loaded;
}
private StackPanel findStackPanelInSubtree(FrameworkElement element) {
if (element != null) {
if (element.GetType() == typeof(StackPanel)) {
return element as StackPanel;
}
foreach (FrameworkElement child in getChildren(element)*) {
StackPanel result = findStackPanelInSubtree(child);
if (result != null) return result;
}
}
return null;
}
private List<FrameworkElement> getChildren(FrameworkElement element)* {
if (element != null) {
List<FrameworkElement> result = new List<FrameworkElement>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
result.Add(VisualTreeHelper.GetChild(element, i) as FrameworkElement);
}
return result;
}
return null;
}
}
Is this some kind of cruel joke of Microsoft devs or there is a better way to do this?
* of course this is totally redundant but foreach makes it sooo much nicer to read
Unavailability of the whole children collection looks not really convenient. You may however rewrite your handy getChildren method to exhibit yield keyword usage:
private List<FrameworkElement> getChildren(FrameworkElement element) {
if (element != null) {
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
yield return (VisualTreeHelper.GetChild(element, i) as FrameworkElement);
}
}
yield break;
}
If I've got this in my XAML:
<Button Name="MyButton" Content="Hello" />
Then I can see that the value of MyButton.Content.ToString() is Hello.
But if I've got something like this in my XAML:
<Button Name="MyButton">
<StackPanel>
<Label Content="Hello" />
</StackPanel>
</Button>
Then suddenly MyButton.Content.ToString() is System.Windows.Control.StackPanel.
What's the best way to effectively "flatten" the content of a FrameworkElement and looking the actual text content? So in this second case it should also return Hello like the first.
Recursion
string fetchContentString(object o)
{
if (o == null)
{
return null;
}
if(o is string)
{
return o.ToString();
}
if(o is ContentControl) //Button ButtonBase CheckBox ComboBoxItem ContentControl Frame GridViewColumnHeader GroupItem Label ListBoxItem ListViewItem NavigationWindow RadioButton RepeatButton ScrollViewer StatusBarItem ToggleButton ToolTip UserControl Window
{
var cc = o as ContentControl;
if (cc.HasContent)
{
return fetchContentString(cc.Content);
}
else
{
return null;
}
}
if(o is Panel) //Canvas DockPanel Grid TabPanel ToolBarOverflowPanel ToolBarPanel UniformGrid StackPanel VirtualizingPanel VirtualizingPanel WrapPanel
{
var p = o as Panel;
if (p.Children != null)
{
if (p.Children.Count > 0)
{
if(p.Children[0] is ContentControl)
{
return fetchContentString((p.Children[0] as ContentControl).Content);
}else
{
return fetchContentString(p.Children[0]);
}
}
}
}
//Those are special
if(o is TextBoxBase) // TextBox RichTextBox PasswordBox
{
if(o is TextBox)
{
return (o as TextBox).Text;
}
else if(o is RichTextBox)
{
var rt = o as RichTextBox;
if (rt.Document == null) return null;
return new TextRange(rt.Document.ContentStart, rt.Document.ContentEnd).Text;
}
else if(o is PasswordBox)
{
return (o as PasswordBox).Password;
}
}
return null;
}
Give it a ContentControl,Panel or a TextboxBase and it should give you the first string content it finds.
in the Panel its whatever the first child leads to, in the TextBox base its the Password/Text/Document properties with some help from https://msdn.microsoft.com/en-us/library/bb613548%28v=vs.110%29.aspx#classes_that_contain_arbitrary_content
i haven't tested deeply just the 2 samples you provided but thats probably the way to go.
I'm using VisualTreeHelper.GetChildrenCount() to find child controls, but it always return 0.
Here is my code
<ScrollViewer x:Name="scrollViewerChannelsRecordTimeData">
<StackPanel x:Name="channelsRecordTimeData">
<ItemsControl x:Name="channelRecordTimeItems" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="hoursLines">
//Some Controls here
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
C# code:
channelRecordTimeItems.ItemContainerGenerator.StatusChanged += ChannelRecordTimeItemsStatusChangedEventHandler;
private void ChannelRecordTimeItemsStatusChangedEventHandler(Object sender, EventArgs e)
{
if (channelRecordTimeItems.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (channelRecordTimeItems.HasItems)
{
DependencyObject dependencyObject = null;
Grid gridHighlightRecordData = null;
for (int i = 0; i < channelRecordTimeItems.Items.Count; i++)
{
dependencyObject = channelRecordTimeItems.ItemContainerGenerator.ContainerFromIndex(i); //dependencyObject != null
if (dependencyObject != null)
{
Grid hoursLines = FindElement.FindChild<Grid>(dependencyObject, "hoursLines"); //hoursLines = null
}
}
}
}
}
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); //Return 0 here
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T 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;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
VisualTreeHelper.GetChildrenCount() always return 0,
The code for constructing for items here
List<ChannelRecordTimeItemData> listChannelRecordTimeItemData = new List<ChannelRecordTimeItemData>();
for(int i = 0; i < 5; i++)
{
ChannelRecordTimeItemData item = new ChannelRecordTimeItemData();
listChannelRecordTimeItemData.Add(ChannelRecordTimeItemData);
}
channelRecordTimeItems.ItemsSource = listChannelRecordTimeItemData;
channelRecordTimeItems.Items.Refresh();
I have searched on forum and internet, but i can not solve it, someone can help me?
Many thanks!
T&T
The problem is that when the ItemContainerGenerator signals the ContainersGenerated status, the container (a ContentPresenter) has been created, but not yet loaded. Especially the data template has not yet been applied to the ContentPresenter, hence there is nothing in the visual tree.
You may get around this by adding a Loaded event handler when looping over the generated containers.
private void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
{
if (itemsControl.ItemContainerGenerator.Status
== GeneratorStatus.ContainersGenerated)
{
var containers = itemsControl.Items.Cast<object>().Select(
item => (FrameworkElement)itemsControl
.ItemContainerGenerator.ContainerFromItem(item));
foreach (var container in containers)
{
container.Loaded += ItemContainerLoaded;
}
}
}
private void ItemContainerLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
element.Loaded -= ItemContainerLoaded;
var grid = VisualTreeHelper.GetChild(element, 0) as Grid;
...
}
If your using Caliburn.Micro this will help you.
For your Viewmodel the base Class should be Screen then only VisualTreeHelper.GetChildrenCount() give no.of childs.(because Screen will Activate all childs
or
otherwise (FrameworkElement)YourParent).ApplyTemplate() method