I have a standard ListBox with a template:
<ListBox Name="statBox" ItemsSource="{Binding Path=p_statList}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=statName}" />
<TextBox Text="{Binding Path=statValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextChangedHandler" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public partial class MainWindow : Window
{
Controller controller = new Controller();
public MainWindow()
{
InitializeComponent();
DataContext = controller;
}
private void TextChangedHandler(object sender, TextChangedEventArgs args)
{
}
}
public struct Stat
{
public string statName { get; set; }
public int statValue { get; set; }
public Stat(string stat, int value)
{
statName = stat;
statValue = value;
}
}
internal class Controller : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private ObservableCollection<Stat> statList = new ObservableCollection<Stat>();
public ObservableCollection<Stat> p_statList
{
get { return statList; }
set {
statList = value;
}
}
public Controller()
{
p_statList.Add(new Stat("Attack", 2));
p_statList.Add(new Stat("Defense", 3));
p_statList.Add(new Stat("Luck", 4));
}
public void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
The issue I'm having is that there's no obvious way to distinguish value changes by the user among the text boxes.
Name="{Binding Path=controlName}"
, but C# did
not like that at all.Tags="{Binding Path=myName}"
, but I'm not finding anything in my research.An overly-complicated solution would be to traverse the UI recursively and look for the nth occurence of a TextBox, but that's very hacky and isn't sustainable if the UI changes.
The obvious solution is to just abandon the ListBox and the ObservableCollection and hard-code separate values to individual text boxes I can easily bind to, but that feels very un-savvy.
EDIT:
I've updated the above code with a more complete example that includes the C#. Some naming has changed, but the functionality is identical.
A breakpoint inside p_statList.set is never hit. I also tried breaking in TextChangedHandler to check controller.p_statList[0] in the immediate window, and the value never changes from 2.
Keep in mind binding here will change the properties of an instance not the instance itself.
Make sure your custom type Stat
is a mutable reference type (a class with writeable properties) and also consider implementing INotifyPropertyChanged
which will help you notify the UI back in case needed.
public class Stat : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public int _statValue;
public int statValue
{
get { return _statValue; }
set
{
_statValue = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(statValue)));
}
}
...
}
As for handling TextChanged
yourself, DataContext
of the sender TextBox is the (playerStat) instance you're looking for. Just cast sender to TextBox
and then cast DataContext
to your custom type then use it, No need to traverse the tree to figure the index.