Getting value of TextBlock inside ComboBox DataTemplate - c#

I have the following XAML:
<ComboBox Height="23" HorizontalAlignment="Left" Grid.Row="6" Grid.Column="2"
Name="cbo_team" VerticalAlignment="Top" Width="148"
DataContext="{Binding ElementName=cbo_component, Path=SelectedItem}"
SelectedIndex="0">
<ComboBox.ItemsSource>
<Binding XPath="Teams/Team/#id"
Converter="{StaticResource xmlConverter}">
<Binding.ConverterParameter>
<local:XmlConverterParameter
XPathTemplate="/Products/Teams/Team[{0}]"
XPathCondition="#id='{0}'" />
</Binding.ConverterParameter>
</Binding>
</ComboBox.ItemsSource>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In C#, I'm trying to get the value of the TextBlock that is in the current selected item in the ComboBox. How do I do that? This question is pretty much the same, but the only answer doesn't help.

Check this sample out. The textblock (below the combobox) is showing the value of the name attribute of the currently selected xml element in the combobox. A message box will popup with the same result from the lookup in the visual tree. The lookup fails on initial selection changed. Looks like comboboxitems are created after selected item is set.
XAML:
<Window x:Class="CBTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Window.Resources>
<XmlDataProvider x:Key="UsersData" XPath="Users">
<x:XData>
<Users xmlns="">
<User name="Sally" />
<User name="Lucy" />
<User name="Linus" />
<User name="Charlie" />
</Users>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<StackPanel>
<ComboBox
Name="_comboBox"
ItemsSource="{Binding Source={StaticResource UsersData},XPath=*}"
SelectedIndex="0"
SelectionChanged="OnComboBoxSelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}" Name="nameTextBlock" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Below shows how to get the value of selected item directly from the data. -->
<TextBlock
DataContext="{Binding Path=SelectedItem, ElementName=_comboBox}"
Text="{Binding XPath=#name}" />
</StackPanel>
</Window>
Code behind, showing how to get the text directly by traversing the visual tree:
private void OnComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox comboBox = sender as ComboBox;
ComboBoxItem comboBoxItem = comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.SelectedItem) as ComboBoxItem;
if (comboBoxItem == null)
{
return;
}
TextBlock textBlock = FindVisualChildByName<TextBlock>(comboBoxItem, "nameTextBlock");
MessageBox.Show(textBlock.Text);
}
private static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(NameProperty) as string;
if (controlName == name)
{
return child as T;
}
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
return null;
}

Sorry a little late to the party :) but the following works as well (ironically was in the same fix as you!!)
TextBlock tb1 = (TextBlock)cbo_team.SelectedItem;
MessageBox.Show(tb1.Text);

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBox ContactListBox = sender as ListBox;
ListBoxItem listBoxItem = ContactListBox .ItemContainerGenerator.ContainerFromItem(ContactListBox.SelectedItem) as ListBoxItem;
if (listBoxItem == null)
{
return;
}
TextBlock txtBlock = FindVisualChildByName<TextBlock>(listBoxItem, "ListTextBlock");
MessageBox.Show(txtBlock.Text);
}
private static T FindVisualChildByName<T>(DependencyObject parent, string name) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
string controlName = child.GetValue(NameProperty) as string;
if (controlName == name)
{
return child as T;
}
T result = FindVisualChildByName<T>(child, name);
if (result != null)
return result;
}
return null;
}

Others already suggested to use a SelectionChanged event. Didn't test the code below, but you may give it a try.
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
TextBlock tvContent = (sender as ComboBox).SelectedItem as TextBlock;
string content = tvContent.Text;
}

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = (ComboBox)sender;
ComboBoxItem comboAsTextblock = (ComboBoxItem)comboBox.SelectedItem;
string comboBoxItemText = comboAsTextblock.Content.ToString();
// comboBoxItemText is what you want :)
}

Related

How to get the Parent Item of the SelectedItem on ListView.SelectionChanged

