Search code examples
c#wpfmvvmdata-bindingcode-behind

How to programmatically bind to property of passed in object


I've been trying to bind the source, row, and column properties of a newly generated image to some value on an object on my viewmodel. Ideally, this could be solved by binding to the properties of the object passed into the eventArgs. Is there a way to do this?

Here's an example of what I want to do:

private void OnNewEnemy(EnemyViewModel enemyViewModel)
        {
            Image Enemy = new Image()
            {
                Stretch = Stretch.Fill
            };

            Enemy.SetBinding(Image.SourceProperty, new Binding("enemyViewModel.Icon"));
            Enemy.SetBinding(Grid.RowProperty, new Binding("enemyViewModel.Row"));
            Enemy.SetBinding(Grid.ColumnProperty, new Binding("enemyViewModel.Column"));

            locationGrid.Children.Add(Enemy);
        }

I've been looking into using the syntax new Binding("property.subproperty") but, as my "enemyViewModel" objects are regularly added to and removed from a list on my viewmodel, this method doesn't seem to work.

I'm also aware that it's bad practice to write in the code behind but this seems to be the only way to dynamically add and remove images from the GUI.

Thanks for any suggestions.


Solution

  • You could write

    Enemy.SetBinding(Image.SourceProperty,
        new Binding("Icon")
        {
            Source = enemyViewModel
        });
    

    or

    Enemy.SetBinding(Image.SourceProperty,
        new Binding
        {
            Path = new PropertyPath("Icon"),
            Source = enemyViewModel
        });
    

    You should however consider using an ItemsControl that uses a Grid as its ItemsPanel and has the Image and the Bindings in its ItemTemplate and ItemContainerStyle.

    In the example below, Enemies would be a public property of type ObservableCollection<EnemyViewModel> in your main view model.

    <ItemsControl ItemsSource="{Binding Enemies}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        ... 
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        ...
                    </Grid.RowDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Grid.Column" Value="{Binding Column}"/>
                <Setter Property="Grid.Row" Value="{Binding Row}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Image Source="{Binding Icon}" Stretch="Fill"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    

    See also Data Templating Overview.