Search code examples
wpfdatagridselecteditemcollectionviewsource

Binding an Image control to a datagrids datacontext (viewmodel)


I have a WPF DataGrid that is bound to a CollectionViewSource. The view source contains some data about rental properties, i.e. the address and an image of that property. The DataGrid shows the address information but the image is shown via an Image control outside of the DataGrid. This Image control is bound to the data same DataContext as the DataGrid. The idea is that when the user selects a row in the DataGrid the image changes to the image of that property. Select a different row (property) and the image changes (I have put the Image control outside of the DataGrid as showing every image make the grid rather large). By default the DataGrid is read only with a row selection unit of 'fullrow'. So far all works as required. I have a Button to allow the user to edit the grid, this changes the grid to editable with a row selection unit of 'cell'.

Now it falls over, the image disappears and there no longer seems to be a link back to the DataContext. I am guessing that some magic is linking the selected item of the data grid back to the view source which allows the image property to be available to my Image control but once the row selection changes to cell there is no longer a selected item in the DataGrid.

How do I keep the image visible when I make the grid editable?

I am not a professional programmer and am learning WPF and C# for 'fun'. Grateful thanks.

The relevant markup for the DataGrid:

<Window
    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:Entities="clr-namespace:wpfPortfolioManager.Entities"
    xmlns:local="clr-namespace:wpfPortfolioManager"
    xmlns:DataSets="clr-namespace:wpfPortfolioManager.DataSets"
    xmlns:dsAllTablesTableAdapters="clr-namespace:wpfPortfolioManager.DataSets.dsAllTablesTableAdapters" x:Name="window" x:Class="wpfPortfolioManager.Forms.HomeScreen"
    mc:Ignorable="d"
    Title="HomeScreen" SizeToContent="WidthAndHeight" Loaded="Window_Loaded" >
    <Window.Resources>
        <CollectionViewSource x:Key="propertyViewSource" d:DesignSource="{d:DesignInstance {x:Type Entities:Property}, CreateList=True}"/>
    </Window.Resources>

    <TabItem x:Name="tabProperties" Header="Properties" DataContext="{StaticResource propertyViewSource}" Background="{x:Null}" >
        <Grid Background="#FFDA4D2E" ScrollViewer.CanContentScroll="True" ShowGridLines="False">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="98"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <StackPanel Grid.Column="0" Orientation="Vertical" Width="{Binding ElementName=buttonSaveProperties, Path=Width}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="78">
                <Button x:Name="buttonSaveProperties" IsEnabled="{Binding ElementName=propertyDataGrid, Path=IsReadOnly, Converter={StaticResource convertBoolean}}" Click="buttonSaveProperties_Click" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2,5">
                    <StackPanel Orientation="Horizontal" Width="90">
                        <Image   Stretch="Fill" Width="25" Height="25" Margin="0" Source="Save_16x.png"/>
                        <TextBlock Text="Save" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0"/>
                    </StackPanel>
                </Button>

                <Button  x:Name="buttonCancel" Margin="2,5" IsEnabled="{Binding ElementName=propertyDataGrid, Path=IsReadOnly, Converter={StaticResource convertBoolean}}" Click="buttonCancelProperty_Click">
                    <StackPanel Orientation="Horizontal" Width="90">
                        <Image   Stretch="Fill" Width="25" Height="25" Margin="0" Source="stop.png"/>
                        <TextBlock Text="Cancel" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0"/>
                    </StackPanel>
                </Button>
            </StackPanel>

            <StackPanel Grid.Column="1" Orientation="Horizontal" Width="Auto">
                <Button x:Name="buttonMakeEditable" Click="buttonMakePropertiesEditable_Click"  VerticalAlignment="Top" Margin="0,0,0,0" Width="30" Height="30"  HorizontalAlignment="Left" >
                    <Image Source="i_edit-itemBlue_F12.png"  Stretch="Fill" Width="25" Height="25" Margin="1"/>
                </Button>
                <DataGrid x:Name="propertyDataGrid" ItemsSource="{Binding}" SelectedItem="{Binding SelectedProperty}" Style="{DynamicResource ReadOnlyDataGridStyle}"  HorizontalAlignment="Left" RowHeaderWidth="20">
                    <DataGrid.Columns>
                        <DataGridTextColumn x:Name="propertyNameColumn" Width="Auto" Header="Property" Binding="{Binding PropertyName}"/>
                        <DataGridTextColumn x:Name="address1Column" Width="Auto" Header="Street" Binding="{Binding Address1}"/>
                        <DataGridTextColumn x:Name="address2Column" Width="Auto" Header="Town" Binding="{Binding Address2}"/>
                        <DataGridTextColumn x:Name="address3Column" Width="Auto" Header="County" Binding="{Binding Address3}"/>
                        <DataGridTextColumn x:Name="address4Column" Width="Auto" Header="Post Code" Binding="{Binding Address4}"/>
                        <DataGridTemplateColumn x:Name="columnNewPicture" Header="New Picture"  Visibility="Collapsed" >
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Button x:Name="buttonNewPicture" Click="buttonNewPicture_Click" CommandParameter="{Binding Path=PropertyID}" Height="10" IsEnabled="{Binding ElementName=propertyDataGrid, Path=IsReadOnly, Converter={StaticResource convertBoolean}}"></Button>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                </DataGrid>
                <Border BorderBrush="#FF6EA410" BorderThickness="2" Height="150" Width="150" HorizontalAlignment="Left" VerticalAlignment="Top" >
                    <Image x:Name="testImage" Source="{Binding Image}" HorizontalAlignment="Left" Margin="4,0,0,0" VerticalAlignment="Top" Width="150" Height="150"/>
                </Border>
            </StackPanel>
        </Grid>
    </TabItem>