Does anybody know how I can retrieve the selected Ellipse "bgColor" from the following code please?
I can retrieve the Color, but I need to get the Ellipse which holds it.
XAML
<ListView Name="BgColorList" Height="80" Width="850"
ItemsSource="{Binding ColorList}"
ItemContainerStyle="{StaticResource ListViewTransparent}"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.IsHorizontalRailEnabled="True"
IsItemClickEnabled="True"
VerticalAlignment="Bottom"
SelectionChanged="BgColorList_SelectionChanged"
Margin="0,0,0,35">
<ListView.DataContext>
<local2:NamedColors />
</ListView.DataContext>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center" Margin="0,0,0,0" Height="65" Width="65">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Name="bgColor" Grid.Column="0" Height="50" Width="50" Margin="2" VerticalAlignment="Center" Stroke="Transparent" StrokeThickness="1">
<Ellipse.Fill>
<SolidColorBrush Color="{Binding }" />
</Ellipse.Fill>
</Ellipse>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# - I don't seem to be able to find a way to retrieve the Ellipse here.
private void BgColorList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView view = (ListView)sender;
var selected = view.SelectedItem;
Color selectedColor = (Color)view.SelectedItem;
}
The ListView has a ContainerFromItem methid which should give you a reference to the ListViewItem container. You could then get a reference to the Ellipse using the VisualTreeHelper class:
private void BgColorList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView view = (ListView)sender;
var selected = view.SelectedItem;
var container = view.ContainerFromItem(selected);
if (container != null)
{
Ellipse ellipse = FindVisualChild<Ellipse>(container);
if (ellipse != null)
{
//...
}
}
}
private static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
How to get the Parent Item of the SelectedItem on ListView.SelectionChanged
For ListView, you could use ContainerFromItem method to get ListViewItem for SelectedItem, then use VisualTreeHelper to find the child element, but VisualTreeHelper will consume system performance. And for you scenario, the better way is listen Ellipse Tapped event as #Noorul said, when the item is clicked the Tapped even will invoke.
private void BgColor_Tapped(object sender, TappedRoutedEventArgs e)
{
var ellipse = sender as Ellipse;
}

UWP C# SQL Webservice GridView triggers

i have class created for data retrieval
I have XAML code with gridview and cs code which connects to SQL webservice and i can get data from SQL :-)
my gridview has
textblock - data from sql table
checkbox
textbox
I would like to have some actions on the checkbox and textboxes. How do I get my textboxes to become visible upon checkbox click?
I have got this code working in other apps without gridviews, but I can't get it to work here. how do I reference the event_handler inside the gridview
XAML example
<GridView x:Name="GreenQuestionGridView" ItemsSource="{Binding}" Background="Green" Margin="0,40,0,0">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Height="40" Width="600" >
<StackPanel Orientation="Horizontal">
<TextBlock Width="200" VerticalAlignment="Bottom" TextWrapping="Wrap" Text="{Binding question_green}" />
<CheckBox x:Name="chkBox" Checked="chkBox_Checked" Unchecked="chkBox_Unchecked" Indeterminate="chkBox_Indeterminate" VerticalAlignment="Bottom" IsThreeState="True" />
<TextBox x:Name="txtBox" Visibility="Collapsed" Width="200" VerticalAlignment="Bottom" />
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
here is code that works in other app, but this needs to reference the gridview
private void chkBox_Checked(object sender, RoutedEventArgs e)
{
if (chkbox.IsChecked == null)
{
txtbox.Visibility = Visibility.Visible;
}
else
{
txtbox.Visibility = Visibility.Collapsed;
}
}
For the CheckBox you need also the event CheckBox_Unchecked to hide it again.
<CheckBox Unchecked="CheckBox_Unchecked" Checked="CheckBox_Checked" ... />
IsChecked is a nullable type you didnt even checked if it's true. Your code will hide the txtbox so long as the IsChecked is not null.
private static void ToggleTextBoxVisibility(object sender) {
if(!(sender is CheckBox)) {
return;
}
CheckBox checkBox = sender as CheckBox;
foreach(var child in ((checkBox.Parent as StackPanel).Children)) {
if(!(child is TextBox)) {
continue;
}
TextBox textBox = child as TextBox;
if(checkBox.IsChecked.HasValue && checkBox.IsChecked.Value) {
textBox.Visibility = Visibility.Visible;
} else {
textBox.Visibility = Visibility.Collapsed;
}
}
}
private void CheckBox_Checked(object sender, RoutedEventArgs e) {
ToggleTextBoxVisibility(sender);
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e) {
ToggleTextBoxVisibility(sender);
}
A clean solution would be to control it with a binding to a property in your viewmodel.

