2009-03-10 7 views
1

J'ai une zone de liste avec un tas de contrôles dans chaque élément de la liste. Lorsque je clique sur le bouton dans la zone de liste, je veux ajouter un nouvel élément à la liste dans la zone de liste. Je suis venu jusqu'ici. Donc, ma question est comment puis-je obtenir la zone de texte et comment puis-je mettre à jour la liste?TextBox, Button et ListBox dans un ListBox

Voici mon événement click

private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e) 
{ 
    Button button = (Button)sender; 
    Project proj = button.DataContext as Project; 
    if(proj.Tasks == null) 
     proj.Tasks = new List<Task>(); 

    proj.Tasks.Add(new Task("Added Task")); 
} 

Thanx

+0

Qu'est-ce que vous entendez par «mettre en place la zone de texte»? – Joey

+0

Le but était de créer la tâche avec le texte de la zone de texte. TextBox newTaskTextBox = quelque chose ici; proj.Tasks.Add (nouvelle tâche (newTaskTextBox.Text)); –

Répondre

0

Cette solution a fonctionné pour la tâche à portée de main.

private void ButtonAddNewTask_Click(object sender, RoutedEventArgs e) 
    { 
     Button button = (Button)sender; 
     DependencyObject obj = LogicalTreeHelper.GetParent(button); 
     StackPanel item = obj as StackPanel; 
     TextBox textBox = item.FindName("textBoxTask") as TextBox; 
     ListBox listBox = item.FindName("taskList") as ListBox; 

     Project proj = button.DataContext as Project; 
     if(proj.Tasks == null) 
      proj.Tasks = new List<Task>(); 

     listBox.ItemsSource = proj.Tasks; 
     listBox.Items.Refresh(); 
    } 
+0

Alors que cela fonctionne, ce n'est pas la meilleure solution IMO. Il est préférable d'interagir avec les objets de votre modèle plutôt que d'extraire les données des contrôles eux-mêmes. – Andy

+0

J'ai eu le ModelView décrit pour moi aujourd'hui. Je suis un développeur webbforms et c'est pourquoi je pense un peu différent. J'essaie ton exemple. –

7

La solution la plus simple serait susceptible d'avoir un objet représentent chaque élément dans la zone de liste externe. Il aurait alors des propriétés qui représenteraient chaque contrôle dans l'élément - le texte dans le TextBox, et les éléments dans le ListBox (une liste de tâches, je pense, basée sur votre gestionnaire de clic).

Dans votre gestionnaire de clic, vous pouvez obtenir le DataContext de Button (qui devrait être un élément de la collection de la liste externe) et ajouter une nouvelle tâche à la liste des tâches de cet objet. Puisque le ListBox interne est lié à cette liste, il doit être mis à jour avec le nouvel élément (en supposant qu'il envoie des événements lorsque des éléments sont ajoutés, comme avec ObservableCollection).

Mise à jour: D'après vos commentaires, ce qui suit devrait fonctionner.

Votre classe Project doit avoir deux propriétés:

class Project 
{ 
    public string Name { get; set; } 

    private ObservableCollection<Task> tasks = 
     new ObservableCollection<Task>(); 
    public IList<Task> Tasks 
    { 
     get { return this.tasks; } 
    } 
} 

La classe Task a juste une propriété - le nom de la tâche.

La classe ProjectView est un wrapper autour de la classe Project (j'ai trouvé cette idée dans la réponse de @ timothymcgrath). Il garde la trace du nom d'une nouvelle tâche, et la Project actuelle:

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)); 
     } 
    } 
} 

Vous aurez besoin d'une nouvelle classe qui sera utilisée comme DataContext. Quelque chose comme ceci:

class Model 
{ 
    private ObservableCollection<ProjectView> projects = 
     new ObservableCollection<ProjectView>(); 
    public IList<ProjectView> Projects 
    { 
     get { return this.projects; } 
    } 
} 

Dans le code derrière, définissez l'DataContext de l'objet à une instance de la classe ci-dessus:

public class Window1 
{ 
    public Window1() 
    { 
     this.InitializeComponent(); 

     this.DataContext = this.model; 
    } 

    private Model model = new Model(); 
} 

Dans le XAML, les liaisons devrait être modifiée pour se lier à la propriétés ci-dessus:

<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> 

Enfin, dans le gestionnaire de clic pour le bouton, créez la tâche. Le DataContext du Button sera le ProjectView pour cet article.

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 
     }); 
    } 
} 

Étant donné que tous les contrôles obtiennent leurs valeurs par liaison, vous n'avez pas besoin d'accéder au contrôle lui-même pour obtenir les données - il suffit d'utiliser les structures de données qui approvisionnent les contrôles déjà.

Il serait probablement préférable de déplacer le code qui crée la tâche dans une autre classe (éventuellement Project), mais je l'ai simplement laissé dans le gestionnaire d'événements pour faciliter la frappe de ma part.

Update 2: modifié le code ci-dessus pour déplacer la propriété NewTaskName dans une catégorie distincte qui enveloppe une instance de Project pour une utilisation avec l'interface utilisateur. Est-ce que cela fonctionne mieux pour vous?

+0

Problème ici est même si j'obtiens le datacontext, le projet dans ce cas je ne peux pas obtenir la boîte de texte Ajouter une nouvelle tâche ainsi quand j'ajoute une tâche au projet je ne peux pas obtenir la valeur pour le nom de tâches du zone de texte. –

+0

Vous ne devriez pas avoir besoin de la TextBox, cependant. Au lieu de cela, le DataContext doit contenir le nom du projet, auquel TextBox.Text est lié. – Andy

+0

Mais alors comment créer une nouvelle tâche quand j'appuie sur le bouton? J'ai besoin de savoir quoi dans la zone de texte. –

2

Je suppose que votre liste de projets est remplie avec une collection d'objets de projet. Je voudrais ajouter une commande IC AddNewTask à la classe Project et l'exposer à travers une propriété. Puis liez le bouton Ajouter une nouvelle tâche au nouvel ICommand AddNewTask. Pour le CommandParameter, placez le TaskName dans et il sera passé dans la commande. Essayez de lire sur certains MVVM (Model View ViewModel) pour quelques exemples de comment cela fonctionne. C'est très propre et fonctionne très bien.

+0

Je vais en lire plus sur le concept Model ViewModel. Mais encombrer mes objets de code concernant l'interface utilisateur me semble être un très mauvais concept. Donc, je veux éviter cela autant que possible. –

+1

Donc, ce que vous devriez vraiment faire est d'envelopper l'objet métier réel avec un objet ViewModel. Il s'agit essentiellement d'un objet enveloppant l'objet Project, mais il traduit l'objet Project pour fonctionner avec l'interface utilisateur WPF. Vous devez ensuite placer la commande sur l'objet Project ViewModel. – timothymcgrath