Binding to Child Class of DataContext
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;
}
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
精彩评论