How to find value of TextBox inside DataTemplate in WPF? - wpf

I'm facing some problems in getting the value of the TextBox in my DataTemplate.
My ListView is bound to an ObservableCollection<lstProduse>, and the TextBlock inside the data template is bound to the Denumire property of lstProduse. The text of the TextBox is typed in by hand.
What I want to achieve is to loop through all items to get the texts of TextBox and TextBlock.
<ListView x:Name="lstReceta" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding lstProduse}" SelectionChanged="lstReceta_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" HorizontalAlignment="Center" Text="{Binding Denumire}"/>
<TextBox Grid.Column="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="10,0"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

You have a view model with an ObservableCollection. You have already bound the Text property of the TextBlock to the Denumire property on your item type. Just create another property e.g. Input for the Text property of the TextBox in your item type and bind to it, too.
<TextBox Grid.Column="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Padding="10,0" Text="{Binding Input}"/>
Then you can simply loop over your lstProduse collection in your view model, since the properties of your items are synchronized through the bindings in TextBlock and TextBox.

Related

How can I set the binding for a control's property (which is inside DataTemplate and UserControl) to use the ItemSource's given property?

I would like to make a UserControl which have a DataTemplate, and inside that DataTemplate there are controls. I would like to bind to those nested (inside the DataTemplate) controls' properties so I can set them when I reuse this UserControl. The nested controls will use the ItemSource's properties but the property names of the ItemSource's properties could be different.
The UserControl:
<UserControl x:Class="ContextMenu.BaseFilterUserControl"
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"
x:Name="Self">
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Margin="10"
Text="Owners" />
<Button Grid.Column="1"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10"
Click="FilteButtonClicked"
Width="40"
Height="40"
x:Name="FilterButton">
<Popup x:Name="FilterBoxPopup"
PlacementTarget="{Binding ElementName=FilterButton}"
Placement="Bottom"
StaysOpen="False">
<Border BorderBrush="Black"
Background="White"
Margin="2">
<ListView ItemsSource="{Binding ElementName=Self, Path=FilterList}"
x:Name="FilterListView"
Height="300"
Width="150">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!--<CheckBox IsChecked="{Binding IsChecked}" />-->
<!--<TextBlock Text="{Binding Name}" />-->
<!--This is where I don't know how to properly bind eg. the above control, things I tried:-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=Self, Path=DataContext.FilterElementName}" />-->
<!--<TextBlock Text="{Binding ElementName=FilterListView, Path=DataContext.FilterElementName}" />-->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</Popup>
</Button>
<TextBlock Grid.Column="3"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10"
Text="{Binding ElementName=Self, Path=SelectedNames}" />
</Grid>
</UserControl>
This how the UserControl is used, the FilterElementName="Name" is what I would like to set, depending on the list binded with FilterList list:
<local:BaseFilterUserControl FilterList="{Binding Owners}"
FilterElementName="Name"
SelectedNames="{Binding SelectedNames}"/>
In this case the Owners is a simple IReadOnlyList of an Owner class. The Owner class has a string Name property. But I will use this UserControl again with different list eg. where I would like to use the Versions list's Release property (for the TextBlock inside the UserControl):
<local:BaseFilterUserControl FilterList="{Binding Versions}"
FilterElementName="Release"
SelectedNames="{Binding SelectedReleases}"/>
The ListView is properly populated with items, so the FilterList DependencyProperty is working. But the nested controls are only working when I hard code the bindings:
<TextBlock Text="{Binding Name}" />
For this to work you would need to bind the Path-property of your TextBlocks Text-Binding to the FilterElementName property of your UserControl. Unfortunately the Path property of the Binding class is not a DependencyProperty and therefore not bindable.
One way to to achieve your goal would be to use the DisplayMemberPath property of the ListView, which is bindable:
<ListView x:Name="FilterListView"
Width="150"
Height="300"
ItemsSource="{Binding ElementName=Self, Path=FilterList}"
DisplayMemberPath="{Binding ElementName=self, Path=FilterElementName}"/>
If this approach does not work because you need to specify a more complex ItemTemplate, another way would be create a property of type DataTemplate in your UserControl, use that as ItemTemplate in the ListView and specify it from outside like so:
<local:BaseFilterUserControl FilterList="{Binding Versions}"
SelectedNames="{Binding SelectedReleases}">
<local:BaseFilterUserControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Release}" />
</DataTemplate>
</local:BaseFilterUserControl.ItemTemplate>
</local:BaseFilterUserControl>

