ImageGallery 不会从其他 class 更新

ImageGallery won't update from other class

所以我试图从服务器以字节为单位下载图像,但图像不显示。我得到一个正确的字节数组并重新调整它。它可以从 imagegallerypagemodel 添加图片,但从 IssuePageModel 添加它们时不起作用。所以我怀疑 class 被错误地实例化或者 notifyproperty 不起作用。我试图只添加相关部分,但可以添加任何遗漏的内容。

合集

 public class ImageGalleryPageModel
    {
        public ObservableCollection<ImageModel> Images
        {
            get { return images; }
        }

    private ObservableCollection<ImageModel> images = new ObservableCollection<ImageModel>();

这可以添加来自此 class

的图片
 private async Task ExecutePickCommand()
        {
            MediaFile file = await CrossMedia.Current.PickPhotoAsync();

            if (file == null)
                return;

            byte[] imageAsBytes;
            using (MemoryStream memoryStream = new MemoryStream())
            {
                file.GetStream().CopyTo(memoryStream);
                file.Dispose();
                imageAsBytes = memoryStream.ToArray();
            }

            if (imageAsBytes.Length > 0)
            {
                IImageResizer resizer = DependencyService.Get<IImageResizer>();
                imageAsBytes = resizer.ResizeImage(imageAsBytes, 1080, 1080);

                ImageSource imageSource = ImageSource.FromStream(() => new MemoryStream(imageAsBytes));
                Images.Add(new ImageModel { Source = imageSource, OrgImage = imageAsBytes });
            }
        }

然后我第二次加载 issuepagemodel,这里是 imagegallerypagemodel 的实例

public class IssuePageModel : FreshBasePageModel, INotifyPropertyChanged
{


 public ImageGalleryPageModel ImageGalleryViewModel { get; set; } = new ImageGalleryPageModel();

然后我把图片下载下来放到Collection里面,notify属性被触发了,debug的时候可以看到get上了,但是set的部分看不到。

 private void AddTheImages(int imageIssueId)
        {
            var imageData = App.Client.GetImage(imageIssueId);

            byte[] imageAsBytes = imageData.Item1;

            if (imageAsBytes.Length > 0)
            {
                IImageResizer resizer = DependencyService.Get<IImageResizer>();
                imageAsBytes = resizer.ResizeImage(imageAsBytes, 1080, 1080);

                ImageSource imageSource = ImageSource.FromStream(() => new MemoryStream(imageAsBytes));
                ImageGalleryViewModel.Images.Add(new ImageModel { Source = imageSource, OrgImage = imageAsBytes });
            }
        }

整个Xaml

  <freshMvvm:FreshBaseContentPage NavigationPage.HasNavigationBar="false" xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:freshMvvm="clr-namespace:FreshMvvm;assembly=FreshMvvm"
         xmlns:converters="clr-namespace:ASFT.Converters;assembly=ASFT"
         xmlns:maps="clr-namespace:ASFT.PageModels;assembly=ASFT"
         xmlns:controls="clr-namespace:ASFT.Controls;assembly=ASFT"
         x:Class="ASFT.Pages.IssuePage" Padding="4,25,4,4" x:Name="IssuePages">
<ContentPage.Resources>
    <ResourceDictionary>
        <Style x:Key="Labelfont" TargetType="Label">
            <Setter Property="FontSize" Value="Medium" />
        </Style>
        <converters:DateTextConverter x:Key="DateToTextConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

<Grid BackgroundColor="Black">
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition Height="*" />
        <RowDefinition Height="50" />
    </Grid.RowDefinitions>

    <!--Top Navigation Bar RETURN TO EVENTS-->
    <Grid Grid.Row="0" RowSpacing="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="20" />
        </Grid.ColumnDefinitions>
        <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="#FBB040">
            <Label Text="    &lt;   EVENTS" TextColor="White" FontSize="Large" HorizontalOptions="StartAndExpand"
                   VerticalOptions="CenterAndExpand">
                <Label.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding OnGoToListCommand}" NumberOfTapsRequired="1" />
                </Label.GestureRecognizers>
            </Label>
            <Label Text="{Binding LocationText}" TextColor="Black"/>
        </StackLayout>
    </Grid>


    <ScrollView Grid.Column="0" Grid.Row="1" Orientation="Vertical" VerticalOptions="FillAndExpand">
        <StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Spacing="15" Padding="0,30,0,0">

            <!--Category-->
            <Label Text="CATEGORY"  HorizontalTextAlignment="Start" VerticalTextAlignment="Center"
                   Style="{StaticResource Labelfont}" TextColor="White" />
            <Frame OutlineColor="#FBB040" Padding="2" HeightRequest="40">
                <StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="FillAndExpand"
                             Spacing="6" HeightRequest="40">
                    <Entry Text="{Binding TitleEx}" TextColor="White" VerticalOptions="Center"
                           HorizontalOptions="FillAndExpand" Style="{StaticResource Labelfont}" HeightRequest="40"
                           BackgroundColor="Black" />
                </StackLayout>
            </Frame>

            <!--Title-->
            <Label Text="TITLE" HorizontalTextAlignment="Start" VerticalTextAlignment="Center"
                   Style="{StaticResource Labelfont}" TextColor="White" />
            <Frame OutlineColor="#FBB040" Padding="2" HeightRequest="40">
                <StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="FillAndExpand"
                             Spacing="6" HeightRequest="40">
                    <Entry Text="{Binding TitleEx}" TextColor="White" VerticalOptions="Center"
                           HorizontalOptions="FillAndExpand" Style="{StaticResource Labelfont}" HeightRequest="40"
                           BackgroundColor="Black" />
                </StackLayout>
            </Frame>

            <!--PictureGallery-->
            <Label Text="IMAGES" HorizontalTextAlignment="Start" VerticalTextAlignment="Center"
                   Style="{StaticResource Labelfont}" TextColor="White" />
            <Grid BindingContext="{Binding ImageGalleryViewModel}">

                <Grid.RowDefinitions>
                    <RowDefinition Height="128" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <controls:ImageGalleryControl Grid.Row="0" ItemsSource="{Binding Images}">
                    <controls:ImageGalleryControl.ItemTemplate>
                        <DataTemplate>
                            <Image Source="{Binding Source}" Aspect="AspectFit">
                                <Image.GestureRecognizers>
                                    <TapGestureRecognizer
                                        Command="{Binding Path=BindingContext.PreviewImageCommand, Source={x:Reference IssuePages}}"
                                        CommandParameter="{Binding ImageId}" />
                                </Image.GestureRecognizers>
                            </Image>
                        </DataTemplate>
                    </controls:ImageGalleryControl.ItemTemplate>
                </controls:ImageGalleryControl>
                <Grid Grid.Row="1">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Button Grid.Column="0" Text="Add photo" Command="{Binding CameraCommand}" />
                    <Button Grid.Column="1" Text="Pick photo" Command="{Binding PickCommand}" />
                </Grid>
            </Grid>
            <Label Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="3" Text="{Binding ImageText}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="White" />

            <!--Description-->
            <Label Text="DESCRIPTION" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Style="{StaticResource Labelfont}" TextColor="White" />
            <Frame Padding="2" OutlineColor="#FBB040">
                <Editor Text="{Binding DescriptionEx}" HeightRequest="100" BackgroundColor="Black" TextColor="White" />
            </Frame>

            <!--Grid for Status and Severity-->
            <Grid HorizontalOptions="Center">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>

                <!--Severity-->
                <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Text="SEVERITY" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" Style="{StaticResource Labelfont}" TextColor="White" />
                <Label Grid.Row="0" Grid.Column="1" Text="" VerticalTextAlignment="Center"
                       HorizontalOptions="StartAndExpand" Style="{StaticResource Labelfont}"  />

                <StackLayout Grid.Row="1" Grid.Column="0" Orientation="Horizontal" HorizontalOptions="Center">
                    <Image Source="severity_5.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="{Binding Severity5Opacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}"  CommandParameter="severity_5.png" />
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="1" Grid.Column="1">
                    <Image Source="severity_4.png" HorizontalOptions="Center"
                           VerticalOptions="Center" HeightRequest="40" Opacity="{Binding Severity4Opacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="severity_4.png"  />
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="1" Grid.Column="2">
                    <Image Source="severity_3.png" HorizontalOptions="Center"
                           VerticalOptions="Center" HeightRequest="40" Opacity="{Binding Severity3Opacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="severity_3.png"  />
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="1" Grid.Column="3">
                    <Image Source="severity_2.png" HorizontalOptions="Center"
                           VerticalOptions="Center" HeightRequest="40" Opacity="{Binding Severity2Opacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="severity_2.png" />
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="1" Grid.Column="4">
                    <Image Source="severity_1.png" HorizontalOptions="Center"
                           VerticalOptions="Center" HeightRequest="40" Opacity="{Binding Severity1Opacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="severity_1.png" />
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>


                <!--Status-->
                <Label Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Text="STATUS" HorizontalTextAlignment="Start" VerticalTextAlignment="Center" TextColor="White" Style="{StaticResource Labelfont}" />
                <Label Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="2" FontSize="Micro" Text="STATUS" HorizontalTextAlignment="End" VerticalTextAlignment="Center" TextColor="White"
                       Style="{StaticResource Labelfont}" IsVisible="True" />
                <StackLayout Grid.Row="3" Grid.Column="1" Orientation="Horizontal" Spacing="0">
                    <Image Source="statusUnresolved.png" HorizontalOptions="Center"
                           VerticalOptions="Center" HeightRequest="40" Opacity="{Binding StatusUnresolvedOpacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="statusUnresolved.png"/>
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="3" Grid.Column="2" Orientation="Horizontal" Spacing="4">
                    <Image Source="statusInProgress.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="{Binding StatusInProgressOpacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="statusInProgress.png"/>
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>

                <StackLayout Grid.Row="3" Grid.Column="3" Orientation="Horizontal" Spacing="4">
                    <Image Source="statusDone.png" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="40" Opacity="{Binding StatusDoneOpacity}">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding OnStatusClickedCommand}" CommandParameter="statusDone.png"/>
                        </Image.GestureRecognizers>
                    </Image>
                </StackLayout>
            </Grid>

            <!--Date Created-->
            <Label Text="TIME AND DATE: " Style="{StaticResource Labelfont}" TextColor="White" />
            <Frame OutlineColor="#FBB040" BackgroundColor="Black" Padding="2">
                <StackLayout Orientation="Horizontal" VerticalOptions="Center"
                             HorizontalOptions="FillAndExpand" Spacing="0" Margin="0" Padding="0"
                             BackgroundColor="Black" HeightRequest="40">
                    <Label Text=" " Style="{StaticResource Labelfont}" TextColor="White" BackgroundColor="Black"
                           Margin="1" />
                    <Label Text="{Binding CreatedEx, Converter={StaticResource DateToTextConverter}}"
                           Style="{StaticResource Labelfont}" TextColor="White" BackgroundColor="Black"
                           VerticalOptions="Center" />
                </StackLayout>
            </Frame>

            <!--REPORTED BY-->
            <Label Text="REPORTED BY: " Style="{StaticResource Labelfont}" TextColor="White" />
            <Frame OutlineColor="#FBB040" BackgroundColor="Black" Padding="2">
                <StackLayout Orientation="Horizontal" VerticalOptions="Center"
                             HorizontalOptions="FillAndExpand" Spacing="0" Padding="1" BackgroundColor="Black"
                             HeightRequest="40">
                    <Label Text=" " Style="{StaticResource Labelfont}" TextColor="White" BackgroundColor="Black" />
                    <Label Text="{Binding CreatedByEx}" Style="{StaticResource Labelfont}"
                           TextColor="White" BackgroundColor="Black" VerticalOptions="Center" />
                </StackLayout>
            </Frame>

            <!--Map View-->
            <Grid>
                <Grid.BindingContext>
                    <maps:TkMapPageModel/>
                </Grid.BindingContext>
                <Grid.RowDefinitions>
                    <RowDefinition Height="30" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="40" />
                    <RowDefinition Height="50" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="20" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="20" />
                </Grid.ColumnDefinitions>

                <Label Grid.Column="1" Grid.Row="0" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"
                       Text="Tap and hold on map to set/move pin" Font="Large" />
                <StackLayout Grid.Column="1" Grid.Row="1" VerticalOptions="FillAndExpand">
                    <RelativeLayout x:Name="MapRelativeLayout" VerticalOptions="FillAndExpand" HeightRequest="920" WidthRequest="300" />
                </StackLayout>
                <StackLayout Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="3" Orientation="Vertical" Spacing="0">
                    <Label Text="{Binding MapText}" VerticalOptions="Center" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="White" />
                </StackLayout>
            </Grid>

            <Button Text="Submit"
                    HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand" Command="{Binding SubmitCommand}"
                    BackgroundColor="#FBB040" />
        </StackLayout>
    </ScrollView>

    <!--Bottom Navigation Bar-->
    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="20" />
        </Grid.ColumnDefinitions>

        <StackLayout Grid.ColumnSpan="3" Grid.Row="1" Orientation="Horizontal"
                     HorizontalOptions="FillAndExpand" Spacing="5" BackgroundColor="White">
            <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="#FBB040">
                <Image Source="photo2.png" WidthRequest="100" HeightRequest="100" HorizontalOptions="FillAndExpand"
                       VerticalOptions="Center">
                </Image>
            </StackLayout>

            <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="#FBB040">
                <Image Source="photo2.png" WidthRequest="100" HeightRequest="100" HorizontalOptions="FillAndExpand"
                       VerticalOptions="Center">
                </Image>
            </StackLayout>

            <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="#FBB040">
                <Image Source="showmap.png" WidthRequest="100" HeightRequest="60" HorizontalOptions="FillAndExpand"
                       VerticalOptions="CenterAndExpand">
                </Image>
            </StackLayout>

        </StackLayout>
    </Grid>
</Grid>

正如我重复的那样,它在从 imagegallerypagemodel 添加而不是从其他 class IssuePageModel

添加时有效
public class ImageGalleryControl : ScrollView
    {

    public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create<ImageGalleryControl, IList>(
            view => view.ItemsSource,
            default(IList),
            BindingMode.TwoWay,
            propertyChanging: (bindableObject, oldValue, newValue) => {
                ((ImageGalleryControl)bindableObject).ItemsSourceChanging();
            },
            propertyChanged: (bindableObject, oldValue, newValue) => {
                ((ImageGalleryControl)bindableObject).ItemsSourceChanged(bindableObject, oldValue, newValue);
            }
        );

    public IList ItemsSource
    {
        get
        {
            return (IList)GetValue(ItemsSourceProperty);
        }
        set
        {
            SetValue(ItemsSourceProperty, value);
        }
    }

Images 在你的模型中是一个 ObservableCollection ,它很像一个列表。当您添加图像时,您不会点击 set 因为您没有更改集合,而是添加到现有集合中。这是正确的方法... ObservableCollection 包含 INotifyCollectionChanged 接口,这意味着有一个 CollectionChanged 事件你可以订阅并监听变化。在任何项目源中使用 XAML 绑定时,它应该会自动为您侦听和处理此事件,但在后面的代码中或您自己代码中的任何地方,您很可能需要这样做。

请记住,集合引用也可能发生变化,因此您也需要对其进行处理。下面是如何正确处理模型之间 ObservableCollection 的示例。

public class ExampleModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private ObservableCollection<string> names = new ObservableCollection<string>();

    public ObservableCollection<string> Names
    {
        get => names;
        set
        {
            names = value;

            //Only called if I change the collection reference i.e. make a new ObservableCollection or assign it to another exising reference.
            //Not called adding or removing items from existing collection.
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Names)));
        }
    }
}

