Binding to viewmodel from inside a datatemplate - wpf

I have multiple videos displayed they are bound with a videocollection in Mainviewmodel. Everything works fine untill I try to bind the enter command to Mainviewmodel. I Don't know the syntax for this. As it stands the binding is set to Video and not Mainviewmodel.
Errormessage:
'StartVideoCommand' property not found on 'object' ''Video'
Xaml:
<Window.Resources>
<local:MainViewModel x:Key="MainViewModel"/>
</Window.Resources>
<Grid DataContext="{StaticResource MainViewModel}">
<ListBox ItemsSource="{Binding Videos}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.InputBindings>
!!! <KeyBinding Key="Enter" Command="{Binding StartVideo}" /> !Bound to Video not to Mainviewmodel grrr
</Grid.InputBindings>
... layout stuff
<TextBlock Text="{Binding Title}" Grid.Column="0" Grid.Row="0" Foreground="White"/>
<TextBlock Text="{Binding Date}" Grid.Column="0" Grid.Row="1" Foreground="White" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding Length}" Grid.Column="1" Grid.Row="1" Foreground="White" HorizontalAlignment="Right"/>
... closing tags

Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext.StartVideo}"

Another approach would be to use ElementName binding instead of RelativeSource.
Example:
<Window x:Name="root" ... >
...
Command="{Binding ElementName=root, Path=DataContext.StartVideo}"
...
A possible advantage over RelativeSource is that this is explicit; if someone changes the XAML hierarchy then relative references could break unintentionally. (Not likely in this specific example of binding to a Window however).
Also if your "root" element already is named then so much the better it is easy to take advantage of.
It is also somewhat more readable.

Related

WPF Specified element is already the logical child of another element. Disconnect it first

I have recently started getting the exception "Specified element is already the logical child of another element. Disconnect it first." after converting a bunch of Frame controls to simpler ContentControl controls to host sub-model views within their logical parent model views.
In the below example, the commented out code when commented out will stop the crash from occurring:
<UserControl x:Class="GUI.Views.Scenario.PathogenRiskLiquidIngestionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ScenModels="clr-namespace:GUI.ViewModels.ScenarioModels">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Vertical">
<DockPanel>
<Label DockPanel.Dock="Left" Content="Risk Assessment Title"></Label>
<TextBox DockPanel.Dock="Right" Text="{Binding RiskAssessmentTitle, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DockPanel>
<DockPanel>
<Label DockPanel.Dock="Left" Content="Risk Calculated"></Label>
<ComboBox SelectedItem="{Binding RiskCalculated, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RiskCalculatedOptions}"></ComboBox>
</DockPanel>
<DockPanel>
<Label DockPanel.Dock="Left" Content="{Binding MinAcceptableInfectionRiskLabel}"></Label>
<TextBox DockPanel.Dock="Right" Text="{Binding MinAcceptableInfectionRisk, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DockPanel>
</StackPanel>
<!--<GroupBox Header="Pathogens">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Add Pathogen" Command="{Binding AddPathogen}"></Button>
<Button Content="Remove Pathogen" Command="{Binding RemovePathogen}" CommandParameter="{Binding SelectedIndex, ElementName=PathogenList}"></Button>
</StackPanel>
<ListView Name="PathogenList" ItemsSource="{Binding PathogensPresentViews}" Tag="{Binding}" BorderThickness="0" Background="Transparent">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type ScenModels:PathogenPresentIngestViewModel}">
<ContentControl Content="{Binding}"></ContentControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</GroupBox>
<GroupBox Header="Receptor Characteristics">
<StackPanel Orientation="Vertical">
<ContentControl Content="{Binding ReceptorVolumeLiquidIngestedPerEventView}"></ContentControl>
<ContentControl Content="{Binding ExposuresView}"></ContentControl>
</StackPanel>
</GroupBox>-->
</StackPanel>
</UserControl>
After doing a search for this kind of exception the most common cause seems to be some incorrect styling, however in this application I have not yet styled any elements. Can someone please tell me what is likely to be causing this exception?
Thanks,
Alex.
Without more knowledge of what all the underlying objects are i can not do much more than guessing...
Here is something really fishy:
<ListView Name="PathogenList" ItemsSource="{Binding PathogensPresentViews}" Tag="{Binding}" BorderThickness="0" Background="Transparent">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type ScenModels:PathogenPresentIngestViewModel}">
<ContentControl Content="{Binding}"></ContentControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As mentioned in the comments you should never even have a situation where you bind to *Views, the view model should not have references to views, but only view models. There is little point in following a model-view-separation pattern if you violate it like that.
Anyway, as you bind to what presumably are views and hence UI-elements something undesirable might be happening here:
DataTemplate is created: View is added to ContentControl -> Now a child of a control
ListView goes "Well screw the DataTemplate, i can display this item as is." (Because it is a view, which it should not be).
ListView adds item (the view, which is the child of the ContentControl) to the ItemsPanel.
Whoops!
But that is probably not it, WPF should be smarter than that, so how about this: Multiple references to the same instance in the PathogensPresentViews list?
Or the same view model is used in two places, creating two views, which both try to display the same list of UI-elements?
Whatever the actual case may be, you should not fight the symptoms but the illness, which most likely is that your view models contain views.