How to focus a datatemplated TextBox in the first element of an ItemsControl in a Window, when the Window is opened? (C#, WPF)

When the user clicks a button in the application, a window opens up. I want the TextBox belonging to the first item in the ItemsControl to be focused, so that the user can start typing as soon as the Window is opened, without needing to manually select the TextBox.
How can this be done?
For simplicity, we can say that the Window looks approximately like this:
<Window>
<Grid>
<ItemsControl ItemsSource="{Binding MyItems}">
<DataTemplate>
<Grid>
<StackPanel>
<StackPanel>
<customControls:ValidationControl>
<TextBox Text="" />
</customControls:ValidationControl>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl>
</Grid>
</Window>
UPDATE
After looking at propsed answers as well and this link:
Window Loaded and WPF
The modified solution is as follows:
<Window>
<Grid>
<ItemsControl x:Name="myItemsControl" ItemsSource="{Binding MyItems}">
<DataTemplate>
<Grid>
<StackPanel>
<StackPanel>
<customControls:ValidationControl>
<TextBox Text="" />
</customControls:ValidationControl>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl>
</Grid>
</Window>
In constructor:
this.Loaded += new RoutedEventHandler(ThisWindowLoaded);
Loaded Method:
private void ThisWindowLoaded(object sender, RoutedEventArgs e)
{
var textbox = FindVisualChild<TextBox>(myItemsControl.ItemContainerGenerator.ContainerFromIndex(0));
FocusManager.SetFocusedElement(myItemsControl, textbox);
}
Super-duper method:
public static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null)
return childItem;
}
}
return null;
}
FocusManager.SetFocusedElement() may be what you are finding.
Xaml
<ItemsControl x:Name="ItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In Loaded event, you focus on first textbox:
var textbox = ItemsControl.ItemContainerGenerator.ContainerFromIndex(0).FindChildByType<TextBox>();
FocusManager.SetFocusedElement(ItemsControl, textbox);
Following is the way I find first child:
public static T FindVisualChild<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null
&& child is T)
{
return (T)child;
}
T childItem = FindVisualChild<T>(child);
if (childItem != null)
return childItem;
}
}
return null;
}
In case anybody is interested in:
In my situation the ItemsControl has several different DataTemplates for different kind of items. Some are editable, some not. That's why I can not tell what kind of editor is currently displayed in which item.
So I was looking for a different solution:
I noticed, when I press the tab key once (right after the dialog opens), the focus moves to the intended element in my ItemsControl.
That's why I generate a tab in the loaded event as so:
this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (Action)delegate
{
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
});
If is WPF you should do smt like below :
<Grid>
<DataGridCell>
<Grid>
<TextBox x:Name="TextBox1" Text=" ">
</TextBox>
</Grid>
</DataGridCell>
</Grid>
And code-behind:
TextBox1.Focus();

how to make check-box tap action triggered when is-enabled is false

In my wp8 app i have checkbook in list box and i want to be able to tap check-box when is-enabled is false.i mean i want to tap check box but is-checked shouldn't change
<ListBox Name="URLListBox" Height="Auto" Grid.Row="2" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent" Margin="0,0,0,10" >
<CheckBox IsChecked="{Binding file}" x:Name="surename" Tag="{Binding b1Tag}" Grid.Column="0" FontSize="25" Content="{Binding text}" Tap="surename_Tap" VerticalAlignment="Center" HorizontalAlignment="Center"></CheckBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You could make by declaring that CheckBox inside Listbox and tick the CheckBox on Listbox tap event. Like this,
<ListBox Name="listBox" Tap="listBox_Tap">
<CheckBox Content="sample" IsEnabled="False" Name="checkBox" />
</ListBox>
And tick the CheckBox in ListBox tap event.
private void listBox_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
checkBox.IsChecked = true;
}
If you have several CheckBox inside that ListBox, get a SelectedIndex in ListBox tap event and change that CheckBox Value. In your case you could refer from the following code, In xaml,
<ListBox Grid.Row="1" Tap="RoleslistBox_Tap" Name="RoleslistBox">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsEnabled="False" Content="{Binding RoleTypes}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And in RoleslistBox tap event, you could write,
private void RoleslistBox_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
int i = RoleslistBox.SelectedIndex;
ListBoxItem item = this.RoleslistBox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
CheckBox tagregCheckBox = FindFirstElementInVisualTree<CheckBox>(item);
tagregCheckBox.IsChecked = true;
}
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}

