XAML property path for templates - wpf

I have the following hierarchy in my XAML:
Grid
Ruler
Ruler
ScrollViewer
ItemsControl
ItemsControl.ItemsPanel
ItemsPanelTemplate
DrawingCanvas
Here Ruler and DrawingCanvas are my UserControls. DrawingCanvas exposes a property named MousePosition that I want both Rulers to bind to. After reading about PropertyPath Syntax, I tried the following:
Chip="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Mode=OneWay, Path=Children[2].(ScrollViewer.Content).(ItemsControl.ItemsPanel).(local:DesignerCanvas.MousePosition).X}"
but this doesn't seem to work. What am I doing wrong?

Generally in these circumstances you would simply name the element you wish to bind to. It is much less brittle than finding your way through the tree:
Chip="{Binding MousePosition.X, ElementName=myCanvas}"
However, because your canvas is part of the ItemsControl template, the ElementName will not be visible to elements outside the ItemsControl.
In order to bind the two together, you need some intermediate property that both can see. One way would be to declare a DependencyProperty in your code-behind for the view. Both your Canvas and Rulers could then bind to that property using ElementName on the view root.
Another alternative is to hijack the Tag property that every element has, so in your case you could use ItemsControl.Tag as the intermediary:
Chip="{Binding Tag.X, ElementName=myItemsControl}"
<DrawingCanvas MousePosition={Binding Tag,
ElementName=myItemsControl,
mode=OneWayToSource} />

You are selecting element 3 in the Children collection which will map to the fourth element inside of the Grid. You are looking for Children[2].

Related

Dynamicly putting Object to many visual parents in WPF

I want to create my own usercontrol which will take some FrameworkElement as parameter (as Content) and then it will didplay it on few copies (how much? it depends) Anyway copies has to be binded to their VM. Single copy will be probably a StackPanel with few binded buttons and TextBoxes. So it will look like this:
<MyControl> <Panel with stuff in it/> </MyControl>
Now in my control I hot ItemsControl and I am dynamicly putting there items. But one content (stack panel for example) can only has one parent so it doesn't work. How can I achive this? I heard that DataTemplates can help me but I don't know how. Also I wander if I can't do something like this:
<MyControl> <ViewModel of Panel with stuff/> </MyControl>
You need to take a DataTemplate property that contains the FrameworkElement(s), then create ContentPresenters in the control, with their Content set to the piece of ViewModel and their ContentTemplate set to your DataTemplate property.

WPF binding with PlacementTarget and RelativeSource

Can you explain the following WPF code:
DataContext="{Binding Path=PlacementTarget,RelativeSource={x:Static RelativeSource.Self}}">
I find it extremely confusing. What is placement target and what is relative source?
This looks like a hack that is used for popup-elements such as ContextMenus and Popup-windows.
The problem with these elements is, that they are disconnected from the visual tree of your window. Therefore the DataContext is not available. The PlacementTarget is a link to an element of the visual-tree.
Mostly you will find a binding path like PlacementTarget.Tag where in the source element the Tag property has been set to the DataContext but in some situations, the element itself is also meaningful, such as in your example.
Assuming that the above code is used in a ToolTip or a ContextMenu, the DataContext will be set to the control that "owns" the element.
Look at the post from (Gishu +1) for an explanation of the mechanics.
Every FrameworkElement has a DataContext that is an arbitrary object. The default source for a data binding is that DataContext. You can use RelativeSource.Self to change the source for a binding to the FrameworkElement itself instead of its DataContext. So the RelativeSource part just moves you "up one level" from the DataContext of the FrameworkElement to the FrameworkElement itself. Once you are at the FrameworkElement you can specify a path to any of its properties. If the FrameworkElement is a Popup, it will have a PlacementTarget property that is the other FrameworkElement that the Popup is positioned relative to.
In short, if you have a Popup placed relative to a TextBox for example, that expression sets the DataContext of the Popup to the TextBox and as a result {Binding Text} somewhere in the body of the Popup would bind to the text of the TextBox.
This is binding the DataContext of a thing (UI Control? need to see more of the code snippet ) to its own PlacementTarget property value.
RelativeSource is used to indicate the source object relative to the binding target. The path property indicates the name of the property on the source object.

