Binding selected item in listbox with datatemplate - c#

I have simple listbox here: link to image where I have binded data. Now when I tap on one item(one row) listbox item/row is selected/violet color but in fact this item is not real selected only row change color but when I click on image or text then row where I click in not selected/violin but item in code is selected. I'm not sure if You understand what I'm saying. In short if I click on blank space row then row is visual selected but sender like listbox item not get data, if I click on text or image then sender get data but its not visual selected. How I can do it when I click anywhere row is selected/violet and item-sender get data?
And my second question is why my rectangle used like line has max width like text I want strech this rectangle at full width of listbox is this possible?
XAML:
<ListBox x:Name="lbMessagesUsersList" Foreground="Black" ItemsSource="{Binding MyDatasMessagesUserList }">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem Tapped="userTapped" Tag="{Binding}" >
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding MessengeHisPhoto}" HorizontalAlignment="Left" Width="40" Height="40" Margin="5,-18,0,-18" Stretch="Fill" ></Image>
<TextBlock x:Name="tbMessengerName" Text="{Binding MessengeFullName}" HorizontalAlignment="Left"
Grid.Column="1" Margin="25,0,0,0"/>
</Grid>
<Rectangle Height="1" Margin="0,0,0,-38" HorizontalAlignment="Stretch">
<Rectangle.Fill>
<SolidColorBrush Color="Black"/>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
CS:
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var button = sender as ListBoxItem;
if (button != null)
{
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == button.Tag);
if (subject != null)
{
IdOfChoosenUser = subject.MessengeIdUser;
}
}...
I also try remove ListbOxItem and set binding tag to stackPanel but this dont work too.{

Note that, when you use an ItemTemplate, each item gets wrapped in a ListBoxItem -- so, you shouldn't use a ListBoxItem in the template, as that will produce two nested ones. Try removing it, like so:
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Tapped="userTapped" Tag="{Binding}">
<!-- content here -->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
For the handler, it might be easier to simply reference the ListBox by name instead of trying to use Tag (thanks #MetroSmurf):
private void userTapped(object sender, TappedRoutedEventArgs e)
{
var selectedItem = lbMessagesUsersList.SelectedItem;
var subject = MyDatasMessagesUserList.FirstOrDefault(sub => sub == selectedItem);
}
For the second part of your question: to stretch the items, you need to stretch the ListBoxItem wrapper. Do this by using ItemContainerStyle:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Related

how to select the item of list veiw data template in wpf?

I made a list view, and i used a data Template as you seeing
below and now i want to select the Which item,the user selected and put the text of the selected item textblock to a variable
<ListView.ItemTemplate>
<DataTemplate >
<Border CornerRadius="20"
BorderThickness="2"
BorderBrush="Red"
Width="70"
Height="70"
Margin="5,5">
<Button x:Name="calender_btn"
Background="Transparent"
BorderBrush="Transparent"
Style="{StaticResource calender_btn_style}"
Click="calender_btn_Click">
<TextBlock x:Name="calender_txt_block"
Text="{Binding _outputdate}"
Foreground="White"
Width="55"
FontSize="14"
Margin="3,5"
TextWrapping="Wrap"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextAlignment="Center"
>
</TextBlock>
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="20"/>
</Style>
</Button.Resources>
</Button>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
I have no idea how I can do it, because I am new in wpf, if you know how I can do it.
Depending on how your data looks like, you probably get a data object to each button that contains the text. You can find that object in the buttons data context.
private void calender_btn_Click(object sender, RoutedEventArgs e)
{
var dataObject = ((Button)sender).DataContext;
}
If you are using ItemsSource you can get the selected value by binding to ListView SelectedItem (SelectedValue in some cases).
SelectedItem="{Binding SelectedData, Mode=TwoWay}"
Assuming that you have SelectedData property in your DataContext with same type as your data in the shown collection.
Google for MVVM

change TextBox readonly by clicking on ListBoxItem

I have a TextBox inside a ListBoxItem which is disabled so I can drag and drop it in the ListBox.
Once I double click it I want it to be Enabled so I can edit the Text and when I'm done I want it do be Disabled again to do drag and drop.
I have the MouseDoubleClick event on the ListBoxItem but it doesn't change the TextBox ReadOnly. Can anybody tell me how to achieve this.
at the moment TextBox returns null. seems like I don't get access to it the way I'm trying.
XAML
<ListBox Name="Locations" Cursor="Hand" HorizontalAlignment="Left" Height="351" Margin="10,48,0,0" VerticalAlignment="Top" Width="285" ItemsSource="{Binding Locations}" dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ListBoxItem_MouseDoubleClick"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Name="textBox" Text="{Binding Path=Name}" IsHitTestVisible="False" Width="270" Background="Transparent" BorderThickness="0" Margin="2"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In View
private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListBoxItem item = sender as ListBoxItem;
textBox.IsReadOnly = false;
}
That's because the textbox "doesn't exists" in the visual tree, WPF creates one for each listitem when needed, so you never have a reference to it. It is possible to navigate the visual tree and get the textbox reference but I advise against it, instead, create a property in your item model, something like "EditMode" that when you set it to true, the textbox will enable through the binding of the IsEnabled property.

Get dynamic bind listbox item

I have the following listbox in my windows phone 8.1 silverlight application
<ListBox x:Name="ScenarioControl" FontFamily="Segoe WP">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Name="myborder" Margin="0,0,0,0" BorderThickness="0,0,0,3" BorderBrush="Black">
<Grid HorizontalAlignment="Stretch" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Tap="TextBlock_Tap" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Path=Title, Mode=OneTime}" FontFamily="Segoe WP Semibold" Grid.Column="0" FontSize="23"/>
<Image Tap="Image_Tap" Source="/Images/blogger_small.png" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="1" OpacityMask="Black"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you can see TextBlock bound dynamically. I need to change some properties for the last TextBlock element but I cannot find method to get exact element inside the code.
I did search and found that there are solutions with ItemContainerGenerator method but that does not work for me, the following line of code always returns null.
ListBoxItem item = this.ScenarioControl.ItemContainerGenerator.ContainerFromIndex(ScenarioControl.Items.Count - 1) as ListBoxItem;
The most common cause of this problem is that the item in question isn't visible when you make the call to ItemContainerGenerator. Most listboxes, etc. virtualize their contents, meaning that they don't generate UI for elements that would not be visible to the user, so when you go to get the container, you just get null.
This Question + Answer(s) sums this up, and offers a solution to the problem.
Using a converter can help. It's not all declarative, but was what I thought initially.
Change style of last item in ListBox

