How can I find all controls within a control(WPF, C#)? - wpf

I found interesting questions about this like in How can I find WPF controls by name or type?, but I just want my method to return all controls within it. It doesn´t matter the control´s name or its type as long as the method returns all possible controls he can find.

In WinForms this is very easy ... just grab the WinForms container and then prob the '.Controls' property and iterate over the collection of controls returned.
foreach (System.Windows.Forms.Control ctrl in form.Controls)
{
if (ctrl.Name == "tabPageControl")
{ // do something with 'tabPageControl object' }
{
As you can see in WinForms its dead easy to get access at the global container to return a 'ControlCollection' and then iterate through or even deeper if its a panel or something like that. Once you have found what you want are simply building a list of what can be found then do something with your list or your control.
In WPF, this is done slightly differenty. I don't have extensive WPF experience but after 15 mins of playing about I came up with this:
private void button1_Click(object sender, RoutedEventArgs e)
{
// cast out Grid object.
Grid grd = (Grid) this.Content;
// do simple testing to find out what the type is.
string s = grd.ToString();
// in VS, in debug mode, hover 'grd.Children' and Smart Tool Tip that pops
// it will tell exactly under a 'count' property how many controls there are sitting
// on the global container. For me it was just 1, my Button.
foreach (UIElement child in grd.Children)
{
// do some more testing to make sure have got the right control. pref in an If statement but anyhooo.
String sss = child.GetType().FullName;
// cast out the appropriate type.
Button myWpfButton = (Button)child;
}
}
I hope thats enought to get you started.

It depends on the type of parent control. If it is an extension of ContentControl it can only have a single child element, which is found under the Content property.
If it is an extension of Panel it can have many child elements, which are found under the Children property.
There is no guarantee that any of those child elements are necessarily controls though - you will need to do some type checking to confirm whether or not they are of the type you are interested in.
This is also only for a single level of parent-child hierarchy, but should be simple enough to make recursive if you need all child controls.

Related

Call LoadBaml() once for set of similar user controls

In my project I have to use WPF to place a big set of similar user controls(around 2000) on Canvas object. Basically, it's just a set of rectangles, that can change visibility, can be selected and store data object inside.
I add new controls with help of attached property like this:
public static readonly DependencyProperty VisualStaticBlocksProperty =
DependencyProperty.RegisterAttached("VisualStaticBlocks", typeof(ObservableCollection<VisualBlockViewModel>), typeof(BindableBlocksBehaviour),
new UIPropertyMetadata(null, VisualStaticBlocksPropertyChanged));
private static void VisualStaticBlocksPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
ClickSightView clickSight = source as ClickSightView;
ObservableCollection<VisualBlockViewModel> visualBlocks = e.NewValue as ObservableCollection<VisualBlockViewModel>;
if (clickSight != null && visualBlocks != null)
{
foreach (VisualBlockViewModel visualBlock in visualBlocks)
{
clickSight.StaticBlocksCanvas.Children.Add(new VisualBlockView(visualBlock));
}
}
}
However, it takes a lot of time to build all of them(around 2 seconds). I used a profiler to check that main problem is in LoadBaml() method, which is called in InitializeComponent() method.
As I understand, LoadBaml() is used to parse xaml markup. Is it possible somehow to cache the LoadBaml() result for component and reuse it instead of parse xaml each time I create new control instance?
EDIT:
To represent this set of objects visually I have created user control with Canvas on it, and created attached property VisualStaticBlocks to attachblock view models(type VisualBlockViewModel) to this control and insert visual block instances(type VisualBlockView) directly to Canvas.
EDIT2:
I've solved the problem by giving up using user controls for this purpose at all.
As my controls are quite simple, I used Rectangle() class instead with 3 manually added bindings and 3 manually added events. Of course, there were no InitializeComponent() calls at all.It allowed me to build the set of 2000 rectangles in 200 miliseconds, which is 10 times faster.
Anyway, still will be grateful for information if I can clone similar objects without loading BAML each time.
It sounds like you have an issue with the time it takes to create visual elements. I can see why you think you need to call InitializeComponent, but that is not how WPF works.
As noted here:
The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).
Which leads me to suspect you do not understand how (or why) InitializeComponent works; it is impossible to call it once to build multiple elements and externally, no less.
You are using ObservableCollection, which neither works well with large data sets nor complex views. Consider using a thread-safe ObservableCollection and add the data objects on a background thread. This shouldn't be an issue because you're adding data objects (view models) versus visual objects (views); visual objects should be added on the same thread (UI) they are created.
It would help to provide additional information as you have not explained how you represent these objects visually. Is the collection bound to an ItemsControl and does it define a DataTemplate to visually represent each data object?

Best method to find controls under a dropped usercontrol

I'm trying to implement a drag-drop function to a usercontrol.
I've managed to get this working properly with the use of google, however, when dropping a control I wish to find every similar type controls that might be or might not be under it.
My current way would be check every control and see if it's under the dropped control. But I wonder if there is a better way, like a find control function which can check for controls within a given range?
you could use this function:
http://msdn.microsoft.com/en-us/library/system.windows.media.visualtreehelper.findelementsinhostcoordinates%28v=vs.95%29.aspx
void xy_MouseMove(object sender, MouseEventArgs e)
{
if (m_IsDraging)
{
var res = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), elemenetWhichChildrenYouWantToCheck);
//... check res for drop elements and react
}
}
Or you can just use this function only in MouseLeftButtonUp event
Well..
First, the Logical Tree is the tree of sub-controls a control is made of.
Second, check this post

