WPF bind to collection of controls - wpf

I want to do a binding to a collection of homemade custom controls.I've a collection of objects (called Parameters), each one of these should be presented in a panel by a custom control.
I have the following in my main window:
<ItemsControl Grid.Column="1"
ItemsSource="{Binding Path=Parameters}">
</ItemsControl>
And I have a resource file to declare how to view each Parameter object:
<DataTemplate DataType="{x:Type path:Parameter}">
<Grid>
<myControls:MyUserControl
Parameter="{Binding RelativeSource={RelativeSource Self}}">
</myControls:MyUserUserControl>
</Grid>
</DataTemplate>
My control takes a Parameter so I want to bind it to itself for every item in the collection. How can I do that binding?

In a DataTemplate that serves as ItemTemplate in an ItemsControl, the DataContext holds the individual items of the source collection. Hence the binding should look like this:
<myControls:MyUserControl Parameter="{Binding}" />
and the ItemsControl may be written like this to explicitly use the DataTemplate:
<ItemsControl ItemsSource="{Binding Path=Parameters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<myControls:MyUserControl Parameter="{Binding}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Related

specifying binding source to usercontrol in listbox

How can I specify datasource for a user control:
<ScrollViewer VerticalScrollBarVisibility="Visible" VerticalAlignment="Stretch">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:ucMyControl />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
How ucMyControl know what values to take? I realize that lisbox will create as many ucMyControls as there are items in bondong collection?
Also, scrollviewers don't work - I can't get to 3rd control (they are all empty anyway, but I hope you'll give me leads how to bind it).
The control that is instantiated from an ItemTemplate in an ItemsControls (e.g. ListBox) has its DataContext property automatically set to the appropriate item from the Items or ItemsSource collection of the ItemsControl.
So if you for example have a collection of Person objects with properties FirstName and LastName as ListBox items you can bind like this in your UserControl:
<UserControl ...>
<StackPanel>
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
</UserControl>
You do not need to set the DataContext property explicitly.

How to bind to parent DataTemplate from within an ItemsControl.ItemTemplate

I have a container type control which contains a number of items. The container control has a DataTemplate defined which also contains a ItemsControl with a DataTemplate for the item. The items however need to bind to something from the container control. A simplified example is given below:
<DataTemplate DataType="{x:Type ContainerType}">
<!-- Display of the container stuff-->
<ItemsControl ItemsSource="{Binding Items, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type Item}">
<!-- Display of the item stuff -->
<ComboBox Text="Choose a container-level option..."
ItemsSource="{WHAT GOES HERE?}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
How to I bind something at the item level back up to the container level?
You can use a RelativeSource binding
<ComboBox ItemsSource="{Binding SomeCollection,
RelativeSource={RelativeSource
AncestorType={x:Type local:MyContainerControl}}}"/>
What you use for your binding path depends on where the collection is located. If it is located as a DependencyProperty on MyContainerControl, then the above binding works fine. If it is located in the DataContext of MyContainerControl, then you'll need to set the binding path to DataContext.SomeCollection
Maybe use TemplateBinding?
Something like:
{TemplateBinding YourPropertyInTheDataTemplateContext}
I've always been a big fan of ElementName. Basically you make sure you name the outer level control like : x:Name="MainWin" and then you can do something like this:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding ElementName=MainWin, Path=DataContext.SomeCollection}"/>

Relative Binding in Silverlight

I have an ItemsControl in my application. The page associated with the ItemsControl is bound to a view-model. The view-model includes two properties: People and Options. For each person, I want to display a list of options in a ComboBox. The options are defined in my view-model. My code looks like the following:
<ItemsControl ItemsSource="{Binding Path=People}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="Options" DisplayMemberPath="FullName" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However, because each Item represents a Person, the ComboBox is looking at the Person object for a property called "Options". How do I reference the view model for the from the ComboBox instead of the Person?
Thanks!
You can use the following technique
<ComboBox ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.Options}" DisplayMemberPath="FullName" />
Assuming that your LayoutRoot's DataContext is the View Model. If not you can give your ItemsControl a name and use that for the ElementName.

WPF : How to create separate Resources for each item in a bound ItemsControl

