When are the elements of a ContentProperty actually created? - wpf

I am creating a custom WPF control that uses the following markup:
<custom:FilterPanel
Grid.Row="1"
FilterTarget="{Binding Path=MyItems}">
<custom:FilterParameter
ParameterName="Name"
TargetProperty="Name" />
<custom:FilterParameter
ParameterName="Date"
TargetProperty="MyDate" />
</custom:FilterPanel>
I've set the ContentProperty for my FilterPanel to FilterParameters, which is obviously a collection of FilterParameter objects that I add items to using the markup above. My question is, when are the elements of a ContentProperty actually processed so that instances are created and items are actually added to the underlying collection?
I'm interested in sharing the data source of the parent control with its children, is there any point in the WPF lifecycle where I can override this behavior and add custom logic to the creation of this collection of FilterParameters?

Assuming your class derives from Panel, then the first point in the Panel's lifecycle where you can see children (i.e. children that are decalred in XAML like your example - not children generated via bindings) is Panel.EndInit(), a virtual method you can override in your derived class. Specifically the chldren created between the BeginInit and EndInit methods.

Related

What is the most minimal xaml element that can change a DataContext?

I'm wondering what element can be used, when the only thing you want to change is the DataContext.
I'm aware that I could wrap it in a grid or something, but all of those are rather heavy weight when I just need to change the data context for a single element, so that I can more easily bind to attributes without them getting too long.
The concept of the WPF class hierarchy is described at Microsoft: WPF Architecture. There you can find the System.Windows.FrameworkElement:
The two most critical things that FrameworkElement introduces are data binding and styles.
Checking the properties of FrameworkElement, there the DataContext property is defined. This means the following capability is available:
Support for data binding and dynamic resource references: The property-level support for data binding and resources is implemented by the DependencyProperty class and embodied in the property system, but the ability to resolve a member value that is stored as an Expression (the programming construct that underlies both data binding and dynamic resources) is implemented by FrameworkElement. For more information, see Data Binding Overview and XAML Resources.
The Data​Context property is now available and used at all inherited controls, which you can see at the following class hierarchy:

Bind an XAML event to class other than file root

Windows Phone 8 project, XAML. I have a ListBox inside a pivot item that's of my own class MyPivotItem (derived from vanilla PivotItem) inside a page. The listbox has an ItemTemplate with some controls. I'd like to bind an event in one of those controls to a method in MyPivotItem. The plain syntax Click="OnClick" does not work - the frameword searches for the method in the Page class only.
I could derive the control itself and do some trickery with tree navigation and event forwarding and so on, but I wonder if such a scenario can be served by XAML's internal means.
Is there any way to bind methods non-programmatically to a class that's deeper in the hierarchy than the root object of the XAML file?
Without using something like behaviors or attached properties, it is not possible. XAML file is always associated with partial C# class and in this class you define your controls and events. For every Page.xaml, a Page.g.xaml file is generated that actually creates controls and binds events. However, those events must be defined in root object - the partial class itself.
Even if you create a workaround using either attached properties or behaviors, you are simply offloading that programmatic code to some other place and hiding it behind XAML syntax.
When would you want to bind to control's specific event handlers anyway? Can you simply use UserControls for that?

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 to link container and its contents?

i have an object based on ContentControl type and I want to embed custom controls into its content. below is the code.
the problem is that i need MyContainer to have a list of MyControl objects so that it can communicate to them, and each MyControl will need a reference to its MyContainer.
how is this done properly? one way that i see is to declare an attached property on MyControl and set it to the name of the MyContainer object, but this seems redundant because MyCOntrol objects can search the visual tree to find the container. if searching is the right way to do this, where would i place the code that does the search? in MyControl constructor?
thanks for any input
konstantin
public class MyContainer : ContentControl
{
...
}
public class MyConrol : Control
{
...
}
<c:MyContainer>
<Grid>
<c:MyControl />
</Grid>
</c:MyContainer>
You can add property MyControls to MyContainer class, create a template for MyContainer with a list in it (ItemsControl, ListBox or some other list control), put the list itself inside the grid from your sample code, bind the list's ItemsSource to MyControls property.
To get container for the control in XAML, you can use binding with RelativeSource set to FindAncestor.
If you need to find container from code, you should probably do it every time or cache the value on the first use (can controls be moved to another container?). Contructor is not the appropriate place, because first control is created and only then it is put into the tree.
Attached properties are definitely unnecessary.

