Panel.Children vs Panel.InternalChildren -- What's the difference? - wpf

According to MSDN - Panel.InternalChildren Property:
Classes that are derived from Panel
should use this property, instead of
the Children
property, for internal overrides such
as MeasureCore
and ArrangeCore.
So, this is really a 2 part question:
If I create a Panel of my own, FooPanel, which derives from Panel, I can't seem to override MeasureCore or ArrangeCore. I'm not sure why that statement is even there. I can, however, override MeasureOverride and ArrangeOverride. So, I wonder if I still need to use the InternalChildren property for these 2 methods.
What is the real difference between the Children property and the InternalChildren property?

You would override MeasureOverride and ArrangeOverride, that must be a mistake in the documentation, or intended for internal Microsoft employees. The MeasureCore and ArrangeCore are sealed by FrameworkElement, so you can't override them.
The Children property is public and simply calls InternalChildren, which is protected. So either is probably safe, since Children would get inlined.
MSDN says otherwise ( http://msdn.microsoft.com/en-us/library/ms754152.aspx) but the documentation is wrong. (use reflector to see that the implementation of Children simply calls InternalChildren)

EDIT:
As CodeNaked corrected - MSDN docs are in fact incorrect. InternalChildren and Children are the same.
Using reflector, you can see the implementation of Children which is public UIElementCollection Children { get { return this.InternalChildren; } }. So unless there is some voodoo going on, they are the same.
Children are just children that were added regularly, whereas InternalChildren includes children that were added through data binding (when the panel is the ItemsPanelTemplate)
"Children represents the child collection of elements that the Panel is
comprised of. InternalChildren represents the content of the Children
collection plus those members generated by data binding. Both consist
of a UIElementCollection of child elements hosted within the parent
Panel."
see http://msdn.microsoft.com/en-us/library/ms754152.aspx

Related

When is DataTemplate used in ContentControl and ItemsControl?

I have WPF controls that I created that inherits the FrameworkElement class. One of them is done like this:
[ContentProperty("Children")]
public class ItemsElement : FrameworkElement
{
public ItemsElement()
{
Children = new UIElementCollection(this, this);
}
public UIElementCollection Children { get; private set; }
protected override int VisualChildrenCount
{
get
{
if (Children != null)
{
return Children.Count;
}
return 0;
}
}
protected override Visual GetVisualChild(int index)
{
return Children[index];
}
protected override IEnumerator LogicalChildren
{
get
{
if (Children != null)
{
return Children.GetEnumerator();
}
return EmptyEnumerator.Instance;
}
}
protected override Size MeasureOverride(Size availableSize)
{
return availableSize;
}
protected override Size ArrangeOverride(Size finalSize)
{
return finalSize;
}
}
What I want with this code is to have elements that are not 2D-UI elements. I overrided the Measure and Arrange methods so that it does not compute Sizes of the Children. This is a behavior that I want because these computations are heavy and useless.
The problem is if I put ContentControls and ItemsControls in the Children of my ItemsElement class (through XAML), these controls do not seem to generate their Visual children from Content/ItemsSource with the DataTemplate I defined for them in the Resources.
It does work if I add this code at the beginning of the MeasureOverride method:
foreach (UIElement child in Children)
{
child.Measure(availableSize);
}
I can't understand why...
Do you have any idea?
Thanks!
The question really is why are you extending a class who's behaviour you don't want? The answer is too long for here, but you can read it in full in the Control Authoring Overview page on MSDN. But to answer in short, I can only assume that you are extending the wrong class. Maybe you don't even need to extend any class.
In WPF, there are many alternatives to writing a new control. Often the only real need to do that is to perform some special custom drawing for a unique control that cannot be composed by other means. As the linked page says, WPF enables you to customize existing controls by using its rich content model, styles, templates, and triggers. Further on it continues:
Controls that derive from UserControl or Control rely upon composing existing elements. For many scenarios, this is an acceptable solution, because any object that inherits from FrameworkElement can be in a ControlTemplate. However, there are times when a control's appearance requires more than the functionality of simple element composition. For these scenarios, basing a component on FrameworkElement is the right choice.
Finally, the MeasureOverride and ArrangeOverride methods are required to be implemented correctly in order for the FrameworkElement to render its items. You just need to look at MSDN to find this basic information out. From the FrameworkElement.MeasureOverride Method page:
Your implementation should do the following:
1.Iterate your element's particular collection of children that are part of layout, call Measure on each child element.
And from the FrameworkElement.ArrangeOverride Method page:
Parent elements should call Arrange on each child, otherwise the child elements will not be rendered.
Please read the linked pages for further information and in future, please look in MSDN for information regarding your problem control, method, event, etc. before asking questions here.
UPDATE >>>
In response to your somewhat offensive comment, I can see you do have a valid reason to extend the FrameworkElement class, but that doesn't change anything. You say that you've read all the pages that I showed you, but for some reason, you haven't followed the clear instructions on what you have to do to render the items correctly.
There's no need to answer your question regarding the DataTemplate and when exactly it is rendered because it is irrelevant to your problem. Your problem is caused simply because you have not followed the instructions found on MSDN in your class. That's it. One thing that you can check to see if this is correct or not is this.
If you see the full name of the type of objects in the collection when you run your application, then you have a problem with your DataTemplate not being applied. However, if you see no items, then you have a problem with your rendering and that's simply because you haven't implemented those two essential methods correctly.

Can custom UIElement implement IList<UIElement> and be assigned children directly (in XAML)?

Scenario: I have a range of custom UIElements (in fact, I have replaced all the standard WPF FrameworkElements I would use with lighter, more efficient counterparts) for a custom layout system intended to only use those. They all inherit from a class called Surface (which in turn is a direct descendant of UIElement).
I am now wondering if my version of Panel (let's call it SurfacePanel) can simply implement IList<Surface> and allow child Surface elements to be added directly to it, rather than to a Children property (as with regular WPF panels), in XAML.
To illustrate - in codebehind, I can do now this:
SurfacePanel.Add(child);
And from that, I would like to be able to do this in XAML:
<SurfacePanel>
<child />
</SurfacePanel>
But XAML seems to require me to have a codebehind pattern like this:
SurfacePanel.Children.Add(child)
(I don't really need these controls to support XAML to work in the runtime environment, but when testing and prototyping, I like to make my UI controls "XAML friendly" so I can benefit from the visual designer in VS (along with the property pane etc), if nothing more than as a 'preview' window).
Since my controls inherit from UIElement (and have the proper Measure/Arrange/Render overrides and so on), they function quite well when put on, say, a regular Canvasor Grid. But the VS XAML parser is not too happy about my SurfacePanel (that implements IList<Surface>) when I am adding children to it in markup. It says "Cannot add content to an object of type "SurfacePanel"".
I know that if I add a Children property of an appropriate type and add an attribute to the SurfaceCanvas class ([ContentProperty("Children")]), it will work. But since the SurfacePanel is itself a collection capable of the same thing, is there a way to make XAML 'get it'?
Edit:
I can solve the XAML 'compliance' by adding a Children property on the SurfacePanel that simply returns its inner List, but then adding and removal of elements on that directly bypasses the internal logic that wire the child elements up.
If the inner list was an ObservableCollection, I could do it the conventional way and do the wiring in a CollectionChanged event handler - but basically the whole point of integrating IList in the Panel directly is to avoid that..
Edit 2:
This "works" (but bypasses the wiring):
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList<Surface>
{
private readonly List<Surface> _children = new List<Surface>();
public List<Surface> Children
{
get { return _children; }
}
}
I cannot return this because SurfacePanel is not a List<Surface>, but an IList<Surface>.
If I change the property to
public IList<Surface> Children
{
get { return this; }
}
I get an error message even with the following XAML (but not with <m:SurfacePanel/>):
<m:SurfacePanel>
</m:SurfacePanel>
The error message is
Cannot set content property 'Children' on element 'SurfacePanel'. 'Children' has incorrect access level or its assembly does not allow access.
Also implement IList and declare the Children property like this:
[ContentProperty("Children")]
public class SurfacePanel : Surface, IList, IList<Surface>
{
public IList Children
{
get { return this; }
}
...
}

Why does the Parent property of a container of an ItemsControl return null and not the Panel it sits on?

This one has me stumped. We have a custom ItemsControl which uses both custom containers as well as a custom panel as its ItemsHost. Now the panel has some metrics that the containers need for rendering purposes. Since they are direct children of the panel in the visual tree, you'd think that the Parent property of the container would return the panel, but it doesn't!
I have also confirmed this exact thing using Snoop on a standard ListBox so this isn't exclusive to our code, but apparently all containers of ItemsControls.
Now I know I can use the VisualTreeHelper to get the visual parent (which is what I need) but why would the parent not be the panel?
If the argument is that the panel is simply part of the Visual Tree and Parent is reserved for the Logical Tree, then wouldn't the parent be the ItemsControl?
If the argument there is the container too is part of the ItemsControl's visual tree and not the logical tree, then why would the contents hosted in the container return the container as its Parent property?
That means if you're walking the logical tree from a data item, you stop at the containers, which may explain why our bindings from the containers to the panels aren't working as expected. (I believe bindings are based on a logical hierarchy and not a visual one, but I'd have to test to be sure.)
I never noticed that and this spiked my curiosity.
After looking for clues in the .Net Framework in found that Parent property seems indeed to be set manualy:
This required several steps but I found that the only way to change the parent property is to invoke these methods:
If I analyse for example the FrameworkElement.AddLogicalChild method, I found that these methods are using it:
This confirms that the parent property is supposed to refer to the logical tree.
I tried to create my own custom control:
[ContentProperty("CustomContent")]
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public object CustomContent
{
get { return GetValue(CustomContentProperty); }
set { SetValue(CustomContentProperty, value); }
}
public static readonly DependencyProperty CustomContentProperty = DependencyProperty.Register("CustomContent", typeof(object), typeof(CustomControl1));
}
with this template:
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<ContentPresenter ContentSource="CustomContent" />
</ControlTemplate>
I used it this way:
<WpfApplication1:CustomControl1 Width="50" Height="50">
<Rectangle Fill="Red" />
</WpfApplication1:CustomControl1>
... this worked like this (like a charm :-)):
... and guess what... Parent of the rectangle is not set :-)
I don't have time to continue investigating for now but regarding ItemsControl, I imagine that maybe the ItemContainerGenerator doesn't know the logical parent in which it inserts itemsContainers, that could explain why parent property is not set in this case... but that need to be proven...
The FrameworkElement.Parent property documentation says it may be null e.g. for items created in datatemplates. In such case they propose using FrameworkElement.TemplatedParent:
For templates, the Parent of the template eventually will be null. To
get past this point and extend into the logical tree where the
template is actually applied, use TemplatedParent.
May be it's your case? It helped me in similar case (I used Parent then if it's null used TemplateParent as fallback).
Yes, the answer is late but it may help others who stumbles on same error as me

