【问题标题】:TextBox, Button and ListBox in a ListBox列表框中的文本框、按钮和列表框
【发布时间】:2010-05-17 03:07:08
【问题描述】:

我有一个列表框,每个列表项中有一堆控件。

<ListBox x:Name="projectList" IsSynchronizedWithCurrentItem="True">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}" />
                <ListBox x:Name="taskList" ItemsSource="{Binding Tasks}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding Name}" />
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <TextBox x:Name="textBoxTask" />
                <Button
                    x:Name="ButtonAddNewTask"
                    Content="Test"
                    CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext}"
                    Click="ButtonAddNewTask_Click"
                    />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

当我单击列表框中的按钮时,我想向列表框中的列表框添加一个新项目。我已经走到这一步了。所以我的问题是如何获取文本框以及如何更新列表框?

这是我的点击事件

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

感谢

【问题讨论】:

  • “获取文本框”到底是什么意思?
  • 目标是使用文本框中的文本创建任务。 TextBox newTaskTextBox = somthing here; proj.Tasks.Add(new Task(newTaskTextBox.Text));

标签: wpf listbox


【解决方案1】:

最简单的解决方案可能是让一个对象代表外部 ListBox 中的每一项。然后它将具有表示项目中每个控件的属性 - TextBox 中的文本和 ListBox 中的项目(我认为,基于您的 Click 处理程序的任务列表)。

在您的 Click 处理程序中,您可以获得 Button 的 DataContext(它应该是外部列表集合中的一个项目),并将新任务添加到该对象的任务列表中。由于内部 ListBox 绑定到该列表,因此应该使用新项目对其进行更新(假设它在添加项目时发送事件,例如使用 ObservableCollection)。

更新:根据您的 cmets,以下应该可以工作。

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

最后,在按钮的点击处理程序中,创建任务。 ButtonDataContext 将是该项目的 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 的实例以用于 UI。这对您更有效吗?

【讨论】:

  • 这里的问题是,即使我获得了数据上下文,在这种情况下,项目我无法获取添加新任务文本框,因此当我向项目添加任务时,我无法获得文本框中任务名称的值。
  • 不过,您不应该需要 TextBox。相反,DataContext 应该包含 TextBox.Text 绑定到的项目的名称。
  • 但是当我按下按钮时如何创建一个新任务呢?我需要知道文本框中的内容。
  • 我非常感谢我得到的所有帮助。但这不是一个好的解决方案。因为它是wiered拥有该项目的财产。
  • 问题是,您需要将它存储在某个地方。您不能只有一个 Window (或其他)字符串成员,因为每个项目都需要一个。是否要求您在列表中有 TextBox,或者单独的对话框可以吗?
【解决方案2】:

我假设您的项目列表框填充了项目对象的集合。我会将 AddNewTask ICommand 添加到 Project 类并通过属性公开它。然后将 Add New Task 按钮绑定到新的 AddNewTask ICommand。对于CommandParameter,输入TaskName,它将被传递到命令中。

尝试阅读一些 MVVM(模型视图视图模型)以了解其工作原理的一些示例。它非常干净,效果很好。

【讨论】:

  • 我将阅读更多关于模型视图模型概念的内容。但是用关于 UI 的代码来弄乱我的对象对我来说是一个非常糟糕的概念。所以我想尽可能避免这种情况。
  • 所以,您真正应该做的是用 ViewModel 对象包装真正的业务对象。它基本上是一个包装 Project 对象的对象,但它会将 Project 对象转换为与 WPF UI 一起使用。然后将命令放在 Project ViewModel 对象上。
【解决方案3】:

可以说,这个解决方案可以解决手头的任务。

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

【讨论】:

  • 虽然可行,但它并不是 IMO 的最佳解决方案。与模型对象进行交互比从控件本身中提取数据要好得多。
  • 我得到了今天为我描述的 ModelView。我是 webbforms 开发人员,这就是为什么我觉得有点不同。我正在尝试您的示例。
猜你喜欢
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多