MVVMLearning
MVVMLearning

Reputation:

ICommand in MVVM WPF

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

Upvotes: 3

Views: 11368

Answers (6)

Sam
Sam

Reputation: 11

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

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

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

Upvotes: 1

Arcturus
Arcturus

Reputation: 27065

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

Upvotes: 4

MVVMLearning
MVVMLearning

Reputation:

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.

Upvotes: 1

MVVMLearning
MVVMLearning

Reputation:

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

Upvotes: 0

Bas Bossink
Bas Bossink

Reputation: 9708

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

Upvotes: 0

Arcturus
Arcturus

Reputation: 27065

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}

Upvotes: 2

Related Questions