Самое простое решение, вероятно, состоит в том, чтобы один объект представлял каждый элемент во внешнем ListBox. Тогда он будет иметь свойства, которые будут представлять каждый элемент управления в элементе - текст в TextBox и элементы в ListBox (я думаю, что это список задач на основе вашего обработчика Click).
В обработчике Click вы можете получить DataContext Button (который должен быть элементом в коллекции внешнего списка) и добавить новую задачу в список задач этого объекта. Поскольку внутренний ListBox связан с этим списком, он должен быть обновлен новым элементом (при условии, что он отправляет события при добавлении элементов, например, с помощью ObservableCollection).
Обновление : Исходя из ваших комментариев, должно работать следующее.
Ваш Project
класс должен иметь два свойства:
class Project
{
public string Name { get; set; }
private ObservableCollection<Task> tasks =
new ObservableCollection<Task>();
public IList<Task> Tasks
{
get { return this.tasks; }
}
}
У класса Task
есть только одно свойство - имя задачи.
Класс ProjectView
является оберткой для класса Project
(я понял эту идею из ответа @ timothymcgrath). Он отслеживает название новой задачи и текущую Project
:
class ProjectView : INotifyPropertyChanged
{
public Project Project { get; set; }
private string newTaskName = string.Empty;
public string NewTaskName
{
get { return this.newTaskName; }
set
{
this.newTaskName = value;
this.OnPropertyChanged("NewTaskName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
PropertyChangedEventHandler eh = this.PropertyChanged;
if(null != eh)
{
eh(this, new PropertyChangedEventArgs(propName));
}
}
}
Вам понадобится новый класс, который будет использоваться как DataContext
. Примерно так:
class Model
{
private ObservableCollection<ProjectView> projects =
new ObservableCollection<ProjectView>();
public IList<ProjectView> Projects
{
get { return this.projects; }
}
}
В приведенном ниже коде установите DataContext
объекта для экземпляра вышеуказанного класса:
public class Window1
{
public Window1()
{
this.InitializeComponent();
this.DataContext = this.model;
}
private Model model = new Model();
}
В XAML привязки должны быть изменены для привязки к указанным выше свойствам:
<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Projects}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Project.Name}" />
<ListBox x:Name="taskList"
ItemsSource="{Binding Project.Tasks}"
DisplayMemberPath="Name" />
<TextBox x:Name="textBoxTask"
Text="{Binding Path=NewTaskName, UpdateSourceTrigger=PropertyChanged}"/>
<Button x:Name="ButtonAddNewTask" Content="Test"
Click="ButtonAddNewTask_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Наконец, в обработчике нажатия кнопки создайте задачу. DataContext
из Button
будет ProjectView
для этого элемента.
private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
ProjectView curProject = btn.DataContext as Project;
if(null != curProject)
{
curProject.Project.Tasks.Add(new Task()
{
Name = curProject.NewTaskName
});
}
}
Поскольку все элементы управления получают свои значения посредством привязки, вам не требуется доступ к самому элементу управления для получения данных - просто используйте структуры данных, которые уже предоставляют элементы управления.
Возможно, было бы лучше переместить код, который создает задачу, в другой класс (возможно, Project
), но я просто оставил его в обработчике событий для простоты ввода с моей стороны.
Обновление 2 : изменил приведенный выше код, чтобы переместить свойство NewTaskName
в отдельный класс, который упаковывает экземпляр Project
для использования с пользовательским интерфейсом. Это работает для вас лучше?