.....

public class ExampleViewModel : INotifyPropertyChanged
{
    private readonly ExampleModel ExampleModel;
    public event PropertyChangedEventHandler PropertyChanged;
    private ObservableCollection<string> names = new ObservableCollection<string>();

    public ExampleViewModel()
    {
        ExampleModel = new ExampleModel();
        ExampleModel.PropertyChanged += ExampleModel_PropertyChanged;
        Names = ExampleModel.Names;
        if (Names != null) Names.CollectionChanged += Names_CollectionChanged;
    }

    private void ExampleModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(ExampleModel.Names):
                //Here we reassign the entire collection if it changes.
                if (Names != null) Names.CollectionChanged -= Names_CollectionChanged;
                Names = ExampleModel.Names;
                if (Names != null) Names.CollectionChanged += Names_CollectionChanged;
                break;
        }            
    }

    private void Names_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        foreach (var item in e.OldItems) Names.Remove((string)item);
        foreach (var item in e.NewItems) Names.Add((string)item);
    }

    public ObservableCollection<string> Names
    {
        get => names;
        set
        {
            names = value;

            //Only called if I change the collection reference i.e. make a new ObservableCollection or assign it to another exising reference.
            //Not called adding or removing items from existing collection.
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Names)));
        }
    }

}

注意:这个例子只是为了让你更清楚地了解它是如何工作的,可能不是实现这两者的最简洁的方法。如果知道它们都是 ObservableCollections 那么那很好,但至少我们应该假设它们是 IEnumerable 并且处理方式略有不同。无论哪种方式......但是你需要在你的代码中手动处理它并且 XAML 应该在绑定到 ItemsSource 时为你处理它。