Binding to local variable in codebehind - wpf

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}}

Related

Collection Source different DataContext from ListView using it

I have the following DataTemplate for a ListView that uses a StackPanel for its ItemsPanelTemplate.
<DataTemplate x:Key="DayTemplate">
<Border BorderBrush="Black"
BorderThickness="2"
CornerRadius="5"
Width="150"
Height="440"
Background="White">
<StackPanel Orientation="Vertical">
<Border BorderBrush="Black"
BorderThickness="0 0 0 5"
Background="White">
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Add Session"
Command="{x:Static cmd:TimetableCommands.AddSession}"/>
</ContextMenu>
</StackPanel.ContextMenu>
<TextBlock FontWeight="Bold"
TextAlignment="Center"
Text="{Binding Path=DateInfo.Date, Mode=OneWay, Converter={StaticResource DateNoTime}}"/>
<TextBlock TextAlignment="Center"
FontWeight="Bold"
Text="{Binding Path=DateInfo.DayOfWeek}"/>
</StackPanel>
</Border>
<ListBox Name="lbSessions"
Background="Transparent"
HorizontalAlignment="Center"
Visibility="Visible"
ItemsSource="{Binding Source={StaticResource SessionList}}"
ItemTemplate="{StaticResource SessionTemplate}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
I also have the following Collection Source that I'd like to use for the sorting functionality. It is in a seperate Resource Library, referenced correctly in the Resource Library where the DataTemplate is defined.
<CollectionViewSource Source="{Binding Path=Sessions, Mode=OneWay}"
x:Key="SessionList">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="StartTime"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
When run, no data is displayed, and the output window indicates that the collection source is trying to obtain the data for the list of items from the window level DataContext, not the DataContext the list uses (which is a couple of layers down from the Window ViewModel).
If I change the line
ItemsSource="{Binding Source={StaticResource SessionList}}"
to
ItemsSource="{Binding Path=Sessions}"
It works fine, though I no longer have the sorting fuctionality of the Collection Source, which I really want to make use of to avoid having to write my own sorting code.
What's going on? and how can I set the correct DataContext on the Collection Source.
Is there a reason you need to keep your CollectionViewSource in a separate ResourceDictionary?
Try nesting the CollectionViewSource in the ListBox's Items
<ListBox Name="lbSessions"
Background="Transparent"
HorizontalAlignment="Center"
Visibility="Visible"
ItemTemplate="{StaticResource SessionTemplate}">
<ListBox.Items>
<CollectionViewSource Source="{Binding Path=Sessions, Mode=OneWay}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="StartTime" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</ListBox.Items>
</ListBox>

Binding to the IsSelected property of the parent ListViewItem

I'm attempting to bind the Visibility property of a TextBlock that's held within the ItemTemplate for a ListView to the IsSelected property of the TextBlock's parent ListViewItem.
With this markup, the TextBlock is always visible.
<ListView x:Name="ItemListView" ItemsSource="{Binding Path=Accounts}" Margin="60,0,0,10" Grid.Row="1" Grid.Column="0">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100">
</ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Width="100" Height="100" Grid.Column="0"></Image>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Path=Account.Name}"
FontSize="24" Margin="5,0,0,0" TextWrapping="Wrap" />
</StackPanel>
<TextBlock Grid.Column="3" VerticalAlignment="Bottom"
Visibility="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=IsSelected, Converter={StaticResource boolConverter}, Mode=OneWay}">
Show More Details...
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Notes:
1. In case it makes any difference, this is WinRT; a Metro app written in C#.
2. boolConverter is a fairly standard converter appears to work correctly.
Use Mode=FindAncestor:
<TextBlock Grid.Column="3" VerticalAlignment="Bottom"
Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListViewItem}, Path=IsSelected, Converter={StaticResource boolConverter}, Mode=OneWay}">
I think that in this case you will have to use ElementName=ItemListView
#Murven 's answer was close. This is what worked for me:
<TextBlock Visibility="{Binding DataContext.IsSelected, ElementName=ItemListView Converter={StaticResource boolConverter}, Mode=OneWay}">
I had to use DataContext.IsSelected to access the context of the ItemListView.
Not sure if there is a better way.

