如何在数据模板中实例化 ViewModel
How To Instantiate ViewModel In A Data Template
我是 WPF、通用应用程序等方面的新开发人员。
我创建了一个视图来显示问题。
我有一个公开问题集合的 QuestionsViewModel。在 XAML 中,我使用 ItemsControl 来显示问题。
现在在 DataTemplate 中,我想使用用户控件来显示问题,实例化我的 QuestionViewModel,将当前问题绑定到它,并将视图模型设置为用户控件的上下文。我的问题是 {Binding} 没有给我一个问题,我有一个 Binding 对象:/
我应该那样做吗?
如何获取 ViewModel 中的当前问题?
代码
public class Question : AEntity, IQuestion
{
public Question(String title, String answer) : base()
{
this.Title = title;
this.Answer = answer;
}
public string Title { get; set; }
public string Answer { get; set; }
}
public class QuestionsViewModel : BaseViewModel
{
private String description;
public QuestionsViewModel()
{
ObservableCollection<Question> questions = new ObservableCollection<Question>();
questions.CollectionChanged += Questions_CollectionChanged;
Questions = questions;
AddQuestionCommand = new AddQuestionCommand(Questions);
AddQuestionCommand.Execute(null);//add a question for test
}
private void Questions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Description = String.Format("{0} question(s)", Questions.Count);
}
public ICollection<Question> Questions { get; private set; }
public ICommand AddQuestionCommand { get; private set; }
public String Description {
get { return description; }
set
{
description = value;
onPropertyChanged("Description");
}
}
}
public class QuestionViewModel : BaseViewModel
{
private Question question;
public QuestionViewModel()
{
}
public Question Question { get; set; }
}
问题视图
<UserControl x:Class="Question_Answer.View.Control.QuestionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Question_Answer.Model"
xmlns:uc="using:Question_Answer.View.Control"
xmlns:vm="using:Question_Answer.ViewModel.Control"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<vm:QuestionsViewModel Description="view description"> </vm:QuestionsViewModel>
</UserControl.DataContext>
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Questions}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="model:Question">
<uc:QuestionItemControl>
<uc:QuestionItemControl.DataContext>
<vm:QuestionViewModel Question="{Binding}" />
</uc:QuestionItemControl.DataContext>
</uc:QuestionItemControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
质疑用户控制权
<UserControl
x:Class="Question_Answer.View.Control.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Question_Answer.View.Control"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Question.Title}" />
<TextBlock Grid.Column="1" Text="{Binding Question.Answer}" />
</Grid>
您的代码中有些地方需要修改。
我直接做了一个代码示例供大家参考。
public class QuestionsViewModel:ViewModelBase
{
private String description;
public ObservableCollection<Question> questions { get; set; }
public QuestionsViewModel()
{
questions = new ObservableCollection<Question>();
questions.CollectionChanged += Questions_CollectionChanged;
questions.Add(new Question("t1","a1"));
}
private void Questions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Description = String.Format("{0} question(s)", e.NewItems.Count);
}
public String Description
{
get { return description; }
set
{
description = value;
RaisePropertyChanged("Description");
}
}
}
public class QuestionViewModel:ViewModelBase
{
private Question question;
public QuestionViewModel()
{
}
public Question Question
{
get { return question; }
set
{
question = value;
RaisePropertyChanged("Question");
}
}
}
<UserControl
x:Class="AppMvvm2.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" />
<TextBlock Grid.Column="1" Text="{Binding Answer}" />
</Grid>
<UserControl
x:Class="AppMvvm2.QuestionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<local:QuestionsViewModel></local:QuestionsViewModel>
</UserControl.DataContext>
<StackPanel Margin="0 50 0 0">
<TextBlock Text="{Binding Description}"></TextBlock>
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding questions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:QuestionItemControl></local:QuestionItemControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<Page
x:Class="AppMvvm2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:QuestionsControl></local:QuestionsControl>
</Grid>
一个target 属性你想绑定一些东西到应该是一个依赖属性:https://msdn.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties
但是您在这里应该做的是让 QuestionItemControl 从其在 ItemsControl 中的 parent 元素继承 DataContext。只是避免在 ItemsControl 中显式设置 QuestionItemControl 的 DataContext 属性:
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Questions}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="model:Question">
<uc:QuestionItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...并直接绑定到 QuestionItemControl.xaml 中的标题和答案属性:
<UserControl
x:Class="Question_Answer.View.Control.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Question_Answer.View.Control"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" />
<TextBlock Grid.Column="1" Text="{Binding Answer}" />
</Grid>
</UserControl>
这应该有效,因为每个 QuestionItemControl 的 DataContext 实际上是 ItemsControl 的 ItemsSource 中的 Question
object。
最后我将使用一组 QuestionViewModel 而不是 Question。这可能意味着将问题集合与 QuestionViewModel 同步...
我是 WPF、通用应用程序等方面的新开发人员。
我创建了一个视图来显示问题。
我有一个公开问题集合的 QuestionsViewModel。在 XAML 中,我使用 ItemsControl 来显示问题。
现在在 DataTemplate 中,我想使用用户控件来显示问题,实例化我的 QuestionViewModel,将当前问题绑定到它,并将视图模型设置为用户控件的上下文。我的问题是 {Binding} 没有给我一个问题,我有一个 Binding 对象:/
我应该那样做吗? 如何获取 ViewModel 中的当前问题?
代码
public class Question : AEntity, IQuestion
{
public Question(String title, String answer) : base()
{
this.Title = title;
this.Answer = answer;
}
public string Title { get; set; }
public string Answer { get; set; }
}
public class QuestionsViewModel : BaseViewModel
{
private String description;
public QuestionsViewModel()
{
ObservableCollection<Question> questions = new ObservableCollection<Question>();
questions.CollectionChanged += Questions_CollectionChanged;
Questions = questions;
AddQuestionCommand = new AddQuestionCommand(Questions);
AddQuestionCommand.Execute(null);//add a question for test
}
private void Questions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Description = String.Format("{0} question(s)", Questions.Count);
}
public ICollection<Question> Questions { get; private set; }
public ICommand AddQuestionCommand { get; private set; }
public String Description {
get { return description; }
set
{
description = value;
onPropertyChanged("Description");
}
}
}
public class QuestionViewModel : BaseViewModel
{
private Question question;
public QuestionViewModel()
{
}
public Question Question { get; set; }
}
问题视图
<UserControl x:Class="Question_Answer.View.Control.QuestionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Question_Answer.Model"
xmlns:uc="using:Question_Answer.View.Control"
xmlns:vm="using:Question_Answer.ViewModel.Control"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<vm:QuestionsViewModel Description="view description"> </vm:QuestionsViewModel>
</UserControl.DataContext>
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Questions}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="model:Question">
<uc:QuestionItemControl>
<uc:QuestionItemControl.DataContext>
<vm:QuestionViewModel Question="{Binding}" />
</uc:QuestionItemControl.DataContext>
</uc:QuestionItemControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>
质疑用户控制权
<UserControl
x:Class="Question_Answer.View.Control.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Question_Answer.View.Control"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Question.Title}" />
<TextBlock Grid.Column="1" Text="{Binding Question.Answer}" />
</Grid>
您的代码中有些地方需要修改。
我直接做了一个代码示例供大家参考。
public class QuestionsViewModel:ViewModelBase
{
private String description;
public ObservableCollection<Question> questions { get; set; }
public QuestionsViewModel()
{
questions = new ObservableCollection<Question>();
questions.CollectionChanged += Questions_CollectionChanged;
questions.Add(new Question("t1","a1"));
}
private void Questions_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Description = String.Format("{0} question(s)", e.NewItems.Count);
}
public String Description
{
get { return description; }
set
{
description = value;
RaisePropertyChanged("Description");
}
}
}
public class QuestionViewModel:ViewModelBase
{
private Question question;
public QuestionViewModel()
{
}
public Question Question
{
get { return question; }
set
{
question = value;
RaisePropertyChanged("Question");
}
}
}
<UserControl
x:Class="AppMvvm2.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" />
<TextBlock Grid.Column="1" Text="{Binding Answer}" />
</Grid>
<UserControl
x:Class="AppMvvm2.QuestionsControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<local:QuestionsViewModel></local:QuestionsViewModel>
</UserControl.DataContext>
<StackPanel Margin="0 50 0 0">
<TextBlock Text="{Binding Description}"></TextBlock>
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding questions}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:QuestionItemControl></local:QuestionItemControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<Page
x:Class="AppMvvm2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppMvvm2"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:QuestionsControl></local:QuestionsControl>
</Grid>
一个target 属性你想绑定一些东西到应该是一个依赖属性:https://msdn.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties
但是您在这里应该做的是让 QuestionItemControl 从其在 ItemsControl 中的 parent 元素继承 DataContext。只是避免在 ItemsControl 中显式设置 QuestionItemControl 的 DataContext 属性:
<ItemsControl Name="Questions" Grid.Row="1" Grid.ColumnSpan="2" ItemsSource="{Binding Questions}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="model:Question">
<uc:QuestionItemControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...并直接绑定到 QuestionItemControl.xaml 中的标题和答案属性:
<UserControl
x:Class="Question_Answer.View.Control.QuestionItemControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Question_Answer.View.Control"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Title}" />
<TextBlock Grid.Column="1" Text="{Binding Answer}" />
</Grid>
</UserControl>
这应该有效,因为每个 QuestionItemControl 的 DataContext 实际上是 ItemsControl 的 ItemsSource 中的 Question
object。
最后我将使用一组 QuestionViewModel 而不是 Question。这可能意味着将问题集合与 QuestionViewModel 同步...