How can an AttachedProperty have multiple values?

How can an AttachedProperty which is a single property defined by an owning parent element, be set with multiple values through several child elements of that parent?
For example:
If I have:
<DockPanel>
<CheckBox DockPanel.Dock="Top">Hello</CheckBox>
<CheckBox DockPanel.Dock="Bottom">World</CheckBox>
</DockPanel>
Here we have a single DockPanel element and it has a single Dock property. How can it be set to "Top" and then "Bottom" simultaneously?
It will end up in a method looking like this
public class DockPanel : Panel
{
public static readonly DependencyProperty DockProperty;
// ...
public static void SetDock(UIElement element, Dock dock)
{
element.SetValue(DockProperty, value);
}
}
As you can see, it's actually not set on the parent, but the CheckBox itself, through the static method SetDock on DockPanel and not the parent instance. Doing it in code behind makes this a little clearer, notice how we never use an instance of a DockPanel.
DockPanel.SetDock(checkBox1, Dock.Top);
DockPanel.SetDock(checkBox2, Dock.Bottom);
Hopefully this was clear, unless your question was how this works "under the hood". In that case, see this question.
Quote from link.
The purpose for this mechanism is to
"attach" to other objects information
needed by parent objects, not the
child objects themselves.
A CheckBox has no use for a Dock property unless it is in a DockPanel. Same goes for Grid.Row, Canvas.Left, Validation.HasError (read only) etc. So basically, the DockPanel is the one needing the information, but it needs all its childs to be able to store it. Hence, it's using an Attached Property for it. If you created a new Panel, called PuneetPanel, and you needed an Angel to calculate the child position, then you could define your own Attached Property, PuneetPanel.Angel inside this panel and all childs could use this without having to be subclassed.
This is a very nice question. The answer lies in how the AttchedProperty works. The AttachedProperty is used by the parent to render a child. Before rendering the child, the parent looks out for any attached property defined on child and applies to the child.
I found this from msdn which might be useful for you ::
DockPanel defines the DockPanel.Dock attached property, and DockPanel has class-level code as part of its rendering logic (specifically, MeasureOverride and ArrangeOverride). A DockPanel instance will always check to see whether any of its immediate child elements have set a value for DockPanel.Dock. If so, those values become input for the rendering logic applied to that particular child element....
You can see this link to get detailed overview ::
http://http://msdn.microsoft.com/en-us/library/ms749011.aspx
Hope it helps you!!
For your own custom attached properties there are two options to achieve what you are looking for:
1. If the number of combinations of settable values are not to complex you could make your attached property of type enum that has the FlagsAttribute set. You can the combines the values you want to set using bitwise-or |:
[Flags]
public enum MultiDock
{
Left,
Top,
Right,
Bottom
}
And its usage in code:
MyCustomPanelOrWhatever.SetMultiDock(MultiDock.Left | MultiDock.Bottom);
This has one small proplem though, you can not do the above in xaml directly, you would have to write a MarkupExtension that can convert string to flagged enum values. Its usage would then look like this:
<CheckBox src:MyCustomPanelOrWhatever.MulitDock="{src:FlaggedEnum Left|Bottom}" />
2. Since attached properties can be of any type, they can of course also be complex types (with multiple subproperties) or even collections, so it is easily possible to do something like this:
MyCustomPanelOrWhatever.SetMultiDock(new List<MultiDock> { MultiDock.Left, MultiDock.Bottom });
If you have defined your attached property that way, you do not need any converters for xaml, you can use it directly:
<CheckBox>
<src:MyCustomPanelOrWhatever.MultiDock>
<src:MultiDock.Left/>
<src:MultiDock.Bottom/>
</src:MyCustomPanelOrWhatever.MultiDock>
</CheckBox>