Binding to local variable in codebehind

I have to bind to local variable from code behind in ListView.ItemTemplate. I have tried using datacontext:
<ListView Name="listView" ItemsSource="{Binding}" DataContext="{Binding}" VerticalAlignment="Stretch" MaxHeight="300" Background="White" Foreground="Black" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" BorderBrush="#FFCDCDCD" BorderThickness="1" ScrollViewer.VerticalScrollBarVisibility="Auto" IncrementalLoadingTrigger="None" SelectionChanged="listView_SelectionChanged" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" MaxHeight="25" >
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Text}" Width="{Binding Width}" />
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Image}" Stretch="None" Width="40" HorizontalAlignment="Left" Margin="4,0,0,0" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In place of "Width="{Binding Width}"" I want to have property from datacontext - rest of data is connected with itemsSource. How to "tell" xaml to use property from datacontext? (right now it returns error "property not found"). I don't want to change my itemsource and add there width because it is constant for every element (it's obvious ;) ).
Thank you in advance :)
It does use the DataContext, which for the ItemTemplate is the item. You can walk up the tree to find another DataContext, in this case you want the ListView's:
{Binding DataContext.Width,
RelativeSource={RelativeSource AncestorType=ListView}}

Silverlight pass DataContext to ConverterParameter?

How can I pass the DataContext of LayoutRoot to the converter of the ContentControl inside the ListBox items template?
<Grid x:Name="LayoutRoot"
Background="White"
DataContext="{Binding Source={StaticResource myViewModel}}">
<StackPanel HorizontalAlignment="Left"
Margin="6,6,0,394"
Orientation="Vertical"
Width="200"
d:LayoutOverrides="Height">
<ListBox x:Name="listBox2"
ItemsSource="{Binding MyCollection, Mode=TwoWay}"
VerticalAlignment="Top"
Height="400">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"
ContentTemplate="{Binding Converter={StaticResource myConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Text="{Binding Value1, Mode=TwoWay}"/>
</StackPanel>
</Grid>
I want to be able to touch the objects inside the DataContext from within the Converter and use them for TwoWay binding on controls within the ListBox item's DataTemplate.
Any ideas? Any suggestions?
Thank you.
I just got the DataContext up to the converter using:
<ContentControl Content="{Binding}"
ContentTemplate="{Binding Converter={StaticResource stringToDataTemplateConverter}, ConverterParameter={StaticResource myViewModel}}" />
Now I have another problem my dynamic property binding is not working.
(sorry for my bad english)
I'm not sure about what you are trying to do here but with SL 5 you can use RelativeSource to get the DataContext:
{Binding DataContext,RelativeSource={RelativeSource AncestorLevel=1,AncestorType=Grid}}

WPF Nested Databinding; How Do I Bind To An Item Inside Another Item