Adding controls causes silverlight to hang

I have a very strange issue that I'm finding it extremely hard to debug, thus I once more turn to the SO community. :-)
First a bit on my setup (note! I'm new to silverlight, just learning by doing, so my entire premise might be wrong!):
I have some root nodes (forms) under which are some leaves (questions).
Now, I simply want to show / hide the questions depending on which form is currently selected - fairly easy.
I do the following:
A number of controls are added to a LayoutControl (I'm using the DeveloperExpress components)
When you select another form, the created controls are saved in a list on the previously selected form
When you select a form, the list of controls is iterated through, and each element is added to my layoutcontrol again
When I save the elements in the list on a Form I make sure to first .Remove() them from their parent to make sure there are no issues with that.
And, this works.
If I have 1 or controls.
If I have MORE than that, everything is added as usual, no exception is thrown - but Silverlight apparently does an infinite loop somehow? No matter if I run it in IE or Chrome, it just crashes the browser! (Or, I guess, the browser plugin).
I've tried pausing the debugger during this to see where the problem is, but it just stops in "external code".
I don't think a code sample will do much good, but here's the two methods that save and load the elements: (Note: AddControl is a method I've defined - it wraps the element in another control to provide a bit of functionality. Likewise .MyChildren removes the control from the wrapper using .Remove() and returns it)
private void LoadElementsFromCurrentForm()
{
foreach (var child in _currentForm.Elements)
{
layoutControl1.AddControl(child);
}
}
private void SetElementsOnCurrentForm()
{
_currentForm.Elements.Clear();
foreach (var child in layoutControl1.MyChildren)
{
_currentForm.Elements.Add(child);
}
}
How do I even go about debugging this!?
I don't know if it matters, but all these controls have a unique name as well.
Regards
Søren
To take full advantage of Silverlight you should try to use XAML to do the markup of design.
If you want to make a list of questions use e Listbox or ItemsControl.
Then you can style each item in the list to "look right".
If you bind the listbox to ObservableCollection you just add or remove in the list and the gui will be updated according to whats in your list.
Take a look at MVVM which works really good with Silverlight.

Sharing same vector control between different places

I'm trying to implement the following: I have an Items Manager, that has an Item class inside. Item class can store two possible visual representations of it - BitmapImage(bitmap) and UserControl(vector).
Then later, in the game, I need to share the same image or vector control between all possible places it takes place. For example, consider 10 trees on the map, and all point to the same vector control. Or in some cases this can be bitmap image source.
So, the problem is that BitmapImage source can be easily shared in the application by multiple UIElements. However, when I try to share vector control, it fails, and says Child Element is already a Child element of another control. I want to know how to organize this in the best way. For example replace UserControl with other type of control, or storage, however I need to be sure it supports Storyboard animations inside.
The code looks like this:
if (bi.item.BitmapSource != null)
{
Image previewImage = new Image();
previewImage.Source = bi.item.BitmapSource;
itemPane.ItemPreviewCanvas.Children.Add(previewImage);
} else
if (bi.item.VectorSource != null)
{
UserControl previewControl = bi.item.VectorSource;
itemPane.ItemPreviewCanvas.Children.Add(previewControl);
}
Or it is not possible to share same control in different places, then what is the best way to make a copy, or the best way to store vector data.
Thanks in advance
So, I found the problem. It is possible to attach the same UserControl to different controls.
However, when on update I was deleting control, and then filling up it again with a new pointer, that sometimes was the same as before deleting, somehow it was still in memory. And so it was like 2 same user control attached to the same parent.
I added a line of code that was cleaning all children in control, before updating it with new vector UserControl, and now works like a charm.

Designing Windows.Form with multiple panels -> How to hide one panel (like a PS layer)

How can I hide one panel in Visual Studio 2008 Form Designer like a layer in PS? Otherwise, can someone recommend another better method to design multiple "screens" that must be clicked through by the user?
What you describe is a wizard, and you might want to investigate the approach from Eric J.
However, when I have cases where I want to have multiple panels in the same space within my UI and I want to switch between them in the designer, I like to use a TabControl and hide the tabs on the TabControl. This makes the UI easier to manage at design time and the code is pretty simple to switch between the tabs at run time.
I made a custom control that derives from TabControl called HiddenTabsControl that is very simple. The class only overrides the WndProc and lets the TabControl base class handle everything else. All you need to do is:
Add a New Item to your project
Choose Custom Control,
Name it something like HiddenTabsControl.
Change the base Class to TabControl, remove the Constructor and the OnPaint override that Visual Studio added.
Copy this override for WndProc into the class:
protected override void WndProc(ref Message m)
{
// Hide tabs by trapping the TCM_ADJUSTRECT message
if (m.Msg == 0x1328 && !DesignMode)
{
m.Result = (IntPtr)1;
}
else
{
base.WndProc(ref m);
}
}
Now you can change tabs in the designer and design the UI easily and in the code you can handle events to change tabs as needed. Changing the Selected tab is easily done with:
this.hiddenTabsControl.SelectedTab = this.tabPageYouWantVisible;
One side effect of removing the tabs is the space that the tabs occupy when the control is constructed. Removing them will make the space the HiddenTabsControl occupies change by shrinking it. I usually set the Anchor of the HiddenTabsControl to bottom to keep it from shrinking.
I used this Wizard code in a recent project and it worked well.
It provides the basic experience you are after.
Another less elegant, but quick hack, approach is to simply not add the panel to the parent form until runtime. In doing that, the designer has no idea where the panel belongs prior to compilation, and it won't be displayed.
For example, find the block of code where you add controls to the parent form:
//this->Controls->Add(this->panel_X);
this->Controls->Add(this->tabControl);
this->Controls->Add(this->menuStrip_topMenu);
Comment or remove the statement, then find the handle to the event that occurs when the form is loaded:
this->Load += gcnew System::EventHandler(this, &MainForm::MainForm_Load);
Then in the definition of the event handler, add the control to the form:
System::Void MainForm_Load(System::Object^ sender, System::EventArgs^ e) {
...
...
this->Controls->Add(this->panel_X);
}
I haven't experienced any unwanted side effects by doing this, but if anyone has a good reason to not I'd be interested in hearing it.

Resources