change datatemplate resource runtime

I have created one datatemplate resouce in my xaml file
<navigation:Page.Resources>
<DataTemplate x:Key="PageFooter" >
<StackPanel Width="{Binding Path=UsablePageWidth, Mode=OneWay}" Height="Auto" x:Name="spFooter" HorizontalAlignment="Center">
<TextBlock x:Name="txtParameter" FontSize="16" Text="{Binding}"
FontWeight="Bold" Foreground="White"
HorizontalContentAlignment="Center"
Width="{Binding Path=UsablePageWidth, Mode=OneWay}"
Background="Black" Height="35" />
</StackPanel>
</DataTemplate>
</navigation:Page.Resources>
Now in my code behind i want to update this Textblock with my database value
How to do this? I am new in silverlight

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>

Tab order and index in WPF using the MVVM pattern

I'm having an issue with tabbing through the controls on a WPF application using the MVVM pattern. I have the following XAML which defines a tree structure
<Grid Background="Transparent" Margin="10">
<TreeView ItemsSource="{Binding FirstLevelNavigableViewModels}" Background="Transparent"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
ItemContainerStyle="{StaticResource TreeViewItemStyle1}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type ViewModel:VendorViewModel}" ItemsSource="{Binding Children}">
<View:VendorView HorizontalContentAlignment="Stretch" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type ViewModel:ProductViewModel}">
<View:ProductView HorizontalContentAlignment="Stretch" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
</Grid>
When the treeview is loaded, the XAML for the "ProductView" is as follows
<Border Margin="0,2,2,2" CornerRadius="3" Background="#3FC7B299" DockPanel.Dock="Right" HorizontalAlignment="Right" Width="109">
<StackPanel Orientation="Vertical" Margin="6,4">
<DockPanel>
<TextBlock DockPanel.Dock="Left" FontFamily="Segoe" FontSize="10" FontWeight="Medium"
Foreground="Black" Opacity="0.75"
Text="CALC. REG. PRICE"></TextBlock>
<Button Width="10" Height="10" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Padding="-4" Margin="0" Command="{Binding UserDefinedRetailPriceCommand}" Visibility="{Binding UserDefinedRetailPriceButtonView}">
<Image Width="10" Height="10" Source="/Arhaus.Pricing.Client;component/Styles/Images/error.png"></Image>
</Button>
</DockPanel>
<TextBox FontFamily="Segoe" FontSize="16" FontWeight="Medium" KeyboardNavigation.IsTabStop="True" KeyboardNavigation.TabIndex="{Binding RegularPriceTabIndex}"
Foreground="Black" Opacity="0.9" KeyboardNavigation.TabNavigation="Continue"
ebf:LostFocusBehaviour.LostFocusCommand = "{Binding LostFocusSugg}"
Text="{Binding NewSuggestedRetailPrice,Converter={StaticResource FormattingConverter}, ConverterParameter=' \{0:C\}', Mode=TwoWay, UpdateSourceTrigger=LostFocus}" Background="#FFE6DED3" BorderBrush="#FFE6DED3" DataContext="{Binding StringFormat=\{0:c\}, NotifyOnValidationError=True}" Padding="0" TabIndex="1"></TextBox>
</StackPanel>
</Border>
I have the Tab Index bound to an integer that is ever increasing and bound as the treeview is loaded (I.E. I have it setup as tab index 1, 2, 3 etc. as each successive model is loaded).
I would like to be able to hit tab and jump to the next textbox in the treeview but when I click the TAB key, nothing happens. I'm not sure if I have the tabbing setup correctly but I'm very new to WPF and at a loss as to where and how to set the tabbing to make it work. I'm used to WinForms where you just set the tab index and go from there.
Thank you for your assistance, I apologize for the large code block.
I don't have a solution ready but some thoughts that maybe may help:
I don't know what RegularPriceTabIndex returns but probably it begins for each new TreeViewItem at the same index?
The same tab-index is given then multiple times because of repeating use of ProductView. Perhaps this leads to a problem. You can try setting FocusManager.IsFocusScope="true" for the ProductView. Maybe this helps.
Try also to set Control.IsTabStop="true" on the TextBox.

Resources