【问题标题】:ValueConverter called multiple times during navigation导航期间多次调用 ValueConverter
【发布时间】:2014-10-18 21:05:51
【问题描述】:

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

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

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

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

及下方源代码摘要:

汽车视图

<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 项时,Rectangle 的属性 Opacity 设置为值 0.3,否则设置为值 1。

现在,当我返回 HomeView 然后再次返回 CarsView 时,如果我在项目之间点击转换器被调用 2 次​​strong>! 再一次,如果我返回 HomeView 并返回 CarsView,转换器被调用 3 次!等等……

为什么?

【问题讨论】:

  • 显然“CarsView”正在泄漏——实例没有按应有的方式处理,因此它们作为幻影保留在那里,其绑定附加到视图模型实例。

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


【解决方案1】:

嘿文森佐·佩特罗尼奥,

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

CarsView.xaml / CarsView.xaml.cs上创建一个Unloaded事件:

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

然后您需要检查 Car Set 函数上的空值,这样它就不会将您的选择更改为空值。

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


如果您应用更改,您将看到它会按照您想要的方式运行,而且当您导航回“汽车”页面时,它会保存该矩形的颜色。

【讨论】:

  • 非常好!我什至没有想到这一点。
【解决方案2】:

从您的描述中可以清楚地看出,“CarsView”实例正在泄漏——当您在应用程序中导航回来时,它们并没有被释放。这导致它们像幻影一样徘徊,仍然绑定到视图模型,因此每当“SelectedCar”更新时触发转换调用。

我想知道您的视图模型定位器设置是否存在某种错误(我不使用这种模式,所以我不知道)。理论上,XAML 运行时使用弱事件侦听器进行绑定,即使视图模型仍持有绑定,也允许释放视图。此处可能并非如此,因为绑定应用于页面中的 StaticResource ...

我会试试这个:为视图模型实例化使用瞬态范围。这可以防止 IOC 容器持有对视图模型的引用,从而允许它被 GC'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}">

【讨论】:

  • 如果我理解正确,我可以得到与return ServiceLocator.Current.GetInstance&lt;CarsViewModel&gt;(Guid.NewGuid().ToString()); 相同的结果,但我总是得到一个新的 VM 实例。但是,可以是一个解决方案。谢谢;-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
  • 2013-03-17
  • 2022-10-08
  • 1970-01-01
  • 1970-01-01
  • 2017-12-02
相关资源
最近更新 更多