WPF Binding Syntax Question - wpf

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.

Related

XAML property path for templates

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].

Binding with parent DataContext

I'm trying to bind combobox editor in a PropertyGrid to a list.
<dxprg:PropertyGridControl SelectedObject="{Binding SelectedEmployee}">
<dxprg:PropertyDefinition Path="EmployeeCountryID">
<dxprg:PropertyDefinition.EditSettings>
<dxe:ComboBoxEditSettings
ItemsSource="{Binding Path=DataContext.Countries, ElementName=rootWindow}"
ValueMember="CountryId" DisplayMember="CountryName" />
</dxprg:PropertyDefinition.EditSettings>
</dxprg:PropertyDefinition>
</dxprg:PropertyGridControl>
This example is from a third-party control but the problem may be just general.
The "rootWindow" DataContext has been set to a ViewModel which holds a property List(of Country) that I want have as ItemsSource in a Combobox.
I was trying to access that list by setting the Combobox ItemsSource to the rootWindow.DataContext.Countries property but I don't get any data.
Tried also all those RelativeSource FindAncestor bindings but no data appeared either.
Why can't I bind through a DataContext of a given element like this?
This became solved. The problem was not with the binding at all but realated to how I defined the third-party control: Instead of EditSettings I should have defined CellTemplate -> DataTemplate.

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.

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}

Where is IsSynchronizedWithCurrentItem property (or equivalent) for a TreeView?

Tell me it ain't so.
I have a typical windows/file explorer like setup.
Left Side I have a TreeView all data bound showing nodes in a hierachy
Right Side I have a ListView showing Node.Properties
ListView has a IsSynchronizedWithCurrentItem property, which rocks. e.g. If I had another ListView showing a list of nodes and both listViews have this property set to true. Changing selection of node in NodesListView will update the PropertiesListView automatically.
Now I need the same thing with a NodesTreeView and a PropertiesListView... and seems like TreeView has no such property.
Is there a more 'the WPF way' kind of solution to this problem ? Or do I have to handle the NodeSelectionChanged event of the Tree and refresh the listView via code.
A really simple solution is to bind your "details" UI elements to the SelectedValue property of the TreeView. For example, if your TreeView looked like this:
<TreeView Name="CategoryName" ItemsSource="{Binding Source={StaticResource A_Collection}, Path=RootItems}" />
Then you could bind details UI elements (like a textbox) using:
<TextBox Text="{Binding ElementName=CategoryTreeView, Path=SelectedValue.Name}"/>
Would cause the text box to be bound to Name property of the items currently selected in the TreeView.
If you want to bind many UI items as details for the selected TreeView item, consider setting up a DataContext on the elemtent that contains all the details controls (DockPanel / Grid / StackPanel, etc).
<ListView Name="listView1"
ItemsSource="{Binding Path=SelectedItem.Modules,
ElementName=treeView1, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True">
Where ".Modules" is the collection of child items off the selected treeview item you want to display. Don't worry about wiring up the "SelectedItemChanged" event on the treeview.
Why exactly it doesn't implement the property, I do not know, but i have a suggestion down below.
Your code above will work, however, it is not what the IsSynchronizedWithCurrentItem property does. Any ItemsControl binds to the ICollectionView of the ItemsSource property. To get that ICollectionView, we can call CollectionViewSource.GetDefaultCollectionView(object o). Depending on the type of object o, you get a different concrete implementation of the ICollectionView inteface. CollectionView and ListCollectionView are two concrete classes that come to mind.
The ICollectionView interface contains a member called CurrentItem. What the IsSynchronizedWithCurrentItem does is: whenever an item is clicked on the ItemsControl, it sets the CurrentItem for the collection view. The ICollectionView also has two events: CurrentItemChanging and CurrentItemChanged. When the IsSynchronizedWithCurrentItem property is set, the ItemsControl will update the SelectedItem based on what the ICollectionView's CurrentItem is. Makes sense?
In master/detail WPF scenarios, we simply are binding to ICollectionViews and their CurrentItem (the CurrentItem syntax is something like {Binding Items/Name}, where Name is the Name property on the collection's CurrentItem.
So although your code works for your purposes, it doesn't do what that property does. To do what the property does, you need to do the following:
When an item is selected, you need to figure out which collection it belongs to. How do we do this? I believe this is why TreeView doesn't implement it. The selected item is displayed in a TreeViewItem. The DataContext is the object itself, but what is the parent collection ? I guess to get it you could either cache it in some hashmap (silly, but will work) or you can walk up the logical tree and get the TreeViewItem's parent that happens to be an ItemsControl. The ItemsSource property will be your collection.
Get the ICollectionView for that collection.
Need to cast that ICollectionView into a CollectionView (ICollectionView doesn't implement CurrentItem setter)
Call SetCurrent(.. , ..) on the CollectionView instance
Now, anything that is bound to that ICollectionView's CurrentItem will be updated.
This became longer than I expected. Let me know if any further clarification is necesary.
My solution to this turned out to be pretty tiny.. don't know if it is equivalent to IsSynchronizedWithCurrentItem. ListView refreshes as expected.
// the XAML
<TreeView DockPanel.Dock="Left" x:Name="tvwNodes" ItemsSource="{Binding}" SelectedItemChanged="OnNewNodeSelected"/>
<ListView x:Name="lvwProperties" ItemsSource="{Binding Path=Properties}"
// the code-behind
private void OnNewNodeSelected(object sender, RoutedPropertyChangedEventArgs<object> e)
{
lvwProperties.DataContext = tvwNodes.SelectedItem; // this returns the selected Node obj
}

Resources