How can an AttachedProperty have multiple values? - wpf

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>

Related

WPF schema - what defines a ListBox to have a ScrollViewer?

For years, I have felt I don't have a good understanding of WPF because I haven't found an authoritative reference on the possibilities. For example, I just found out that a ListBox has an attached ScrollViewer property.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Auto">
Other than reading a book or article that tells that, how would I know that ScrollViewer is a valid attached property? Is there a XAML schema document or something? How does Visual Studio Designer know?
ScrollViewer isn't an attached property; it's a class.
ScrollViewer.HorizontalScrollBarVisibility is an attached property. But it's not an attached property that ListBox "has"; it's an attached property that can be attached to any DependencyObject at all, including ListBox.
Here's what you see if you right click on ScrollViewer.SetHorizontalScrollBarVisibility and ScrollViewer.GetHorizontalScrollBarVisibility. A pair of static methods like this is required for an attached property. The first parameter is the thing you're attaching the property to. It doesn't have to be DependencyObject; it could be FrameworkElement, ListBox, ItemsControl, or anything else that can support dependency properties.
// Summary:
// Sets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property to a given element.
//
// Parameters:
// element:
// The element on which to set the property value.
//
// horizontalScrollBarVisibility:
// The property value to set.
public static void SetHorizontalScrollBarVisibility(DependencyObject element, ScrollBarVisibility horizontalScrollBarVisibility);
//
// Summary:
// Gets the value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property from a given element.
//
// Parameters:
// element:
// The element from which the property value is read.
//
// Returns:
// The value of the System.Windows.Controls.ScrollViewer.HorizontalScrollBarVisibility
// dependency property.
public static ScrollBarVisibility GetHorizontalScrollBarVisibility(DependencyObject element);
The ListBox itself quite likely has no clue what ScrollViewer.HorizontalScrollBarVisibility means, or even that it exists. But in the ListBox's template, there's probably a ScrollViewer, which will probably have a binding a lot like this:
<ScrollViewer
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
Put that attached property on any control that might have a ScrollViewer in its template, and if the template was written correctly, the ScrollViewer will use that value.
This is really, really nice because ListBox doesn't have to think about its own scrolling behavior. That's all delegated. Building controls by composition is extremely powerful.
The downside is that the whole thing is just a vast box of undifferentiated barf and it's real hard to make sense out of stuff. Intellisense can't tell you much about the scrolling behavior of ListBox when nobody outside that particular ListBox's template at the moment can even guess what that scrolling behavior might be.
So, in answer to your question: Yes. Basically you just have to read a lot of stuff. And keep a "Cool XAML Tricks" text file to note down cool stuff you hear about that you don't have a use for just yet.
But that's the way this profession has always been. Even with Intellisense, you can't use a class you don't know about.
Today I learned about ColumnDefinition.SharedSizeGroup and Grid.IsSharedSizeScope, and I learned that you can derive value converters from MarkupExtension and give them strongly typed, named properties instead of having to pass some goofy string into CommandParameter.
I believe the best docs we have right now are the MSDN documentation pages for WPF controls. For example, if you look up ListBox, you can find information about the attached ScrollViewer, and also it's full default template.
https://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx

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

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

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

How can I animate a property dynamically in a Silverlight 4 UserControl?

I've run into a puzzling limitation in a Silverlight 4 UserControl.
What I'm trying to achieve is to have a panel, which slides out from a minimised state when a button is pressed, but the title bar of it should be draggable with which this maximised state can be resized.
What I've done for the sliding out is to animate the MaxHeight property of the parent Grid of this panel which works quite well even with no hardcoded Height for the panel, but I don't know how can I make this dynamic.
Trying to bind a variable from the code-behind to the 'To' parameter of the 'DoubleAnimation' didn't work, it just silently gets ignored.
As I'm creating UserControls to represent Views, the elements with x:Name properties won't get autogenerated.
I tried to work around this using the code below which mimics what happens in the autogenerated code (with the added bonus of only being done after the layout is actually loaded):
public DoubleAnimation PanelOpenMaxHeightDoubleAnimation;
private void LayoutRoot_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var LayoutRootreference = sender as Grid;
PanelOpenMaxHeightDoubleAnimation = ((DoubleAnimation)(LayoutRootreference.FindName("PanelOpenMaxHeightDoubleAnimation")));
PanelOpenMaxHeightDoubleAnimation.To = 383;
}
This however breaks when trying to set the value of To, as FindName returns null (I have x:Name manually set in XAML for this particular animation to "PanelOpenMaxHeightDoubleAnimation"). I have the sneaking suspicion FindName can't pick DoubleAnimations up from VisualStates, only actual layout children?
I did find the documentation about XAML Namescopes at http://msdn.microsoft.com/en-us/library/cc189026(v=VS.95).aspx#UserControls, but didn't really understand what my options are from this paragraph (other than being very limited):
For the case of a UserControl, there is no equivalent template part attribute convention for parts of the UserControl in the definition XAML, nor is there a template applied at all. Nevertheless, the namescopes between definition and usage remain disconnected, because the definition namescope is defined and then effectively sealed when you package your UserControl into an assembly for reuse. A best practice here is to define your UserControl such that any value that needs to be set to modify the definition XAML is also exposed as a public property of the UserControl.
What does it mean by the last sentence?
Wondering can I do next? Should I try to generate the entire state from code?
Well, managed to work it out so I'm sharing the solution.
Instead of trying to get a reference to the DoubleAnimation in Resources, I named the Grid in the layout I want to animate and get a reference to that using the code in the original question:
var SlidePanel = ((Grid)(LayoutRootreference.FindName("SlidePanel")));
This does return the element and using that it's possible to create a DoubleAnimation and a Storyboard from scratch purely in code. I just used this code example as a starting point: http://msdn.microsoft.com/en-us/library/cc189069(VS.95).aspx#procedural_code
Best part is, you can change the DoubleAnimation.To parameter even after setting everything up in the Storyboard, so now what I'm doing is just resetting that to my calculated value every time before calling Storyboard.Begin().
It's a bit fiddly to set all these up manually, but at least it works nicely once you do.

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