Select a Control alone inside a ListView not the whole ListViewItem

I have a ListView with ItemTemplate contains an Image, TextBox, ComboBox, TextBlock etc when selecting any control should highlight the selected control not the whole ListViewItem any style would be fine.
XAML
<ListView Margin="10" ItemsSource="{Binding TCollection}" SelectedItem="{Binding SelectedTImage}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<Image Width="70" Height="70" HorizontalAlignment="Center">
<Image.Source>
<BitmapImage DecodePixelWidth="300" DecodePixelHeight="300" UriSource="{Binding TImage}"/>
</Image.Source>
</Image>
<TextBox Text="{Binding text}"/>
<TextBlock Text="Contents"/>
<ComboBox/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Any help
What you want is to disable event bubbling. Sure, it hits the control first, but the click event bubbles to the list item, too. In order to stop it, you have to put an event handler on all the controls (button, etc) and set e.Handled = true in the handler to prevent it from going to the ListViewItem.
See: disable event-bubbling c# wpf for more info.
If you never want items to be selected at all (hard to tell with your example), then you'd want to use an ItemsControl instead. That will let you bind to a collection of items and display all of them but it doesn't have the concept of a selected item.

wpf Combobox Itemssource not binding within Listbox Data Template

Please help to fix the below issue.
I have Combobox inside listbox item datatemplate so that I can load multiple combobox but same itemsource
<StackPanel>
<ListBox Width="400" Name="lstFiles" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Name="dataGrid">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Name="dragFileName"
Margin="5,0,0,0"
Text="{Binding fileName, UpdateSourceTrigger=Default}" />
<ComboBox Grid.Row="0"
Grid.Column="1"
Margin="5,0,0,0"
Name="cboDragDocType"
ItemsSource="{Binding dragDocType, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"
Text="{Binding dragDocTypeText}"
IsEditable="True"
IsReadOnly="True" />
<Button Name="dragDelBtn"
Grid.Column="2"
Height="20">X</Button>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Data is loading in comboBox by using ObservableCollection property. In Initialize method i do for loop method to get multiple items.
Problem scenario: Select item from the dropdown1 --> then Select item from the dropdown2 -->Again click dropdown1, Now dropdown1 itemssource is empty.
You don't have a binding to the SelectedItem (or SelectedValue) property of the ComboBox. Therefore the value cannot be saved (or retrieved).
You need to add a property in your class that acts as the DataContext to hold the selected item of the ComboBox, for example:
<ComboBox Grid.Row="0"
...
SelectedItem="{Binding Path=SelectedDragDocType, UpdateSourceTrigger=LostFocus}" />
As a side-note having both IsEditable and IsReadOnly properties on the ComboBox set to true is pretty restrictive. Are you sure it is the intended behavior?

Binding the combobox values for an empty datatemplate of a bound itemscontrol wpf

