tags:

views:

3101

answers:

8

Hello everybody,

I'm having a look at this MVVM stuff and I'm facing a problem.

The situation is pretty simple.

I have the following code in my index.xaml page

    <Grid>
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <view:MovieView ></view:MovieView>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

and in my index.xaml.cs

...

InitializeComponent(); base.DataContext = new MovieViewModel(ent.Movies.ToList()); ....

and here is my MoveViewModel

    public class MovieViewModel
{
    readonly List<Movies> _m;
    public ICommand TestCommand { get; set; }
    public MovieViewModel(List<Movies> m)
    {
        this.TestCommand = new TestCommand(this);
        _m = m;
    }
    public List<Movies> lm
    {
        get
        {
            return _m;
        }
    }
}

finally

here is my control xaml MovieView

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Label VerticalAlignment="Center" Grid.Row="0" Grid.Column="0">Title :</Label><TextBlock VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" Text="{Binding Title}"></TextBlock>  
    <Label VerticalAlignment="Center" Grid.Row="1" Grid.Column="0">Director :</Label><TextBlock VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" Text="{Binding Director}"></TextBlock>
    <Button Grid.Row="2" Height="20" Command="{Binding Path=TestCommand}" Content="Edit" Margin="0,4,5,4" VerticalAlignment="Stretch" FontSize="10"/>
</Grid>

So the problem I have is that if I set ItemsSource at Binding

it doesn't make anything

if I set ItemsSource="{Binding lm}"

it populates my itemsControl but the Command (Command="{Binding Path=TestCommand}" ) doesn't not work.

Of course it doesn't not work because TestCommand doesn't not belong to my entity object Movies.

So finally my question is,

what do I need to pass to the ItemsControl to make it working?

Thx in advance

+1  A: 

Try implementing the INotifyPropertyChanged interface:

public class MovieViewModel : INotifyPropertyChanged
{
    readonly List<Movies> _m;
    private ICommand _testCommand = null;
    public ICommand TestCommand { get { return _testCommand; } set { _testCommand = value; NotifyPropertyChanged("TestCommand"); } }
    public MovieViewModel(List<Movies> m)
    {
        this.TestCommand = new TestCommand(this);
        _m = m;        
    }
    public List<Movies> lm
    {
        get
        {
            return _m;
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string sProp)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(sProp));
        }
    }    
}

What happens is that the TestCommand has a value, and the UI gets no notification that a change is taking place.. On controls you solve this problem using Dependency properties, on data object, you can use the INotifyPropertyChanged interface..

Secondly, the Movie objects have no reference to the parent object..

You can solve this problem in three different ways:

  1. have a reference back to the model on Movie, and make the Bind path like so: ie.. if you property is named ParentMovieModel, then your Binding will be like:

    {Binding Path=ParentMovieModel.TestCommand}

  2. Make a binding based on ElementName like so: Seek up the parent control where you set your DataContext on, and give it a name: i.e. Root. Now create a binding based on the ElementName like so:

    {Binding ElementName=Root, Path=DataContext.TextCommand}

  3. Make a binding based on a RelativeSource like so: Seek up the parent control by type, and use the same path as the one above...

    {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type yourparentwindowtype}}, Path=DataContext.TextCommand}

Arcturus
How does implementing the INotifyPropertyChanged interface help in this case? You assign a value to TestCommand in the constructor. How can anything be subscribed to the PropertyChanged event at that time?
Matthijs Wessels
A: 

Thx for your answer

but finally what do I need to use for the binding

?

if I do so, my itemsControl acts like there is no items

if I do

or

what is the best way for the binding so ?

A: 

What about <ItemsControl ItemsSource="{Binding Path=lm}"> ?

Bas Bossink
A: 

in the ItemsSource="{Binding Path=lm}"> case

the itemsControl works well but I complety bypass my MovieViewModel

and I got this in the output window

System.Windows.Data Error: 39 : BindingExpression path error: 'TestCommand' property not found on 'object' ''Movies' (HashCode=1031007)'. BindingExpression:Path=TestCommand; DataItem='Movies' (HashCode=1031007); target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')

Movies is my entity object and owns only the Title and Director properties

+1  A: 

Got it working

here is the thing

 <ItemsControl DataContext="{Binding}" ItemsSource="{Binding lm}">

 Command="{Binding Path=DataContext.TestCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"

so the RelativeSource was the thing I've missed.

if somebody has a good explaination of this, I would be definitely happy.

+2  A: 

As soon as your items are rendered, each item gets set to the DataContext of the specific row it represents, so you are no longer able to reference to your first DataContext.. Also, due to the fact that you are in a DataTemplate, your bindings will start working when there is need for that Template.. so in that case you need to look up your parent control through a RelativeSource binding...

Hope that explains some things..

Arcturus
A: 

that explains some things,

just the code seems a bit barabarian to me.

I don't understand why these kind of basics things aren't warp up in more understandable properties

thank you very much anyway

A: 

//include namespace using Microsoft.Practices.Composite.Wpf.Commands;

readonly List _m; public ICommand TestCommand { get; set; } public MovieViewModel(List m) { this.TestCommand = new DelegateCommand(TestcommandHandler); _m = m; } public List lm { get { return _m; } }

void TestcommandHandler(object obj) { // add your logic here } }

Sam