0

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

1 Answer 1

2

The path in your Binding can bind deeply into properties of the DataContext. The levels are separated by a dot. You can also access indexers from your binding.

If your DataContext is e.g. the TasksViewModel thane you bind to CurrentTask by saying {Binding CurrentTask}. Also, you would typically bing the SelectedItem of your ListBox to the CurrentTask.

What is plain wrong is trying to inherit from DependencyObject as ViewModel and specifying dependency properties. Dependency properties are needed where you want to specify bindings. ViewModels are just data sources.

Hence, the correct way is to implement INotifyPropertyChanged and notity WPF by raising the event which of your properties change

Sign up to request clarification or add additional context in comments.

2 Comments

Is this the correct syntax for binding the textbox? I still can't get it to show when my SelectedTask changes. <TextBox x:Name="textBoxTaskName" Text="{Binding CurrentTask, Path=Key, Mode=TwoWay}" /> private Task _currentTask; public Task CurrentTask { get { return _currentTask; } set { _currentTask = value; OnPropertyChanged("CurrentTask"); OnPropertyChanged("CurrentTask.TaskName"); OnPropertyChanged("TaskName"); ..
The default parameter to binding is "Path", so you are basically stating Path twice (CurrentTask, Key) - try combining them: Path=CurrentTask.Key

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.