Search code examples
c#windows-phone-8mvvmmvvm-light

ValueConverter called multiple times during navigation


There's something i can't understand in the Converter used with Binding. :-(

I created a simple example with Mvvm-Light ...

here the full solution: https://testbindingwithconverter.codeplex.com/SourceControl/latest

here an image: https://i.sstatic.net/wUf89.png

and below the summary source code:

CarsView

<Rectangle Grid.Row="0" 
               Opacity="{Binding SelectedCar, Converter={StaticResource IntToOpacityConverter}}" 
               Fill="#FFD1E22A"  />
    <ListBox Grid.Row="1" 
             ItemsSource="{Binding Cars}" 
             SelectedItem="{Binding SelectedCar, Mode=TwoWay}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Company}" />
                        <TextBlock Text="{Binding Name}" />
                        <TextBlock Text="{Binding Year}" />
                    </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

CarsViewModel

public class CarsViewModel : ViewModelBase
{
    private ObservableCollection<Car> cars;
    private Car selectedCar;

    public CarsViewModel()
    {
        if (IsInDesignMode)
        {
        }
        else
        {
            // INIT
            this.cars = new ObservableCollection<Car>();
            this.selectedCar = null;

            // FAKE DATA
            for (int i = 1; i <= 10; i++)
            {
                Car newCar = new Car { Id = i, Company = "Company_" + i, Name = "Name_" + i, Year = "200" + i };
                Cars.Add(newCar);
            }
        }
    }

    public ObservableCollection<Car> Cars
    {
        get 
        {
            return cars; 
        }

        set 
        {
            if (cars != value)
            {
                cars = value;
                RaisePropertyChanged(() => Cars);
            }
        }
    }

    public Car SelectedCar
    {
        get
        {
            return selectedCar;
        }

        set
        {
            if (value != selectedCar)
            {
                selectedCar = value;
                RaisePropertyChanged(() => SelectedCar);
            }
        }
    }
}

Converter

public class IntToOpacityConverter : System.Windows.Data.IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double opacity = 1;
        Car c = value as Car;

        if (c != null)
        {
            if (c.Id == 5)
            {
                opacity = 0.3;
            }
        }

        System.Diagnostics.Debug.WriteLine("[IntToOpacityConverter] \t" + "Rectangle Opacity: " + opacity);
        return opacity;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        // One-Way conversion!
        return null;
    }

From the CarsView, when i tap on item 5 of the ListBox, the property Opacity of Rectangle is set to the value 0.3, otherwise to the value 1.

Now, when i go back to the HomeView and then again to the CarsView, if i tap between items the converter is called 2 times! And again, if i go back to the HomeView and return to the CarsView, the converter is called 3 times! And so on...

Why?


Solution

  • Hey Vincenzo Petronio,

    I would not create a new CarsViewModel() per navigate because it won't save your colors on the Rectangle, instead you want to unbind the DataContent when you navigate away like so.

    On CarsView.xaml / CarsView.xaml.cs create a Unloaded Event:

    <phone:PhoneApplicationPage  Unloaded="PhoneApplicationPage_Unloaded">
    
    private void PhoneApplicationPage_Unloaded(object sender, RoutedEventArgs e)
    {
        this.DataContext = null;
    }
    

    Then you need to check for nulls on your Car Set function so it will not change your selection to null.

    public Car SelectedCar
    {
        get
        {
            System.Diagnostics.Debug.WriteLine("[CARSVIEWMMODEL] \t" + "GET SelectedCar");
            return selectedCar;
        }
    
        set
        {
            if (value != selectedCar && value != null)
            {
                selectedCar = value;
                System.Diagnostics.Debug.WriteLine("[CARSVIEWMMODEL] \t" + "SET SelectedCar");
                RaisePropertyChanged(() => SelectedCar);
           }
        }
    }
    


    If you apply the changes you will see it will act the way you want to, plus it will saved the color of that Rectangle when you navigate back to the Cars page.