I have a ListView bound to an ObservableCollection of CustomerContacts.
It works great so far, but being new to this, I'm not sure how to do the next part.
Each customer contact has several contact types, which I want to display under their name.
So inside of CustomerContacts I have another ObservableCollection of ContactTypes.
Here is my current datatemplate:
<DataTemplate x:Key="iconTemplate">
<DockPanel Height="133" Width="150">
<Image Source="/Tracks%203.5;component/Images/NoPic.png" Height="25" Width="25" Margin="1,0" />
<TextBlock DockPanel.Dock="Top" Text="{Binding FullName}" Margin="5,3,5,0" FontWeight="Bold" HorizontalAlignment="Left" />
<<TextBlock Text="{Binding Title}" Margin="5,0,5,3" HorizontalAlignment="Left" />>
</DockPanel>
</DataTemplate>
And here's my first attempt at putting the listview inside:
<DataTemplate x:Key="iconTemplate">
<DockPanel Height="133" Width="150">
<Image Source="/Tracks%203.5;component/Images/NoPic.png" Height="25" Width="25" Margin="1,0" />
<TextBlock DockPanel.Dock="Top" Text="{Binding FullName}" Margin="5,3,5,0" FontWeight="Bold" HorizontalAlignment="Left" />
<ListView ItemsSource="{Binding ContactTypes}">
<ListView.Template>
<ControlTemplate TargetType="ItemsControl">
<ItemsPresenter/>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Margin="3,0,0,0" HorizontalAlignment="Center" Text="{Binding Path=ContactType}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--<TextBlock Text="{Binding Title}" Margin="5,0,5,3" HorizontalAlignment="Left" />-->
</DockPanel>
</DataTemplate>
I want to replace the TextBlock bound to Title with a ListView/ListBox/ItemsControl bound to the items in ContactTypes.
Somewhat similar to this question: < WPF: bind to a List inside a class > but without all the codebehind. It would be nice to have it in the XAML.
There's a couple things I would do here:
Simplify what you're trying to do in order to isolate the problem. Instead of that whole ListView business, just put in there an ItemsControl: <ItemsControl ItemsSource="{Binding ContactTypes}" /> That'll give you the default view of things, which may just show you the data type of your objects (whatever ContactTypes is), but it'll let you know if the binding is working. If it is, you'll get something listed there. If it isn't, you won't.
If you're not getting anything listed, go use Snoop to drill into it and look for errors. Snoop shows the databinding errors, allows you to inspect the DataContext of each of the items, etc.
If you're still having problems, it might benefit us if you posted your class definitions and your code where you wire things up. There might be some other underlying issues (for instance, is the ContactTypes property null when the binding initially occurs and you're not using INPC to let the binding system know when it changes?).

Binding inside listbox itemtemplate problems

I have two separate binding problems with listboxes with an itemtemplate which contains a texbox.
1) One listbox binds to a list of strings. How can I display each string inside the textboxes created and allow two way binding at the same time? Two way binding isn't allowed without specifying a Path or XPath.
<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0" Name="listBoxKeys" VerticalAlignment="Top" Width="219" ItemsSource="{Binding Path=SelectedPlatform.Keys}" SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Mode=OneWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And 2) I use another listbox which binds to some generic list of a custom KeyValuePair class. The itemtemplate contains a textbox and combobox. The textbox text is bound to the key property of each KeyValuePair object and the combobox selecteditem to the value property. My problem is that I want the combo to get filled by a list of strings declared in my viewmodel which will be changing on runtime. The window's datacontext is the viewmodel where the list is declared. I don't know the exact syntax I need to use to bind the combobox itemssource there. Here's my code :
<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding ?}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is that the two-way binding on the source itself cannot work because it would mean that the whole object (string), for which the data template is created, must be replaced when user changes the text in the text box. Obviously, this will not work. Two-way binding will work only on a writable property of the bound object.
In your case I would suggest creating a view model for the items in the list box (basically a view model for your strings) and expose a Value property on it and bind to it in the data template:
<ListBox Height="231" HorizontalAlignment="Left" Margin="0,167,0,0"
Name="listBoxKeys" VerticalAlignment="Top" Width="219"
ItemsSource="{Binding Path=SelectedPlatform.KeyViewModels}"
SelectedItem="{Binding Path=SelectedKey,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Value, Mode=TwoWay}" Margin="0,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
1) Pavlo Glazkov seems to have a good answer to me
2) This is down to the DataContext for the ComboBox now being the key value pair rather than the ViewModel. There may be other ways to do this but the one that I've used before is to set the bindings RelativeSource source back to it's parent ItemsControl.
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}
Something like:
<ListBox Height="393" HorizontalAlignment="Left" Margin="0,72,0,0" Name="listBoxActions" VerticalAlignment="Top" Width="254" ItemsSource="{Binding Path=SelectedPlayer.ControlProfile.MappedActions}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<TextBox Text="{Binding Key, Mode=TwoWay,UpdateSourceTrigger=LostFocus}" Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ComboBox Margin="10,0,0,0" Height="Auto" MinWidth="80" MaxWidth="80" HorizontalAlignment="Left" VerticalAlignment="Center" ItemsSource="{Binding DataContext.Keys, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" SelectedItem="{Binding Value, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources