fill ComboBox with items from different ViewModel - c#

I have a ComboBox binded to DataContext SceneViewModel, but I want to fill it with data from an observableCollection from another ViewModel called GearViewModel.
How do I do this? or is this possible.
Here is the xaml
<UserControl x:Class="MoviePrepper.View.SceneView"
DataContext="{Binding SceneViewModel, Source={StaticResource Locator}}">
<Grid>
<ComboBox ItemsSource="{Binding to observableCollection in GearViewModel}}" SelectedItem="{Binding SceneCollectionView/Equipment, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>

You can achieve this using a binding like this:
<ComboBox ItemsSource="{Binding GearViewModel.MyCollection, Source={StaticResource Locator}}"
SelectedItem="{Binding Equipment, UpdateSourceTrigger=PropertyChanged}"/>
Where the ItemsSource property binds to the GearViewModel.MyCollection property in your Locator, and the SelectedItem binds to the SceneViewModel.Equipment (as set by the DataContext of the UserControl).
It is not clear exactly what property you had in mind for binding on the SelectedItem property, so I made some assumptions.
Anywho, that should solve the problem with binding your ItemsSource property to a different view model.

Related

How to bind to a source inside a ListBox different from the ItemsSource already specified

I have a ListBox inside a HubSection, whose Items are bound to a class "players" added to my DefaulViewModel via code behind.
First I simply put a TextBox bound to the property "PlayerName" of my class "players".
Now I would like to add a ComboBox with some items that are NOT part of the class players.
Is it possible ? I thought that definind an ItemsSource in the ComboBox would sort of override the ItemsSource of the ListBox, but nothing displays.
The DataContext of the whole page is defined like so:
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
Then the HubSection is like so:
<HubSection x:Name="HubSec1">
<DataTemplate>
<ListBox x:Name="ListBox1" ItemsSource="{Binding players}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Path=PlayerName, Mode=TwoWay}"/>
<ComboBox ItemsSource="{Binding Path=ListOfElements}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</HubSection>
If I define the ComboBox in the same way but outside the ListBox, it will display the string elements of "ListOfElements" properly.
But in this ListBox, the ComboBox is empty. So my guess is that having defined an ItemsSource for the ListBox, it is not possible to override it.
I have tried to define a DataTemplate but was not successful doing so, but it might be the good solution (and I did not proceed properly)
What am I missing ?
Edit :
The ComboBox items is an ObservableCollection. It is not part of the "players" class.
Here is how I added these elements to the DefaultViewModel
DefaultViewModel.Add("players", players);
DefaultViewModel.Add("MyItemsList", ListOfElements);
You can walk up the visual tree and bind to an ancestors datacontext:
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
EX:
{Binding Path=ListOfItems, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}
that should give you the datacontext that the listbox has, so assuming your ListOfItems exists in that data context.
Or you can name your control, and then bind to its datacontext by element name:
{Binding ElementName=mySourceElement,Path=ListOfItems}
It can be a little bit tricky to create a good working binding in Windows Apps. A widely used work around is to use the Tag property.
<ListBox x:Name="ListBox1" ItemsSource="{Binding players}" Margin="0,184,0,0" Tag="{Binding Path=ListOfElements}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding Path=PlayerName, Mode=TwoWay}"/>
<TextBox Text="{Binding Path=Tag, ElementName=ListBox1}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
A binding to an element wirh the specific name will work always. And the ListOfElements should be in the scope of the ListBox so you can use the Tag property as a proxy. If you need to bind more than one property, you can also use dummy XAML elements:
<Border Tag="{Binding ...}" Name="dummy1"/>

Binding RowDetails in WPF DataGrid

I cant seem to find how to do this.
I dont see why the datacontext for a rowdetailstemplate becomes the datacontext of the datagrid parent. When it cleary should choose the rowitem from the datagrid itemssource.
<DataGrid Grid.Row="1" x:Name="DataGrid" ItemsSource="{Binding Collection}"
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<TextBox Text="{Binding WhyDoesThisBindingUseDataContextOfDatGridParentAndNotTheRowDataObject}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate></DataGrid>
How can i get the rowdataobject as the datacontext for my template?
Look at the visual tree.
Object which you binds to ItemsSource doesn't apply to RowDetailsTemplate. It takes DataContext of DataGrid Parent.

Binding to a second property

