Search code examples
c#wpfmvvmbindingimagesource

C# WPF MVVM Image Source Updating


I am trying to update the image source in xml from the view model but it seems that I cannot do it and I don't understand what I am missing.

So... In the view xml I have a image for which I made a Binding for a string that is in my model.

<Image x:Name="Pick1Image"
       Grid.Row="7"
       Grid.Column="0"
       Grid.ColumnSpan="6"
       Source="{Binding ImagePath}"
       Style="{StaticResource Image_Viewer_Pick}"/>

Then, in my viewmodel I made this part in which I check the value of a variable and assign the path string to the binding variable accordingly.

public string SelectedPart
{
    get { return _selectedPart; }
    set
    {
        _selectedPart = value;

        if (_selectedPart == "SX")
        {
            _imagesource = "/Images/main_view.png";
        }

        if (_selectedPart == "DX")
        {
            _imagesource = "/Images/second_view.png";
        }

        if (_selectedPart == "TX")
        {
            _imagesource = "/Images/third_view.png";
        }
    }
}

And of course I have the binding variable declaration with the OnPropertyChanged() calling.

public string ImagePath
{
    get { return _imagesource; }
    set { _imagesource = value; OnPropertyChanged(); }
}

If I use a Messagebox.Show(ImagePath) the variable changes ok, but the image remains the same. What could be the problem?

Thank you!

Update 1:

I tried Michalis approach but without luck. So what I did was this:

In ViewModel:

public BitmapImage bitmap;

public BitmapImage Image
{
    get { return bitmap; }
    set
    {
        bitmap = value;
        OnPropertyChanged();
    }
}

public string SelectedPart
{
    get { return _selectedPart; }
    set
    {
        _selectedPart = value;

        if (_selectedPart == "SX")
        {
            bitmap = new BitmapImage();
            bitmap.BeginInit();
            bitmap.UriSource = new Uri(@"\Images\main_view.png", UriKind.Relative);
            bitmap.EndInit();
        }
    }
}

And in XAML I changed the Image Source Binding to Image.

Update 2

This is the Image_Viewer_Pick control style:

<Style x:Key="Image_Viewer_Pick" TargetType="{x:Type Image}">
    <Setter Property="HorizontalAlignment" Value="Center"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="Stretch" Value="Uniform"/>
    <Setter Property="Width" Value="405"/>
    <Setter Property="Height" Value="300"/>
    <Setter Property="Margin" Value="0,0,0,0"/>
</Style>

Final Update - SOLVED

if(_selectedPart == "SX")
                {
                    _imagesource = "/Images/main_view.png";
                    ImagePath = _imagesource;
                }

It seems that Andy had a good point regarding the ImagePath assign rather than _imagesource. The code that is in this update seems that solved the problem. Not sure that I fully understand the problem but I will try to get it.

Thank you so much all of you! Thank you, Andy!


Solution

  • Here's a simplified example I knocked up.

    I'm using mvvm community toolkit here because it's slightly quicker to get this thing working using it.

    Mainwindow:

        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewmodel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="160"/>
        </Grid.ColumnDefinitions>
        <Image Source="{Binding PicPath}"/>
        <StackPanel Grid.Column="1">
            <TextBox Text="{Binding PicPath}"/>
            <Button Content="Take Focus"/>
        </StackPanel>
    </Grid>
    </Window>
    

    The entire purpose of that button is to give me something else in the view can take focus easily. So I can tab out the textbox and the binding transfers the text to viewmodel.

    MainWindowViewModel

    public partial class MainWindowViewmodel : ObservableObject
    {
        [ObservableProperty]
        private string picPath = "/Images/DSC00025.jpg";
    }
    }
    

    That generates a public property PicPath.

    I have two pictures in an Images folder. I have set the properties of these so the files are copied over next to the exe when I compile. This is one possibility.

    Another is you have two instances of your viewmodel. This is quite a common thing we see here on SO and of course one instance is bound. The other has the property change so no change is visible.

    Another possibility is you're relying on setting SelectedPart to change the picture.

    In which case, that should set

     ImagePath = "/Images/main_view.png";
    

    Which will raise property changed.

    Rather than

     _imagesource = "/Images/main_view.png";
    

    enter image description here

    I see the picture. If I overtype that 5 with 6 and tab then the text propogates to the viewmodel, the view is notified and reads the property. The second picture shows.

    When I look in the bin on disk after compiling, I see my picture files there:

    enter image description here

    I'd show you it running but I was very lazy grabbing those pictures off disk and they're of a child.

    If I change it to make the button change the picture:

    public partial class MainWindowViewmodel : ObservableObject
    {
        [ObservableProperty]
        private string picPath = "/Images/DSC00025.jpg";
        
        [RelayCommand]
        private async Task ChangePicture()
        {
            PicPath = "/Images/DSC00026.jpg";
        }
    }
    

    and the button

            <Button Content="Change Picture"
                    Command="{Binding ChangePictureCommand}"
                    />
    

    This also changes the picture successfully.