<!-- Rest of the xaml file -->

</Window>

DataGrid style:

<Style x:Key="BaseDataGridStyle" TargetType="DataGrid">

    <!--#region Grid -->
    <Setter Property="HorizontalAlignment" Value="Left"/>
    <Setter Property="VerticalAlignment" Value="Top"/>
    <Setter Property="AutoGenerateColumns" Value="False"/>
    <Setter Property="GridLinesVisibility" Value="None"/>
    <Setter Property="IsReadOnly" Value="True"/>
    <Setter Property="Margin" Value="0"/>
    <Setter Property="Width" Value="Auto"/>
    <Setter Property="BorderThickness" Value="1"/>
    <!--#endregion-->

    <!--#region Font -->
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="FontSize" Value="14"/>
    <Setter Property="FontWeight" Value="DemiBold"/>
    <!--#endregion-->

    <!--#region Height & Width -->
    <Setter Property="MinHeight" Value="50 "/>
    <Setter Property="MaxHeight" Value="800"/>
    <!--<Setter Property="MinWidth" Value="600"/>-->
    <Setter Property="MaxWidth" Value="1200"/>
    <Setter Property="Width" Value="Auto"/>
    <Setter Property="Height" Value="Auto"/>
    <!--#endregion-->

    <!--#region Columns -->
    <Setter Property="ColumnHeaderStyle" Value="{DynamicResource DataGridColumnHeader}"/>
    <Setter Property="ColumnWidth" Value="Auto"/>
    <Setter Property="CanUserReorderColumns" Value="False"/>
    <Setter Property="CanUserResizeColumns" Value="False"/>
    <!--#endregion-->

    <!--#region Rows -->
    <!--<Setter Property="RowBackground" Value="Red"/>-->
    <Setter Property="SelectionUnit" Value="FullRow"/>
    <!--<Setter Property="CellStyle" Value="{DynamicResource DataGridCellText}"/>-->
    <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected"/>
    <!--#endregion-->

    <!--<Setter Property="RowStyle" Value="{DynamicResource DataGridRowStyle1}"/>-->
    <Setter Property="CellStyle" Value="{DynamicResource DataGridCellStyle1}"/>
</Style>

<Style x:Key="ReadOnlyDataGridStyle"  BasedOn="{StaticResource  BaseDataGridStyle}" TargetType="DataGrid">
    <!--#region Grid -->

    <Setter Property="BorderBrush" Value="Red"/>
    <!--#endregion-->

    <!--#region Rows -->
    <Setter Property="CanUserAddRows" Value="False"/>
    <Setter Property="CanUserDeleteRows" Value="False"/>
    <Setter Property="CanUserResizeRows" Value="False"/>
    <!--#endregion-->

</Style>

<Style x:Key="EditableDataGridStyle" BasedOn="{StaticResource  BaseDataGridStyle}" TargetType="DataGrid">
    <!--#region Grid -->
    <Setter Property="SelectionUnit" Value="Cell"/>
    <Setter Property="IsReadOnly" Value="False"/>
    <Setter Property="BorderBrush" Value="Green"/>
    <!--#endregion-->

    <!--#region Rows -->
    <Setter Property="CanUserAddRows" Value="True"/>
    <Setter Property="CanUserDeleteRows" Value="True"/>
    <!--#endregion-->
</Style>

<Style x:Key="DataGridColumnHeader" TargetType="DataGridColumnHeader">
    <!--#region Column Header -->
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="FontSize" Value="16"/>
    <Setter Property="FontWeight" Value="DemiBold"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <!--#endregion-->
</Style>

<Style x:Key="DataGridCellText" TargetType="DataGridCell">
    <!--#region Cell -->
    <Setter Property="Margin" Value="5, 5"/>
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="MinWidth" Value="40"/>
    <!--#endregion-->
</Style>

DataContext is set as:

private void InitialiseDataContext()
{
    CollectionViewSource propertyViewSource = ((CollectionViewSource)(FindResource("propertyViewSource")));
    CollectionViewSource tenantViewSource = ((CollectionViewSource)(FindResource("tenantViewSource")));

    _context.Properties.Load();
    _context.Tenants.Load();

    propertyViewSource.Source = _context.Properties.Local;
    tenantViewSource.Source = _context.Tenants.Local;
}

DataGrid is made editable by this code:

dgEditable = (Style)FindResource("EditableDataGridStyle");
dgReadonly = (Style)FindResource("ReadOnlyDataGridStyle");

private void buttonMakePropertiesEditable_Click(object sender, RoutedEventArgs e)
{
    propertyDataGrid.Style = propertyDataGrid.Style == dgEditable ? dgReadonly : dgEditable;
    foreach (var item in propertyDataGrid.Columns)
    {
        if (item.Header.ToString() == "New Picture")
        {
            item.Visibility = item.Visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}

Solution

  • Your code seems reasonable, even if you didn't show some things, like to what the Image control is binding to.

    Anyway i found out that there is an issue with the SelectionUnit = Cell, look here.

    The solution can be one of that or you can change your code to only use SelectionUnit = FullRow.

    I don't know why you need Cell as SelectionUnit but if the main concern was to edit a single cell, you can still do it in FullRow.

    This way you wouldn't have to do some fancy code to workaround this issue.

    I hope I was helpful.