0

I am very new to the data binding concept so excuse my question if it appears silly to you. This project is to learn some API functionality and WPF Data Binding along with it. But here is what problem I face when I try to show an icon on screen by setting Data Context of an Image element in WPF to property from a Class that stores the Path of the image used for displaying on screen. I have the following Image:

<Image x:Name="imgWeatherIcon" Grid.Column="2" HorizontalAlignment="Left" Margin="380,134,0,0" VerticalAlignment="Top"
           Width="90" Height="90" Source="{Binding ImgPath, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, diag:PresentationTraceSources.TraceLevel=High}">
        <Image.DataContext>
            <c:ConditionsNow/>
        </Image.DataContext>
    </Image>

I bind the Source to a property so whenever this property changes I get the new icon/image displaying on screen. The problem is that this only works if DataContext is set on start of the program (Main Window) and I need it to be se inside a button click

Here is the code that works:

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        ApiHelper.InitializeApiClient();

        ConditionsNow conditionsObject = new();
        imgWeatherIcon.DataContext = conditionsObject;
        conditionsObject.ImgPath = @"https://developer.accuweather.com/sites/default/files/01-s.png";

    }

Note that 'imgWeatherIcon' is a Label where I display the icon/image and I am setting it's DataContext to the property 'ImgPath'. This is just to clarify the "imgWeatherIcon" since didn't include WPF code for it, but I can do if necessary.

If I do this every time I start up the application the datacontext is set properly to the ImgPath property and is updated in the UI.

But here is where I need it to work and can't figure out why doesn't it work:

    public ConditionsNow condObject = new();

    private async void BtnCity_Click(object sender, RoutedEventArgs e)
    {
        imgWeatherIcon.DataContext = condObject;
        condObject.ImgPath = @"G:\Visual Studio\Projects\Testing\AccuWeatherApp\accuweather-icon.jpg";
                    
    }

I know I lack knowledge of databinding and WPF, but would appreciate if someone give me a hint where the problem is. I did a lot of research about binding etc. but still can't figure this out. Btw if this is helpful for property 'ImgPath' I have INotifyPropertyChanged implemented to be able to track changes.

Bst
  • 11
  • 1
  • I understand what you mean and I tried this, but then does that mean I set the DataContext in Main Window (on start up), then just change the property "ImgPath"? If yes how would that work, since I am binding the data to object created from class ConditionsNow and I can't change the property of that instantiated object inside the button click. And this is where I have to change the "ImgPath" property because in the button click is the place where this happens. If the data is bound to object inside Main Window how can I change it inside the btn click? – Bst Apr 02 '21 at 07:53
  • Can you give me an example how would this work if I want to change the "ImgPath" property inside the button click? And the datacontext has already been set in the startup of the program. Because that is what I am trying to achieve, when clicking this button the data is taken from web source and "ImgPath" has to be changed to the proper image, but don't know how to change it without making an instance of the class that contains the property. And I can't use the one in Main window since it is totally different instance. – Bst Apr 02 '21 at 07:57
  • Resetting the DataContext has no effect because you are always setting it to the same value, i.e. `condObject`. That is not actually a *change* (and it is a bad idea anyway). Resetting the property to the same image path would also not work, because by default WPF caches images by the URI from which they were loaded (assuming that the Click handler is meant to *reload* a new image from the same file path). Besides that, it is pointless to set UpdateSourceTrigger=PropertyChanged on a OneWay Binding. – Clemens Apr 02 '21 at 08:50
  • I was assuming that I bind the Image source to my "ImgPath" property in my class, then create an instance of that class and assign it to the datacontext so whenever I change the "ImgPath" property it takes the new value and reflects in the Image source so basically if I give it new image path (to a new image) it will show the new image. The whole idea of "ImgPath" was that I set the value of that property to the respective image I want to show and all images are taken from web page. And it makes sense that I shouldn't set the datacontext to the same value. – Bst Apr 02 '21 at 09:22
  • Then take a look at the answer... – Clemens Apr 02 '21 at 09:23
  • But the only thing I wonder at the moment is , how do I change the "ImgPath" property without creating and instance of that class and setting value to that property. How can I tell my interface where to take the new image path from (in my case it is in the ImgPath property)? – Bst Apr 02 '21 at 09:24
  • Keep a reference to the object that you have assigned to the DataContext. Set the property of exactly that object. – Clemens Apr 02 '21 at 09:25
  • Thanks for the answer, I was actually thinking the same and that's what I tried to do. I will re-check what I did wrong when trying to use the object that I assigned to the DataContext. – Bst Apr 02 '21 at 09:32

1 Answers1

0

You should not change the whole DataContext binding. You set the DataContext only once, and then only change the underlying properties that are bound to the UI. In your case, you should only change the ImgPath property. Therefore, remove the following from the button click event:

imgWeatherIcon.DataContext = condObject;

Make sure that ConditionsNow implement the INotifyPropertyChanged interface, so that you can call OnPropertyChanged() when changing the value of ImgPath. This is to ensure that the binding will cause the UI to change its presentation.

So your ConditionsNow class should have a form similar to the below:

public class ConditionsNow
{    
    private string _imgPath;
    public string ImgPath
    {
        get => _imgPath;
        set
        {
            _imgPath = value;
            OnPropertyChanged();
        }
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
AmmarArnt
  • 114
  • 4