One of my classes
public class CheckedListItem<T> : INotifyPropertyChanged
has the method
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
My XAML looks like
<ListBox ItemsSource="{Binding Employee}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My employee.cs file
public class Employee
{
public string Name { get; set; }
}
I cant figure out how I can write back to the database when a item is checked or unchecked.
I would appreciate any help and would be more than happy to post the entire code for any files you need to see.
You should be able to get what you want by adding TwoWay for mode and PropertyChanged on UpdateSourceTrigger.
Final XAML will then be:
<ListBox ItemsSource="{Binding Employee}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT:
By the way, I think you might be setting your ItemsSource incorrectly. I'm sure what you want is to bind that listbox to a collection of Employees?
Something like:
public ObservableCollection<Employee> Employees
{
get { return _employees; }
set
{
_employees = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Employees"));
}
}
}
I misunderstood the question so here comes second edit:
Your class files would instead look like this:
public partial class MainWindow : INotifyPropertyChanged
{
private ObservableCollection<MutableKeyValuePair<Employee, bool>> _employees;
public MainWindow()
{
InitializeComponent();
Employees = new ObservableCollection<MutableKeyValuePair<Employee, bool>>
{
new MutableKeyValuePair<Employee, bool>(new Employee("Joakim"), false),
new MutableKeyValuePair<Employee, bool>(new Employee("SoftwareIsFun"), false),
};
}
public ObservableCollection<MutableKeyValuePair<Employee, bool>> Employees
{
get { return _employees; }
set
{
_employees = value;
OnPropertyChanged(nameof(Employees));
}
}
public void UpdateDatabase()
{
foreach (var employee in Employees)
{
if (employee.Value)
{
//DATABASE LOGIC HERE
}
}
}
}
public class MutableKeyValuePair<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public MutableKeyValuePair()
{
}
public MutableKeyValuePair(TKey key, TValue value)
{
Key = key;
Value = value;
}
}
public class Employee
{
public Employee(string name)
{
Name = name;
}
public string Name { get; set; }
}
XAML:
<DataGrid ItemsSource="{Binding Employees}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Key.Name}"/>
<DataGridCheckBoxColumn Binding="{Binding Value}"/>
</DataGrid.Columns>
</DataGrid>
Related
I have to bind the changes of checkbox in the listview.
checkbox should work in such a way that if i select all checkbox and uncheck a single checkbox then header checkbox should deselect. Same way if i have 3 child checkbox ,and individually select all then parent checkbox should select.
i am using MVVM pattern
xaml
<GridViewColumn Width="80" >
<GridViewColumn.Header >
<!--<CheckBox x:Name="cbSelectAll" IsChecked="{Binding ModelDetails.IsChecked}" Command="{Binding cbSelectAll_Checked}"/>-->
<CheckBox x:Name="cbSelectAll" Command="{Binding CbSelectAll_Checked}" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged}" />
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<WrapPanel x:Name="Layout" >
<CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}" Command="{Binding CheckSingle}"/>
</WrapPanel>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
viewmodel.cs
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
//publishing the event in current classs
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public Item(string name, string matches, string date, bool isSelected)
{
Name = name;
Type = matches;
Date = date;
IsSelected = isSelected;
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
public string Name { get; set; }
public string Type { get; set; }
public string Date { get; set; }
}
here is the another class
public class AddedFilesViewModel : INotifyPropertyChanged
{
public ICommand CbSelectAll_Checked { get; set; }
public ICommand UploadBtnClick { get; set; }
public ICommand CheckSingle { get; set; }
CbSelectAll_Checked = new RelayCommands(SelectAllBtnExecute, SelectAllBtnCanExecute);
UploadBtnClick = new RelayCommands(UploadBtnExecute, UploadBtnCanExecuteUpload);
CheckSingle = new RelayCommands(SingleCheckBoxExecute, SingleCheckBoxExecuteCanExecute);
public void SelectAllBtnExecute(object param)
{
if (selectedStatus == false)
{
foreach (var item in Items)
{
item.IsSelected = true;
}
selectedStatus = true;
}
else
{
foreach (var item in Items)
{
item.IsSelected = false;
}
selectedStatus = false;
}
}
public void SingleCheckBoxExecute(object param)
{
MessageBox.Show("hello");
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public bool SelectAllBtnCanExecute(object param)
{
return true;
}
public void UploadBtnExecute(object param)
{
List<Item> selectedFiles = new List<Item>();
if (Items != null)
{
foreach (var selectedItems in Items)
{
if (selectedItems.IsSelected)
{
selectedFiles.Add(selectedItems);
}
}
I can see the following block need to be changed, Please share full xaml file so I can understand your data binding.
<CheckBox IsChecked="{Binding IsSelected,Mode=TwoWay}" Command="{Binding CheckSingle}"/>
You need to edit it as "DataContext.IsSelected" and need to set binding source, it may be data grid or even full xaml based on your business logic to bind data. An example:
<CheckBox IsChecked="{Binding DataContext.IsSelected,Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Command="{Binding CheckSingle}"/>
I've just given sample example, there are many other ways to achive it.
Maybe I am saying shit but I would try that:
Remove all commands
in isChecked set I would put all you put in selectAllButtonExecute
in isChecked get I would check if all checkbox are checked and return true or false accordingly
in IsSelected set I would add OnPropertyChanged("IsChecked");
I created a User Control page (SubPODetailView.xaml) that contains some entities "that entities defined from SubPO.cs" and a datagridview "that gridview is a separated class called SubInvoice.cs", and everything went good but in the datagridview, one of entities is a combobox display, the problem here is the collection list of this combobox is not displayed when running a program.
SubPO.cs
public class SubInvoice
{
public int Id { get; set; }
[Required]
public string InvoiceName { get; set; }
public DateTime Date { get; set; }
public decimal Amount { get; set; }
public List<string> Status{ get; set; }
public int SubPOId { get; set; }
public SubPO SubPO { get; set; }
}
Configuration.cs (In Migration Folder)
context.SubInvoices.AddOrUpdate(i => i.InvoiceName,
new SubInvoice
{
InvoiceName = "Invoice1",
Date = new DateTime(2020, 5, 26),
Amount = 1200,
SubPOId=context.SubPOs.First().Id,
Status = new List<string>() { "Open", "Closed", "Pending" }
});
SubPODetailViewModel.cs
<DockPanel Grid.Row="12" Margin="10">
<StackPanel DockPanel.Dock="Right" Width="86">
<Button Content="Add" Margin="10"
Command="{Binding AddInvoiceCommand}"/>
<Button Content="Remove" Margin="10"
Command="{Binding RemoveInvoiceCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Invoices}"
SelectedItem="{Binding SelectedInvoice,Mode=TwoWay}"
AutoGenerateColumns="False" RowHeaderWidth="0" >
<DataGrid.Columns>
<DataGridTextColumn Header="Invoices" Width="*"
Binding="{Binding InvoiceName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding Date,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Amount" Width="*" Binding="{Binding Amount,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTemplateColumn Header="Status" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Status,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
SubPODetailViewModel.cs(Model)
namespace SubBV.UI.ViewModel
{
public class SubPODetailViewModel : DetailViewModelBase, ISubPODetailViewModel
{
private ISubPORepository _subPORepository;
private IMessageDialogService _messageDialogService;
private SubPOWrapper _subPO;
private SubInvoiceWrapper _selectedInvoice;
public SubPODetailViewModel(IEventAggregator eventAggregator,
IMessageDialogService messageDialogService,
ISubPORepository subPORepository) : base(eventAggregator)
{
_subPORepository = subPORepository;
_messageDialogService = messageDialogService;
AddInvoiceCommand = new DelegateCommand(OnAddInvoiceExecute);
RemoveInvoiceCommand = new DelegateCommand(OnRemoveInvoiceExecute, OnRemoveInvoiceCanExecute);
Invoices = new ObservableCollection<SubInvoiceWrapper>();
}
public SubPOWrapper SubPO
{
get { return _subPO; }
private set
{
_subPO = value;
OnPropertyChanged();
}
}
public SubInvoiceWrapper SelectedInvoice
{
get { return _selectedInvoice; }
set
{
_selectedInvoice = value;
OnPropertyChanged();
((DelegateCommand)RemoveInvoiceCommand).RaiseCanExecuteChanged();
}
}
public ICommand AddInvoiceCommand { get; }
public ICommand RemoveInvoiceCommand { get; }
public ObservableCollection<SubInvoiceWrapper> Invoices { get; }
public override async Task LoadAsync(int? subPOId)
{
var subPO = subPOId.HasValue
? await _subPORepository.GetByIdAsync(subPOId.Value)
: CreateNewSubPO();
InitializeSubInvoice(subPO.Invoices);
}
private void InitializeSubInvoice(ICollection<SubInvoice> invoices)
{
foreach (var wrapper in Invoices)
{
wrapper.PropertyChanged -= SubInvoiceWrapper_PropertyChanged;
}
Invoices.Clear();
foreach (var subInvoice in invoices)
{
var wrapper = new SubInvoiceWrapper(subInvoice);
Invoices.Add(wrapper);
wrapper.PropertyChanged += SubInvoiceWrapper_PropertyChanged;
}
}
private void SubInvoiceWrapper_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubInvoiceWrapper.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
}
private void InitializeSubPO(SubPO subPO)
{
SubPO = new SubPOWrapper(subPO);
SubPO.PropertyChanged += (s, e) =>
{
if (!HasChanges)
{
HasChanges = _subPORepository.HasChanges();
}
if (e.PropertyName == nameof(SubPO.HasErrors))
{
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
}
};
((DelegateCommand)SaveCommand).RaiseCanExecuteChanged();
if (SubPO.Id == 0)
{
// Little trick to trigger the validation
SubPO.Title = "";
}
}
}
SubInvoiceWrapper.cs (Wraps what is contained in the DataGridView)
public class SubInvoiceWrapper:ModelWrapper<SubInvoice>
{
public SubInvoiceWrapper(SubInvoice model) : base(model)
{
}
public string InvoiceName
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public DateTime Date
{
get { return GetValue<DateTime>(); }
set { SetValue(value); }
}
public decimal Amount
{
get { return GetValue<decimal>(); }
set { SetValue(value); }
}
public List<string> Status
{
get { return GetValue<List<string>>(); }
set { SetValue(value); }
}
}
ViewModelBase.cs (contains the propertychanged)
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
View.xaml
<DataGrid ItemsSource="{Binding RentItems}" SelectedItem="{Binding ID.EquipmentID, Mode=TwoWay}" x:Name="datagrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding EquipmentID, Mode=OneWay}" IsReadOnly="True"/>
</DataGrid.Columns>
Viewmodel
public class ViewModelReturnData : ViewModelBase
{
public ICommand MyCommand { get; set; }
private ObservableCollection<Equipment> rentitems = new ObservableCollection<Equipment>();
public ObservableCollection<Equipment> RentItems
{
get { return rentitems; }
set { rentitems = value; }
}
private Equipment id;
public Equipment ID
{
get { return id; }
set { id = value; ; OnPropertyChanged("ID"); }
}
public ViewModelReturnData()
{
MyCommand = new RelayCommand(ExecuteMyMethod, CanExecuteMyMethod);
}
private bool CanExecuteMyMethod(object parameter)
{
return true;
}
private void ExecuteMyMethod(object parameter)
{
using (rentalEntities context = new rentalEntities())
{
var query = ...
foreach (var item in query)
{
RentItems.Add(item);
}
}
}
}
In ViewModelBase I got the INotifyPropertyChanged.
I Would like to bind value of EquipmentID to my property ID, but I don't know why it is not working. Seems like I missed something in binding. Binding the value like this Text="{Binding ElementName=datagrid1, Path=SelectedItem.EquipmentID}" works, but I have to bind in to property.
stackoverflow!
Starting with my code:
XAML
<DataGrid Margin="25,112,25,10" Name="datGrid" RowHeight="30"
ColumnWidth="150" BorderThickness="1"
Style="{StaticResource AzureDataGrid}" IsReadOnly="False"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Content="CREATE" Name="headerCheckBox"
FontWeight="Bold" Width="Auto"
Checked="headerCheckBox_Checked"/>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Username" Binding="{Binding Path=Username}"/>
<DataGridTextColumn Header="First Name" Binding="{Binding Path=FirstName}"/>
<DataGridTextColumn Header="Last Name" Binding="{Binding Path=LastName}"/>
<DataGridTextColumn Header="Email" Binding="{Binding Path=Email}"/>
</DataGrid.Columns>
</DataGrid>
And C#
public partial class MainWindow : Window
{
ObservableCollection<MyData> MyItems = new ObservableCollection<MyData>();
public MainWindow()
{
datGrid.ItemsSource = MyItems;
MyItems.Add(new MyData { IsChecked = false, Username = "apetecca", FirstName = "Anthony", LastName = "Petecca", Email = "apetecca#email.com"});
MyItems.Add(new MyData { IsChecked = true, Username = "jhalls", FirstName = "Jake", LastName = "Halls", Email = "jhalls#email.com" });
}
public class MyData
{
public bool IsChecked { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
private void headerCheckBox_Checked(object sender, RoutedEventArgs e)
{
foreach (var item in MyItems)
{
item.IsChecked = true;
}
}
When I click on the headerCheckBox to check it, I've watched the variables in the foreach loop to see it is changing the items from false to true but doesn't visually show that on the DataGrid. However, if I manually check or uncheck the boxes, it displays correctly when going through that loop. The loop correctly sets the values but does not change the GUI display.
Everything I have seen online indicates it has to do with TwoWay mode and UpdateSourceTrigger. Both of these are set and I can't seem to find anything else on these.
Any help would be greatly appreciated!
EDIT-
I also tried changing my class to look like this
public class MyData
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
This didn't resolve it either.
Implement INotifyPropertyChanged and then have your properties raise event to make this happen.
Something like:
public class MyData : INotifyPropertyChanged
{
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
if (isChecked != value)
{
isChecked = value;
OnPropertyChanged("IsChecked");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a PrimaryItems List & foreach PrimaryItem there is a SecondaryItems list.So i used a ListBox as ItempTemplate of another ListBox.
<ListBox ItemsSource="{Binding PrimaryItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<ListBox ItemsSource="{Binding SecondaryItems}" SelectedItem="{Binding SelectedSecondaryItem}" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My View Model Code
private List<PrimaryItem> _primaryItems;
public List<PrimaryItem> PrimaryItems
{
get { return _primaryItems; }
set { _primaryItems = value;RaisePropertyChanged(); }
}
//SecondaryItems list is inside in each PrimaryItem
//private List<SecondaryItem> _secondaryItems;
//public List<SecondaryItem> SecondaryItems
//{
// get { return _secondaryItems; }
// set { _secondaryItems = value; RaisePropertyChanged(); }
//}
private SecondaryItem _selectedSecondaryItem;
public SecondaryItem SelectedSecondaryItem
{
get { return _selectedSecondaryItem; }
set
{
_selectedSecondaryItem = value;
if (_selectedSecondaryItem != null)
{
//TO DO
}
}
}<br/>
This is the class structure
public class PrimaryItem
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondaryItem> SecondaryItems{ get; set; }
}
public class SecondaryItem
{
public int Id { get; set; }
public string Name { get; set; }
}
and I set SelectedItem Binding to the Second ListBox.
But am not getting the Selection Trigger on Second ListBox.
Can we use a ListBox inside another ListBox` Template ? If yes how do we overcome this problem?
First of all, use ObservableCollection instead of List since it implements INotifyPropertyChanged interface.
As far as I understand your requirements, PrimaryItem class should has a property SecondaryItems. So remove it from ViewModel and paste to PrimaryItem class (as well as SelectedSecondaryItem property):
private ObservableCollection<SecondaryItem> _secondaryItems;
public ObservableCollection<SecondaryItem> SecondaryItems
{
get { return _secondaryItems; }
set { _secondaryItems = value; RaisePropertyChanged(); }
}
EDIT:
I've fully reproduced your situation and get it working.
Classes:
public class PrimaryItem
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondaryItem> SecondaryItems { get; set; }
private SecondaryItem _selectedSecondaryItem;
public SecondaryItem SelectedSecondaryItem
{
get { return _selectedSecondaryItem; }
set
{
_selectedSecondaryItem = value;
if (_selectedSecondaryItem != null)
{ // My breakpoint here
//TO DO
}
}
}
}
public class SecondaryItem
{
public int Id { get; set; }
public string Name { get; set; }
}
ViewModel:
public class MyViewModel : ViewModelBase
{
private List<PrimaryItem> _primaryItems;
public List<PrimaryItem> PrimaryItems
{
get { return _primaryItems; }
set { _primaryItems = value; RaisePropertyChanged("PrimaryItems"); }
}
public ErrorMessageViewModel()
{
this.PrimaryItems = new List<PrimaryItem>
{
new PrimaryItem
{
SecondaryItems =
new List<SecondaryItem>
{
new SecondaryItem { Id = 1, Name = "First" },
new SecondaryItem { Id = 2, Name = "Second" },
new SecondaryItem { Id = 3, Name = "Third" }
},
Name = "FirstPrimary",
Id = 1
}
};
}
}
View:
<Window x:Class="TestApp.Views.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TestApp.ViewModels;assembly=TestApp.ViewModels"
xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="Title" Height="240" Width="270" ResizeMode="NoResize"
WindowStartupLocation="CenterOwner" WindowStyle="ToolWindow">
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding PrimaryItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<ListBox ItemsSource="{Binding SecondaryItems}" SelectedItem="{Binding SelectedSecondaryItem}" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
You can try to use LinqToVisualTree, it can get alomost all Controls in your app, you just have to select what you want to find(in your case ListBoxItem), and then cast it to your model. I used it, when I needed to get text from TextBox which was in ListboxItem. But it also fits to your task.