I have a WPF Window that has a list of tasks on the left, and details about the selected task on the right. The tasks is just a collection of task, you can consider it POCO.
Single ViewModel having both Tasks and the selected Task
public class TaskViewModel { public TaskViewModel() { Tasks = new Task().GetTasks(); } // A LIST OF MANY TASKS private Tasks _tasksViewModel; public Tasks Tasks { get { return _tasksViewModel; } private set { _tasksViewModel= value; } } // A SINGLE SELECTED TASK private static readonly DependencyProperty CurrentTaskProperty = DependencyProperty.Register( "CurrentTask", typeof(Task), typeof(TaskViewModel), new PropertyMetadata(null)); public Task CurrentTask { get { return (Task) GetValue(CurrentTaskProperty); } set { SetValue(CurrentTaskProperty, value); } } } Window in XAML:
<Window> <!-- A List with multiple tasks. --> <ListBox x:Name="listBoxTasks" ItemsSource="{Binding Path=Task, Mode=TwoWay} /> <!-- The List's selected task's "Task Name" property. --> <TextBox x:Name="textBoxTaskName" Text="{Binding Path=CurrentTask, Mode=TwoWay}" /> <!-- The List's selected tasks' "Task Items" child objects. --> <ListBox x:Name="listBoxTaskItems" ItemsSource="{Binding Path=EditableValue, Mode=TwoWay}" /> </Window> And my MainWindow xaml codebehind:
public partial class MainWindow : Window { private MainWindow() { DataContext = _tasksViewModel; } private void OnListBoxTasksSelectionChanged(object sender, SelectionChangedEventArgs e) { _tasksViewModel.CurrentTask = task; DataContext = _tasksViewModel; } } And now the questions:
How many view models should I have?
- Should I add a second ViewModel? Should I have one ViewModel for Tasks and one for the selected Task, or should I have just one ViewModel for the whole window?
And what is the best way to bind in XAML, and to set the data context(s) in C#?
How can I have only one DataContext for the whole Window? If the Window's DataContext is the TasksViewModel, how can I bind something to TasksViewModel.CurrentTask? Is there a way to bind to a property of a child item of Window.DataContext, or do I need to manually specify a separate DataContext for any controls that use CurrentTask?
- Is there a way to do it like this:
In C#:
DataContext = _tasksViewModel.Tasks In XAML:
<!-- Can I do something similar to DataContext.CurrentTask.TaskName ??? --> <TextBox x:Name="textBoxTaskName" MinWidth="100" Text="{Binding Path=DataContext.CurrentTask.TaskName, Mode=TwoWay}" /> <!-- What about DataContext.CurrentTask.TaskItems??? --> <ListBox x:Name="listBoxTaskItems" ItemsSource="{Binding Path=**DataContext.CurrentTask.TaskItems**, DisplayMember="TaskItemName}" /> Or in other words, is there a better solution than to do this:
// When a Task in Tasks is selected... OnListBoxTasksSelectionChanged() { [...] // Set that Task as the DataContext for other controls textBoxTaskName.DataContext = Task; listBoxTaskItems.DataContext = Task.TaskItems; }