虚拟化不改变新可见项目的属性
Virtualisation not changing properties of new visible items
我目前在 listView 中使用自定义图像对象(<Image>
对象的包装器)。当新的列表视图项目可见(实现)时,我的自定义图像对象的属性不会改变。
例如,如果我的列表视图(包含 30 个具有不同图像 URL 和不同文本的项目)在第一个滚动条上有 3 个项目,那么第 10 个项目与第一个项目具有相同的图像。图像按 [1-9][1-9][1-9] 的顺序重复......但令我惊讶的是,所有 30 个 listViewItems 中的文本都不同。
在调试时,我发现我的图像对象的 setter 仅针对前 9 个项目被调用。有人可以阐明其他系统组件(系统 Image/TextBlock 工作正常)如何获得新的元素值吗?
相关代码段 class 属性:
public sealed partial class CustomImage : UserControl
{
public static readonly DependencyProperty ImageSourceStringProperty = DependencyProperty.Register("ImageSourceString", typeof(string), typeof(CustomImage), new PropertyMetadata(null, new PropertyChangedCallback(ImageSourceStringChanged)));
public string ImageSourceString
{
get { return (string)GetValue(ImageSourceStringProperty); }
set
{
//THIS NEVER GETS HIT FOR ITEMS AFTER 9th ITEM
SetValue(ImageSourceStringProperty, value);
//{More code here}
}
}
}
Xaml Usage
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<custom:customImage x:Name="Img" ImageSourceString="{Binding ImgPath}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
我错过了它应该如何工作吗?如果有不清楚的地方请告诉我,我可以澄清。
依赖属性的getter
和setter
不保证是运行,我们最好不要在[=13=中放任何其他代码].请注意 Custom dependency properties 中的以下 注意事项:
In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue operations. Otherwise, you'll get different behavior when your property is set via XAML versus when it is set via code. For efficiency, the XAML parser bypasses wrappers when setting dependency properties; whenever possible, it uses the registry of dependency properties.
因此,我们可以使用 ImageSourceStringChanged
方法对新值进行操作,而不是在 属性 的 setter
中做出反应,如下所示:
private static void ImageSourceStringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
CustomImage currentImage = obj as CustomImage;
string oldValue = e.OldValue as string;
string newValue = e.NewValue as string;
MainPage.logMsg("ImageSource = " + newValue);
if (oldValue == null || !oldValue.Equals(newValue))
{
string path = newValue;
if (string.IsNullOrEmpty(path))
{
Uri imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
else
{
Uri imageFileUri = null;
try
{
imageFileUri = new Uri(path);
}
catch
{
imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
}
if (imageFileUri != null)
{
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
}
}
}
另外,因为你的 DependencyProperty
的类型是 string
,你不需要比较 OldValue
和 NewValue
因为只有当价值改变了。参见 Property changed behavior for structures and enumerations。
If the type of a DependencyProperty is an enumeration or a structure, the may be invoked even if the internal values of the structure or the enumeration value did not change. This is different from a system primitive such as a string where it only is invoked if the value changed.
所以CustomImage
的完整代码可能是这样的:
public sealed partial class CustomImage : UserControl
{
public static readonly DependencyProperty ImageSourceStringProperty = DependencyProperty.Register("ImageSourceString", typeof(string), typeof(CustomImage), new PropertyMetadata(null, new PropertyChangedCallback(ImageSourceStringChanged)));
public string ImageSourceString
{
get { return (string)GetValue(ImageSourceStringProperty); }
set
{
SetValue(ImageSourceStringProperty, value);
}
}
private static void ImageSourceStringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
CustomImage currentImage = obj as CustomImage;
string path = e.NewValue as string;
MainPage.logMsg("ImageSource = " + path);
if (string.IsNullOrEmpty(path))
{
Uri imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
else
{
Uri imageFileUri = null;
try
{
imageFileUri = new Uri(path);
}
catch
{
imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
}
if (imageFileUri != null)
{
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
}
}
public CustomImage()
{
this.InitializeComponent();
}
}
我目前在 listView 中使用自定义图像对象(<Image>
对象的包装器)。当新的列表视图项目可见(实现)时,我的自定义图像对象的属性不会改变。
例如,如果我的列表视图(包含 30 个具有不同图像 URL 和不同文本的项目)在第一个滚动条上有 3 个项目,那么第 10 个项目与第一个项目具有相同的图像。图像按 [1-9][1-9][1-9] 的顺序重复......但令我惊讶的是,所有 30 个 listViewItems 中的文本都不同。
在调试时,我发现我的图像对象的 setter 仅针对前 9 个项目被调用。有人可以阐明其他系统组件(系统 Image/TextBlock 工作正常)如何获得新的元素值吗?
相关代码段 class 属性:
public sealed partial class CustomImage : UserControl
{
public static readonly DependencyProperty ImageSourceStringProperty = DependencyProperty.Register("ImageSourceString", typeof(string), typeof(CustomImage), new PropertyMetadata(null, new PropertyChangedCallback(ImageSourceStringChanged)));
public string ImageSourceString
{
get { return (string)GetValue(ImageSourceStringProperty); }
set
{
//THIS NEVER GETS HIT FOR ITEMS AFTER 9th ITEM
SetValue(ImageSourceStringProperty, value);
//{More code here}
}
}
}
Xaml Usage
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<custom:customImage x:Name="Img" ImageSourceString="{Binding ImgPath}"/>
<TextBlock Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
我错过了它应该如何工作吗?如果有不清楚的地方请告诉我,我可以澄清。
依赖属性的getter
和setter
不保证是运行,我们最好不要在[=13=中放任何其他代码].请注意 Custom dependency properties 中的以下 注意事项:
In all but exceptional circumstances, your wrapper implementations should perform only the GetValue and SetValue operations. Otherwise, you'll get different behavior when your property is set via XAML versus when it is set via code. For efficiency, the XAML parser bypasses wrappers when setting dependency properties; whenever possible, it uses the registry of dependency properties.
因此,我们可以使用 ImageSourceStringChanged
方法对新值进行操作,而不是在 属性 的 setter
中做出反应,如下所示:
private static void ImageSourceStringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
CustomImage currentImage = obj as CustomImage;
string oldValue = e.OldValue as string;
string newValue = e.NewValue as string;
MainPage.logMsg("ImageSource = " + newValue);
if (oldValue == null || !oldValue.Equals(newValue))
{
string path = newValue;
if (string.IsNullOrEmpty(path))
{
Uri imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
else
{
Uri imageFileUri = null;
try
{
imageFileUri = new Uri(path);
}
catch
{
imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
}
if (imageFileUri != null)
{
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
}
}
}
另外,因为你的 DependencyProperty
的类型是 string
,你不需要比较 OldValue
和 NewValue
因为只有当价值改变了。参见 Property changed behavior for structures and enumerations。
If the type of a DependencyProperty is an enumeration or a structure, the may be invoked even if the internal values of the structure or the enumeration value did not change. This is different from a system primitive such as a string where it only is invoked if the value changed.
所以CustomImage
的完整代码可能是这样的:
public sealed partial class CustomImage : UserControl
{
public static readonly DependencyProperty ImageSourceStringProperty = DependencyProperty.Register("ImageSourceString", typeof(string), typeof(CustomImage), new PropertyMetadata(null, new PropertyChangedCallback(ImageSourceStringChanged)));
public string ImageSourceString
{
get { return (string)GetValue(ImageSourceStringProperty); }
set
{
SetValue(ImageSourceStringProperty, value);
}
}
private static void ImageSourceStringChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
CustomImage currentImage = obj as CustomImage;
string path = e.NewValue as string;
MainPage.logMsg("ImageSource = " + path);
if (string.IsNullOrEmpty(path))
{
Uri imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
else
{
Uri imageFileUri = null;
try
{
imageFileUri = new Uri(path);
}
catch
{
imageFileUri = new Uri("ms-appx:///Assets/Images/failed.png");
}
if (imageFileUri != null)
{
currentImage.mainImage.ImageSource = new BitmapImage(imageFileUri);
}
}
}
public CustomImage()
{
this.InitializeComponent();
}
}