Data binding for two textbox

I have 2 textbox, each in a different listview. First textbox is supposed to show data from a xml file. So when i click on the textbox, the data in the first textbox will show on the second textbox. I did this by doing a very big round about, getting the specific object when i click it and append to another listview. Is there a shorter way to do this through binding by element name in the xaml? My elementName in textbox1 will be the name for textbox2. I try doing it, but I am not sure what my path should be?
Sorry for not including my xaml.
<Window x:Class="GridViewTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:local="clr-namespace:GridViewTest"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="541" d:DesignWidth="858" SizeToContent="WidthAndHeight">
<Window.Resources>
<local:PacketList x:Key="PacketList"/>
<local:BindableSelectionTextBox x:Key="BindableSelectionTextBox"/>
</Window.Resources>
<Grid Height="500" Width="798">
<Grid.RowDefinitions>
<RowDefinition Height="142*" />
<RowDefinition Height="145*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="234*" />
<ColumnDefinition Width="233*" />
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding}" x:Name="lvItems" Grid.RowSpan="2" Grid.ColumnSpan="2">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn Header="Header" Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBox Name ="A" Tag="Header" Text="{Binding SelectedText, Path=headerObj.headervalue}" PreviewMouseLeftButtonUp="Handle_Click"
IsReadOnly="True" BorderThickness="0" >
</TextBox>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<ListView Margin="0,245,0,8" Grid.ColumnSpan="2" Grid.RowSpan="2" >
<TextBox Name="headText" Text="{Binding SelectedText,ElementName=A}"/>
</ListView>
</Grid>
Firstly let us have some education on NameScoping in WPF. In WPF any Bindings within Templates are scoped to that Template only. Also any element named within a template wont be available for Binding.ElementName reference outside the template.
So in your case TextBox A cannot be referred by TextBox headText as textbox A is name-scoped under GridViewColumn.CellTemplate.
Also why is headText textbox under a ListView? ItemsControls like ListBox, ListView, DataGrid should not be used as panels or containers to host single elements. Their intention is to show multiple items. Use Panels or ContentControl instead.
<Grid Margin="0,245,0,8" Grid.ColumnSpan="2" Grid.RowSpan="2" >
<TextBox Name="headText" Text="{Binding SelectedText,ElementName=A}"/>
</Grid>
OR
<ContentControl Margin="0,245,0,8" Grid.ColumnSpan="2" Grid.RowSpan="2" >
<TextBox Name="headText" Text="{Binding SelectedText,ElementName=A}"/>
</ContentControl>
Now to synchronize selection between two textboxes use the following trick...
XAML
<TextBox Name="SelectionSource"
Tag="{Binding ElementName=SelectionTarget}"
SelectionChanged="SelectionSource_SelectionChanged" />
<TextBox Name="SelectionTarget"
Text="{Binding SelectedText, ElementName=SelectionSource,
Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
Code Behind ...
private void SelectionSource_SelectionChanged(object sender, RoutedEventArgs e)
{
var targetTextBox = ((TextBox) sender).Tag as TextBox;
if (targetTextBox != null)
{
var bndExp
= BindingOperations.GetBindingExpression(
targetTextBox, TextBox.TextProperty);
if (bndExp != null)
{
bndExp.UpdateTarget();
}
}
}
If you are using MVVM then handle this SelectionSource_SelectionChanged event in attached behavior.
EDIT 2:
Now in case if one text box is part of ListBox template and other is outside the template then use content control hack...
XAML:
<Window.Resources>
<TextBox x:Key="SelectionTarget"
Text="{Binding Tag.SelectedText,
RelativeSource={RelativeSource Self},
Mode=TwoWay,
UpdateSourceTrigger=Explicit}" />
</Window.Resources>
<StackPanel>
<ListBox>
<ListBox.ItemsSource>
<x:Array Type="{x:Type System:String}">
<System:String>Test String 1</System:String>
<System:String>Test String 2</System:String>
<System:String>Test String 3</System:String>
</x:Array>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="SelectionSource"
Text="{Binding Path=., Mode=TwoWay}"
Tag="{StaticResource SelectionTarget}"
SelectionChanged="SelectionSource_SelectionChanged" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ContentControl Content="{StaticResource SelectionTarget}">
</ContentControl>
</StackPanel>
Code Behind
private void SelectionSource_SelectionChanged(
object sender, RoutedEventArgs e)
{
var targetTextBox
= ((TextBox) sender).Tag as TextBox;
if (targetTextBox != null)
{
targetTextBox.Tag = (TextBox) sender;
var bndExp
= BindingOperations.GetBindingExpression(
targetTextBox, TextBox.TextProperty);
if (bndExp != null)
{
bndExp.UpdateTarget();
}
}
}
Hope this helps.
I'm not really sure what's going on with "SelectedText" you are trying to bind to, but if all you are trying to do is display the "lvItems" SelectedItem text in your "headText" TextBox the following should work
<TextBox Name="headText" Text="{Binding ElementName=lvItems, Path=SelectedItem.headerObj.headervalue}" />
You'll need to change your TextBox "A" binding as well.
<TextBox Name ="A" Tag="Header" Text="{Binding headerObj.headervalue}" IsReadOnly="True" BorderThickness="0" >
</TextBox>
Assuming that headerObj is a property of the Packet class, and headervalue is a property of that, and headervalue is the value you wish to bind to.
The text in "headText" will update when the SelectedItem is changed (not when the TextBox is clicked).