How to add an option to selectively remove items from ComboBox?

I have a ComboBox that displays strings. How can I add an option to remove some items from the ComboBox list? I tried:
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
But I don't know how to locate the item the user chose:
private void MenuItem_OnClick(object sender, RoutedEventArgs e) {
/* ... ??? ... */
}
I don't mind putting some icon next to each item, that removes its related item when clicked, but don't know how to do it..
Summary:
This is how I solved it, finally (The credit belongs to Nawed Nabi Zada, who provided the main idea of "climbing" using the VisualTreeHelper.GetParent(...) to get the ComboBoxItem, in the accepted answer, below)
<ComboBox IsEditable="True" Name="RemotePathComboBox" VerticalAlignment="Center"
SelectionChanged="RemotePathComboBoxOnSelectionChanged"
Grid.Column="1" Margin="0,6" KeyUp="HostNameOrIPAddress_OnKeyUp">
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Button Click="RemoveRemotePathItem_Click" Margin="5" DockPanel.Dock="Left">
<Image Source="{Binding Converter={StaticResource iconExtractor}, ConverterParameter=%WinDir%\\System32\\shell32.dll|131}"/>
</Button>
<TextBlock Name="ItemTextBlock" VerticalAlignment="Center" Text="{Binding Path=Path}"></TextBlock>
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code-behind:
private void RemoveRemotePathItem_Click(object sender, RoutedEventArgs e) {
var depObj = sender as DependencyObject;
while (!(depObj is ComboBoxItem)) {
if (depObj == null) return;
depObj = VisualTreeHelper.GetParent(depObj);
}
var comboBoxItem = depObj as ComboBoxItem;
var item = comboBoxItem.Content as RemotePathItem;
_remotePathsList.Remove(item);
RemotePathComboBox_SelectIndexWithoutChangingList(0);
}
(The "Icon Extractor" that fetches the icon from the system's DLL is from an old post of mine)
You can also do it this way:
<Window x:Class="RemoveItemsFromComboBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ComboBox x:Name="CbxItems" VerticalAlignment="Top" HorizontalAlignment="Left" Width="250">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="MenuItem" Header="Delete" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
<TextBlock Text="Item 1"/>
<TextBlock Text="Item 2"/>
<TextBlock Text="Item 3"/>
<TextBlock Text="Item 4"/>
</ComboBox>
</Grid>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
CbxItems.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
}
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var comboBoxItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);
if (comboBoxItem == null) return;
comboBoxItem.IsSelected = true;
e.Handled = true;
}
private ComboBoxItem VisualUpwardSearch(DependencyObject source)
{
while (source != null && !(source is ComboBoxItem))
source = VisualTreeHelper.GetParent(source);
return source as ComboBoxItem;
}
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
CbxItems.Items.Remove(CbxItems.SelectedItem);
}
}
Put that ContextMenu for each ComboBoxItem instead of the ComboBox itself :
<ComboBoxItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBoxItem.ContextMenu>
You can also put that in DataTemplate or generate it from code behind, depending on how you populate the ComboBox. Then in menu item's click event handler you can do as follow to get user chosen ComboBoxItem :
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
var menuItem = (MenuItem)sender;
var ctxMenu = (ContextMenu)menuItem.Parent;
var comboBoxItem = (ComboBoxItem) ctxMenu.PlacementTarget;
}
For locating the combobox items, you can use checkbox in the item template of the combobox so that user can check the items which he/she wants to delete.
If your combobox is data bound, then you will have to filter the datasource of your combobox i.e. on context menu click you will have to delete the items checked by user from the datasource of your combobox and then re-bind the combobox with datasource.
If you don't have a data bound combobox, then on context menu click simply loop through the combobox items and delete the items which are checked by user.

Categories