I want to achieve the following:
My ViewModel exposes a property named 'Categories' which is a collection of CategoryViewModel objects
Each CategoryViewModel object exposes a property called 'Items' which is a collection of strings*.
On my View, I want a TabControl with a TabItem for each object in the 'Categories' collection.
The Content of each TabItem should be a xceed DataGrid control displaying the contents of the selected tab's Items collection.
<TabControl ItemsSource="{Binding Categories}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding CategoryName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding Items}"
AutoCreateColumns="True">
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This works ok when I bind directly to the ItemsSource property of the DataGridControl. However, in order to utilize all of the functionality of the DataGridControl, I need to bind the ItemsSource property of the DataGridControl to a DataGridCollectionViewSource object that is bound to my Items collection. I do this when the grid ISN'T nested in another control by creating a DataGridCollectionViewSource object in the Resources section of the UserControl and binding to that.
<UserControl>
<UserControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</UserControl.Resources>
<Grid>
<xcdg:DataGridControl
ItemsSource="{Binding Source={StaticResource GridData}}"
AutoCreateColumns="True">
</xcdg:DataGridControl>
</Grid>
</UserControl>
How do I need to structure the XAML so that when the TabControl is being bound, a DataGridCollectionViewSource object is created for each TabItem so that the DataGridControl that is generated within the content of the TabItem can be bound to it?
Clear as mud, right? :)
Thanks!
Notes:
*In the real solution the collection contains objects of a class that is more complex than a simple string, but a string was used to make the example more simple.
OK, this is a bit of a long-shot, but could you use the DataGrid.Tag ...
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
AutoCreateColumns="True">
<xcdg:DataGridControl.Tag>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</xcdg:DataGridControl.Tag>
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
Or ... resources can be defined on any FrameworkElement, so you could try:
<TabControl.ContentTemplate>
<DataTemplate>
<xcdg:DataGridControl
ItemsSource="{Binding Source={StaticResource GridData}}"
AutoCreateColumns="True">
<xcdg:DataGridControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
Source="{Binding Items}" />
</xcdg:DataGridControl.Resources>
</xcdg:DataGridControl>
</DataTemplate>
</TabControl.ContentTemplate>
I don't use the eXceed Grid so cannot test whether these work - just a couple of ideas to try!
Colin E.
You can use x:Shared="True" attribute on a resource. That means a new instance is created for every use of that resource.
Example:
<UserControl.Resources>
<xcdg:DataGridCollectionViewSource x:Key="GridData"
x:Shared="False"
Source="{Binding Items}" />
</UserControl.Resources>

How to properly bind a ListBoxItem in WPF?

I have a listbox and I want to iterate over a collection of Bars in my Foo-object.
<ListBox DataContext="{Binding Path=Foo.Bars}" >
<ListBox.Items>
<ListBoxItem>
<ContentControl DataContext="{Binding Path=.}" />
</ListBoxItem>
</ListBox.Items>
</ListBox>
This is the datatemplate I want to use.
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
If I snoop (--> examine by using the tool Snoop) my application, I notice that the entire collection of Bars is bound to the ContentControl, in stead of just 1.
How can I properly bind so the iteration over the collection goes fine?
You can just set the DataTemplate, and WPF does all the work. Set the ItemsSource to a list of Bar items, and then define a DataTemplate for Bar items.
<ListBox ItemsSource="{Binding Path=Foo.Bars}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could also set the ItemsTemplate directly by using <ListBox.ItemTemplate> instead of <ListBox.Resources>
See Data Binding Overview at MSDN.
First add your namespace to the Window element (Intellisense) :
xmlns:local="clr-namespace:yourenamespace"
Then the following XAML ( in Window.Resources is a clean way to do it ) :
<Window.Resources>
<ObjectDataProvider x:Key="DataProvider" ObjectType="{x:Type local:Foo}"/>
<DataTemplate x:Key="Template" >
<TextBlock Text="{Binding Bar}"/>
</DataTemplate>
</Window.Resources>
Place the Listbox :
<ListBox DataContext="{Binding Source={StaticResource DataProvider}}" ItemsSource="{Binding Bars}" ItemTemplate="DynamicResource Template" />
But, it depends on your code-behind object, you have to set a constructor to initialise public properties within your object which are ObservableCollection<> preferably (There is some restriction rules with object instance in XAML).

Resources