I have two properties in my viewmodel, called Premises and Towns.
I'm binding my ListViewItems to Premises, and in the itemtemplate I want to bind to Towns, but when I use the following XAML it tries to bind to Premises.Towns instead of Towns.
How can I bind to Towns directly?
Viewmodel:
public class MainWindowViewModel
{
public ObservableCollection<Premise> Premises;
public List<Town> Towns;
}
XAML:
<ListView x:Name="PremisesList" Margin="195,35,10,10"
ItemContainerStyle="{StaticResource OverviewListViewItemStyle}"
ItemsSource="{Binding Premises}" HorizontalContentAlignment="Stretch">
And this is what's in my OverviewListViewItemStyle.
<ComboBox ItemsSource="{Binding Towns}" Grid.Row="2" Grid.ColumnSpan="3">
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem>
<TextBox Text="{Binding Name}" />
</ComboBoxItem>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I'd like to be able to select a Town for a Premise via XAML.
You are correct in your assumption. ComboBox looks for Towns in Premise class, which is the class behind each ListViewItem If you want to refer to same context as ListView you need to use RelativeSource binding.
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Towns}"
Grid.Row="2"
Grid.ColumnSpan="3"
DisplayMemberPath="Name"/>
Not related to your problem but you also don't need to specify DataTemplate to display single property. DisplayMemberPath will work as well. If you do specify DataTemplate you don't need to use ComboBoxItem as ComboBox will wrap DataTemplate content in ComboBoxItem so effectively you'll end up with ComboBoxItem inside another ComboBoxItem
You bind the ItemsSource to the Premises property therefore if you bind to the Towns in the OverviewListViewItemStyle the binding engine will look up in the Premise object for a property called Towns.
If you want to select a town for a premises you should tell to the combobox where to look from that property. You can try to set the combobox's datacontext to the main viewmodel with relative source in the binding. Something like that:
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Towns}"

Two way data Binding issue with combo box - WPF

I have a View that contains a Combobox. The Combobox SelectedItem property is data bound to SelectedX property of View Model as two way data binding. When the viewModel is initialized, the SelectedX property is set correctly. But after that when the view renders, it resets the value of SelectedX(since the binding is two-way).
So the two way data binding for the Combobox is basically not working. Please advise.
This is the xaml for my view. I initialize the View model first with apprpriate values for Relationships and SelectedX. When the view renders, the combo box resets the value for SelectedX. (I figured that by adding breakpoints). Hope this helps
<ComboBox Grid.Row="1" Grid.Column="1" Margin="5" Background="White" BorderBrush="DarkGray"
SelectedItem="{Binding SelectedX, Mode=TwoWay}"
ItemsSource="{Binding Relationships}" DisplayMemberPath="Value"
SelectedValuePath="Value" SelectedValue="{Binding Key, Mode=TwoWay}"
IsEditable="False" IsReadOnly="True" />
SelectedValue="{Binding Key, Mode=TwoWay}"
This will change the SelectedItem to its SelectedValue.

What's the difference between these two ways of declaring a self binding?

I have an UserControl containing a ItemsSource DependenceProperty that has to be binded to the ItemsSource property of an internal Control:
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Self}}"
vs
ItemsSource="{Binding ItemsSource, ElementName=controlName}"
controlName is the name of the control.
The first binding is not working while the second one works. I don't get the difference.
Any ideas?
EDIT:
XAML:
<UserControl x:Class="MultiSelectTreeView.MultiSelectableTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Name="multiTree" >
This does not work ---> <TreeView ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Self}}" >
This works ---> <TreeView ItemsSource="{Binding ItemsSource, ElementName=multiTree}" >
In case you want to bind to DP of parent UserControl, you need to bind it using Mode = FindAncestor. Since you are binding on internal control, you need to travel up the Visual Tree.
Self Mode will search for DP in internal control and not on parent UserControl.
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl}}"
I'm assuming from your question that you have Xaml that is something like this in structure:
<UserControl x:Name="rootElement">
<ListBox ItemsSource="{Binding .....}" />
</UserControl>
Your bindings are then doing the following:
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource Self}}"
... this will look for the property ItemsSource on the control that the binding is declared on (that is, the ListBox). In your case this will cause a problem because you're essentially setting up an infinite recursion: your ItemsSource is bound to the ItemsSource is bound to the... ad infinitum. You mention that you're working with a UserControl here, and I suspect that you might be expecting RelativeSource to return the root UserControl element - but this is not the case.
ItemsSource="{Binding ItemsSource, ElementName=rootElement}"
... this will bind to the property ItemsSource on the control with a specific name. If you're working in a UserControl then typically you would have set x:Name on the root element of the UserControl and would be referring to it from a binding in this way. This would allow you to bind the child ListBox to the public ItemsSource property of your UserControl.
Just for information, one other alternative is to use an AncestorType binding to find your parent UserControl. If your UserControl type is called MyControl, it would look something like this:
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType=MyControl}}"

Categories