PortePoisse
PortePoisse

Reputation: 219

How To Instantiate ViewModel In A Data Template

I'm new developer in WPF, universal apps, etc..

I created a view to display questions.

I have a QuestionsViewModel which exposes a Question collection. In the XAML, I use an ItemsControl to display the questions.

Now in the DataTemplate, I would like to use a user control to display the question, instantiate my QuestionViewModel, bind the current Question to it and set the view model as context of the user control. My problem is than {Binding} doesn't give me a Question, I have a Binding object :/

Should i do that way ? How to get the current Question in the ViewModel ?


Code

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

}

Questions view

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

Question user control

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

Upvotes: 4

Views: 518

Answers (3)

PortePoisse
PortePoisse

Reputation: 219

Finally i will use a collection of QuestionViewModel instead of Question. That will probably imply to synchronize the Question collection with the QuestionViewModel...

Upvotes: 0

mm8
mm8

Reputation: 169270

A target property that you want to bind something to should be a dependency property: https://msdn.microsoft.com/en-us/windows/uwp/xaml-platform/custom-dependency-properties

But what you should do here is to let the QuestionItemControl inherit the DataContext from its parent element in the ItemsControl. Just avoid setting the DataContext property of the QuestionItemControl explicitly in the ItemsControl:

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

...and bind directly to the Title and Answer properties in 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>

This should work since the DataContext of each QuestionItemControl is actually a Question object in the ItemsSource of the ItemsControl.

Upvotes: 1

user7344528
user7344528

Reputation:

There're some things need to modify in your code.

I directly make a code sample for your reference.

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>

Upvotes: 2

Related Questions