ValueConverter在导航期间多次调用

时间:2014-08-25 16:45:10

标签: c# windows-phone-8 mvvm mvvm-light

在与Binding一起使用的转换器中有一些我无法理解的东西。 : - (

我用Mvvm-Light创建了一个简单的例子......

这里是完整的解决方案:https://testbindingwithconverter.codeplex.com/SourceControl/latest

这里有一张图片:http://i.stack.imgur.com/wUf89.png

以及摘要源代码:

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);
            }
        }
    }
}

转换器

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;
    }

在CarsView中,当我点击ListBox的第5项时,属性Opacity of Rectangle设置为值0.3,否则设置为值1.

现在,当我回到HomeView然后再回到CarsView时,如果我点击项目之间转换器被称为2次! 再次,如果我回到HomeView并返回到CarsView,转换器被称为3次!等等...

为什么?

2 个答案:

答案 0 :(得分:1)

嘿Vincenzo Petronio,

我不会为每次导航创建一个新的CarsViewModel(),因为它不会将颜色保存在Rectangle上,而是在你离开时想要取消绑定DataContent。

CarsView.xaml / CarsView.xaml.cs 上创建一个卸载的事件:

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

然后你需要检查你的Car Set函数的空值,这样它就不会把你的选择改为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);
       }
    }
}


如果您应用更改,您将看到它将按您希望的方式运行,另外,当您导航回Cars页面时,它将保存该Rectangle的颜色。

答案 1 :(得分:0)

从您描述的内容中可以清楚地看到&#34; CarsView&#34;实例正在泄漏 - 当您在应用程序中导航回来时,它们不会被丢弃。这导致它们像幻影一样徘徊,仍然绑定到视图模型,因此无论什么时候触发转换调用&#34; SelectedCar&#34;得到更新。

我想知道你的视图模型定位器的设置是否有点错误(我不会使用这种模式,所以我不知道)。理论上,XAML运行时使用弱事件侦听器进行绑定,即使视图模型仍然保持绑定,也允许处理视图。这可能不是这种情况,因为绑定应用于页面中的StaticResource ...

我会尝试这个:使用瞬态范围进行视图模型实例化。这可以防止IOC容器保持对视图模型的引用,从而使其成为GC&#39; d:

    public CarsViewModel CarsVM
    {
        get
        {
            return new CarsViewModel();
        }
    }

我意识到这会打破你的IOC模式,但如果这解决了问题,那么至少你会知道最近的原因。

修改

要尝试的另一件事:将定位器放在视图模型的资源中,而不是放在应用程序资源中。这可能会鼓励视图被处理,因为静态资源引用将由页面拥有。

<phone:PhoneApplicationPage.Resources>
    <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</phone:PhoneApplicationPage.Resources>

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource Locator},Path=CarsVM}">