Two overlapping ListBoxes. Selection problem

I'm trying to display shapes in two separate Listboxes occupying the same space. I've set the Background to both Transparent and {x:Null} but the mouseclick is still getting captured by the topmost Listbox so I can't select any shapes from the underlying ListBox.
Here is some sample code reproducing the problem.
<Grid>
<!-- ListBox 1 -->
<ListBox Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{x:Null}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<Ellipse Width="100" Height="100" Stroke="Blue" StrokeThickness="10"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
1
</ListBox>
<!-- ListBox 2 -->
<ListBox Background="{x:Null}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{x:Null}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="100"/>
<Setter Property="Canvas.Top" Value="100"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Ellipse Width="100" Height="100" Stroke="Blue" StrokeThickness="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
1
</ListBox>
</Grid>
This is how I solved this problem for now but I'm more than open for other suggestions :) I enabled hittesting on the Grid and disabled it for both ListBoxes. Then I did the hittesting in the event handler instead
<Grid MouseDown="Grid_MouseDown"
IsHitTestVisible="True"
Background="Transparent">
<!-- ListBox 1 -->
<ListBox Background="Transparent"
IsHitTestVisible="True"
..>
</ListBox>
<!-- ListBox 2 -->
<ListBox Background="Transparent"
IsHitTestVisible="True"
..>
</ListBox>
</Grid>
Event handler and hittesting
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
Grid grid = sender as Grid;
Point ptCurrent = e.GetPosition(grid);
VisualTreeHelper.HitTest(grid, null, new HitTestResultCallback(HitTestCallback), new PointHitTestParameters(ptCurrent));
}
public HitTestResultBehavior HitTestCallback(HitTestResult htrResult)
{
ListBoxItem listBoxItem = GetVisualParent<ListBoxItem>(htrResult.VisualHit);
if (listBoxItem != null)
{
listBoxItem.IsSelected = true;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}
public T GetVisualParent<T>(object child) where T : Visual
{
DependencyObject c = child as DependencyObject;
while ((c != null) && !(c is T))
{
c = VisualTreeHelper.GetParent(c);
}
return c as T;
}
You can bind multiple sets of data to a single ListBox by using a CompositeCollection as the items source:
<ListBox ...>
<ListBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection={Binding ...}" />
<CollectionContainer Collection={Binding ...}" />
</CompositeCollection>
</ListBox.ItemsSource>
</ListBox>
You can then use data templates to control the appearance of different data types. You can either use implicit data templates or you can use an ItemTemplateSelector.
For more information about using and binding to a CompositeCollection, see this resource: How do you bind a CollectionContainer to a collection in a view model?
You could bind your ListBoxes to a Visibility variable. This way you can make the bottom box visible when you are in a situation where the bottom one would be selected and the top on collapsed and visa verse when you need the top one to be selected.

Categories