WPF element that dynamically creates (encapsulated) children at runtime

I want to create a WPF element that, at runtime, is in full control of its child elements -- adding and removing child UI when its properties change. Something a bit like what ItemsControl does when you modify its ItemsSource property, though in my case there'll only be one child.
This will be a view container for MVVM -- when you give it a Model or a ViewModel, it will magically create the correct View and wire everything up. There's no need for my view container to be templatable (since it creates user-defined views, which are UserControls and have their own templates), and I'd prefer that it encapsulate as much as possible. I could probably do this easily by descending from something like Grid, and adding child controls when my own properties change; but Grid publicly exposes its collection of child elements and lets anyone add and remove stuff.
Which WPF class should I descend from for maximum encapsulation, and how do I add child elements to it at runtime?
Based on my understanding of the docs, I tried using FrameworkElement and AddVisualChild, just to see if I could create child controls at runtime. I'm not clear on whether the AddLogicalChild is necessary, but I put it in just in case:
public class ViewContainer : FrameworkElement {
private TextBlock _child;
public ViewContainer() {
_child = new TextBlock { Text = "ViewContainer" };
AddLogicalChild(_child);
AddVisualChild(_child);
InvalidateMeasure();
}
public object Content { get; set; }
protected override Size ArrangeOverride(Size finalSize) {
_child.Arrange(new Rect(finalSize));
return finalSize;
}
protected override Size MeasureOverride(Size availableSize) {
_child.Measure(availableSize);
return _child.DesiredSize;
}
}
When I put a ViewContainer into a Window, and run this, I expect to see a TextBlock saying "ViewContainer". But instead, I just see a blank window. So obviously I'm missing something.
How can I fix the above code so that the "child" control does appear at runtime, but isn't exposed for others to mess with (any more than can be avoided)?
To answer your specific question, you'll also need to override GetVisualChild and VisualChildrenCount properties to enable your child element to be displayed.
Have you thought about taking advantage of WPF's support for implicit DataTemplates?
The way I have handled a requirement similar to yours is by using a ContentControl. I bind the Content property to my ViewModel. I then make sure that in Resource Dictionaries referenced somewhere in the tree above the ContentControl I have DataTemplates defined for all the types of ViewModels that might be assigned to the Content Property.
This way WPF takes care of wiring up the correct view for my ViewModel.

Resources