How exactly do Attached Properties work in WPF?

I'm a bit mystified as to how Attached Properties actually convey their values to either parent or child elements. TextElement.FontFamily causes child elements to inherit the value assigned to that property (a seemingly downstream operation, parent to child). Grid.Column causes a parent item to display that child in a particular position (a seemingly upstream operation, child to parent). How do Attached Property values know to either flow up or down? Is my conception of this incorrect, or is there a piece missing that will put all of this into perspective?
<StackPanel TextElement.FontFamily="Wingdings">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Content="My Button"/>
</Grid>
</StackPanel>
There are two concepts here: dependency properties and attached dependency properties. "Attached Properties" are dependency properties, and as such support dependency property value inheritance.
About basic dependency properties, a very rough statement would be that they basically inherit their values from parent elements in the wpf (logical/visual) tree. A dependency property (attached or not) inherits its value "downwards" if its metadata is set with the FrameworkPropertyMetadataOptions.Inherit flag, and in many cases this is so.
Attached properties are properties which can be set on any wpf object (basically, at least a DependencyObject) via the DependencyObject.SetValue method. The purpose for this mechanism is to "attach" to other objects information needed by parent objects, not the child objects themselves. For example, the Grid.Row is an attached property required by the Grid to place items within its render area.
Dependency properties are inherited "downwards" automatically by the wpf object system.
Attached properties are examined "upwards" explicitly, in the code of specific objects. In the case of Grid, upon determining where to place its items, it checks for the value of Grid.Row and Grid.Column attached properties on each contained item.
It is also often the technique to create custom attached properties which modify in some way the objects they are attached to (for example, the Drag'n'Drop functionality via attached properties).
As an additional note, a good example of an inheriting attached property is TextElement.FontFamily. Grid.Row and Grid.Column properties do not have the Inherits flag set.
TextElement.FontFamily, from Reflector:
FontFamilyProperty = DependencyProperty.RegisterAttached("FontFamily", typeof(FontFamily), typeof(TextElement), new FrameworkPropertyMetadata(SystemFonts.MessageFontFamily, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure), new ValidateValueCallback(TextElement.IsValidFontFamily));
Grid.Row, from Reflector:
RowProperty = DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid), new FrameworkPropertyMetadata(0, new PropertyChangedCallback(Grid.OnCellAttachedPropertyChanged)), new ValidateValueCallback(Grid.IsIntValueNotNegative));
From MSDN:
Although attached properties are settable on any object, that does not automatically mean that setting the property will produce a tangible result, or that the value will ever be used by another object. Generally, attached properties are intended so that objects coming from a wide variety of possible class hierarchies or logical relationships can each report common information to the type that defines the attached property. The type that defines the attached property typically follows one of these models:
The type that defines the attached
property is designed so that it can
be the parent element of the elements
that will set values for the attached
property. The type then iterates its
child objects through internal logic
against some object tree structure,
obtains the values, and acts on those
values in some manner.
The type that defines the attached
property will be used as the child
element for a variety of possible
parent elements and content models.
The type that defines the attached
property represents a service. Other
types set values for the attached
property. Then, when the element that
set the property is evaluated in the
context of the service, the attached
property values are obtained through
internal logic of the service class.
An Example of a Parent-Defined Attached Property
The most typical scenario where WPF defines an attached property is when a parent element supports a child element collection, and also implements a behavior where the specifics of the behavior are reported individually for each child element.
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. Nested DockPanel instances each treat their own immediate child element collections, but that behavior is implementation-specific to how DockPanel processes DockPanel.Dock values. It is theoretically possible to have attached properties that influence elements beyond the immediate parent. If the DockPanel.Dock attached property is set on an element that has no DockPanel parent element to act upon it, no error or exception is raised. This simply means that a global property value was set, but it has no current DockPanel parent that could consume the information.
In simple words this is how I understand it (please correct me if I'm wrong).
An object (A) implements a property that will attach to another object (B) (object B doesn't even know about the existence of this "attachable" property). Object B needs to inherit from DependencyObject.
Object A also implements a static method to check for it's "attachable" property in other objects, A.GetAttachedProperty(B).
If B has the attached property from A, A.GetAttachedProperty will read and return it's value. Otherwise A will try to read it, and return null since it's not there.

Resources