I have an itemscontrol that is bound to a datasource via LINQ. I am trying to create a repeatable control that a) displays the records returned from the LINQ query and b) allows the user to add a new row.
I have 2 collections:
a) fareItemCollection this contains a collection of fareItem objects which include a fareDate and PermitNumber.
b) permitNumbers this is an Ienumerable
I can display the fare items in a repeatable itemsControl. I want to be able to show the permit number as a drop down list (i.e. combobox) of permitNumbers with the permitNumber for that fare selected. The user should be able to select a different permit number to assign to that fareItem if they wish.
I have no problem with retrieving the required data via LINQ, it is how I bind the permitNumbers data in the combobox that I am struggling with. I understand how to bind a dataset to a combobox normally but not when it is within an items control bound to another source. Is this even possible or is there another way to approach this?
Here's my xaml so far - Note PermitNumbers is the name of my iEnumerable which is a public property within the window's class:
<ItemsControl Name="fareItemsControl" ItemsSource="{Binding}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Date</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Driver</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1">
<TextBlock Text="{Binding FareDate, StringFormat={}\{0:dd/MM/yy\}, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource FormFieldTextBoxStyle}">
</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1">
<ComboBox ItemsSource="{Binding Path=PermitNumbers, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"></ComboBox>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Style.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width="160" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock>Date</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0">
<TextBlock>Driver</TextBlock>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="1">
<DatePicker SelectedDate="{Binding FareDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource datePickerStyle}"> </DatePicker>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="1">
<ComboBox ItemsSource="{Binding Path=PermitNumbers, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"></ComboBox>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.Style>
</ItemsControl>
Thanks
Kay
Further information:
The following is the fareObject that I am using within my fareObjectsCollection that is bound to the fareItemsControl above:
public class FareProxy
{
DateTime _fareDate;
public DateTime FareDate
{
get
{
return _fareDate;
}
set
{
_fareDate = value;
}
}
IEnumerable<string> _permitNumbers;
public IEnumerable<string> PermitNumbers
{
get
{
return _permitNumbers;
}
set
{
_permitNumbers = value;
}
}
}
For each fare item the date displays correctly within the items control but the permitNumbers won't bind to the permitNumbers combobox. I tried to create a permitNumbers property within the class of the window I am using and load the permitNumbers by setting the source path of the comboBox to the permitNumbers property as suggested in the post below but this didn't work either.
You need to make a few changes. First things first... whichever Window or UserControl that your fareItemsControl ItemsControl is in has an object set as its DataContext... I think that you've just set the fareItemCollection as the DataContext. That's your first mistake.
That collection property is in some class. Move your permitNumbers collection as a property to that same class. Now set an instance of that class (that contains both collection properties) as the DataContext.
Now, you'll need to update your ItemsControl.ItemsSource property:
<ItemsControl Name="fareItemsControl" ItemsSource="{Binding fareItemCollection}" ... />
For the final part, #ethicallogics was correct to point out that you'd need to use a RelativeSource Binding to access your permitNumbers collection from inside a DataTemplate. However, he omitted the essential DataContext part. So the correct RelativeSource Path should be something more like this:
<ComboBox ItemsSource="{Binding DataContext.permitNumbers, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}" IsEditable="True"></ComboBox>
This means that the ComboBox.ItemsSource is looking for a property named permitNumbers in the object that is set as the DataContext for the parent Window. That should do the trick.

TextBox in UserControl doesn't take all the space

I have a WPF listbox with custom items. Each item is a user control consisting of a grid with two textboxes. I want that the right one takes all the space to fill the ListBoxItem. But all I get working is that the item itself takes the whole space but the textbox takes the size of its content.
So this is the listbox:
<ListBox x:Name="leftListBox" Grid.Column="0" Margin="10"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<local:CustomLine />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the user control:
<UserControl x:Class="SharpComparer.CustomLine"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="30">
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox x:Name="NumberColumn" x:FieldModifier="public"
Text="{Binding LineNumber}"
Grid.Column="0" HorizontalAlignment="Right" />
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Left" />
</Grid>
</UserControl>
What I have already tried after some research at some MSDN posts and around here on Stack Overflow: Setting the HorizontalContentAlignment to Stretch for Listbox.ItemContainerStyle. I used some borders to find the piece which makes problems. The ListBoxItems seem to take the whole width, the usercontrol and its grid, too. The textbox does not take all the space although I thought that Width="*" inside the grid's ColumnDefinition would do that.
Another idea was to bind the textbox width to its parent size, but then it also takes the space of the left textbox (which makes sense, because it gets the whole width) and subtracting this width doesn't seem to work.
What am I doing wrong?
You have to change your UserControl code from this:
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Left" />
to this:
<TextBox x:Name="TextColumn" x:FieldModifier="public"
Text="{Binding Text}"
Grid.Column="1" HorizontalAlignment="Stretch" />

Resources