WPF: Binding items added to UserControl's exposed children

I have a user control that allows items to be added to it by exposing a Grid's Children property. Any control I add shows up fine but when I try to bind a property on the added item to a control in the main window nothing happens (example):
<TextBox Name="txtTest" Text="Success!" />
<mycontrols:CustomUserControl.ExposedGridChildren>
<TextBox Text="{Binding ElementName=txtTest, Path=Text, FallbackValue=fail}"/>
</mycontrols:CustomUserControl.ExposedGridChildren>
This example always results in the TextBox's text showing "fail". Here is how I'm exposing the children in the user control:
public UIElementCollection ExposedGridChildren
{
get { return grdContainer.Children; }
}
Any thoughts? Is it a scope issue? I know I can't name the elements I add to the children because of scope errors. Thanks, Brian.
This might answer your question:
How do you bind a grid's children to a list?
Or as Dr. WPF puts it: (http://drwpf.com/blog/2007/10/15/itemscontrol-a-is-for-abundance/)
Is a Panel an ItemsControl?
No. The logical children of a panel
are UIElements, whereas the logical
children of an ItemsControl (its
Items) can be any CLR objects.
Sidebar: So what is a panel? The main
role of a panel is to provide layout
support for its children. Although a
panel does maintain a collection of
child elements, it is not technically
a WPF “control”… That is, it does not
derive from the Control base class and
it does not support the WPF notion of
templating. Instead, it is a single
element with a single purpose… namely,
to size and position (a.k.a., measure
and arrange) its children.
I was apparently going about this thing all wrong. What I needed to do was create a look-less control and template it to have one (or more) contentpresenter(s).

WPF Binding Syntax Question

I've seen this syntax show up, and have tried to google for it's definition to no avail; what does it mean when a dp is bound this way?
<Grid>
<ContentControl Content="{Binding}"/>
</Grid>
I was under the assumption that you have to bind to some property on the DataContext, or another element, but this appears to bind to nothing.
I believe it means you are binding to the root of whatever the binding context is. So if you use this syntax in a datatemplate that is part of some sort of list control, you would be binding to the root level of whatever the parent control (the list control) was binding to.
I believe {Binding} refers to the DataContext itself.
edit (clarification): By DataContext I mean the current level DataContext. For example, if your window's DataContext is bound to a List, then setting ItemsSource on a ListBox control in your window to {Binding} would bind the ListBox to the List itself, not a property of the List, like Count.
{Binding} is for {Binding [CurrentDataContext]}
{Binding} means that you want to Bind to the the current DataContext which could be set on the object itself. If no DataContext is set on the current object, then it will walk up the VisualTree and find the closest Parent that has a DataContext.

Passing Generic lists to a WPF usercontrol

I want to create a usercontrol that takes lists of different objects. These objects would be assigned to the control at design time. Now I want to be able to use linq to object to sort this list inside the usercontrol. Can anyone give me any ideas as how to go about it?
Add a DependencyProperty of type ObservableCollection<T> to your user control class (call it MyItemsSource for example). In your containing XAML, bind that property to your Linq collection, and inside your user control, bind your ListBox (or other ItemsControl) to the property as follows:
{Binding
RelativeSource={RelativeSource
Mode=FindAncester,
AncestorType=UserControl},
Path=MyItemsSource}
Alternatively, you can set the Name property inside the user control on the top level element (the UserControl element) to for example MyUserControl, and bind against an ElementName instead of a RelativeSource as such:
{Binding ElementName=MyUserControl, Path=MyItemsSource}

Resources