Lisa Maria
Lisa Maria

Reputation: 27

ViewModel always returns null when trying to access it from code-behind

I am still learning Xamarin.Forms, so please bear with me.

I am developing a control, that let's you take pictures with your camera, displays them in a FlowListView and then ultimately I want to take these pictures, turn them into E-Mail attachments and send them off.

While the Gallery part of the control is working and the E-Mail part was already written by somebody else, for some reason I can't access the control's ViewModel from my E-Mail Code-Behind.

I wrote the control in MVVM, the E-Mail part is written in xaml with code-behind.

Whenever I try to access the ImagePickerViewModel to get the contents of my AttachmentList, I get returned null.

I have already tried not initializing the ImagePickerViewModel in the E-Mail code-behind, it still returns null.

What would be the correct way to do this?

ImagePickerViewModel.cs

 [XamlCompilation(XamlCompilationOptions.Compile)]
    public class ImagePickerViewModel : BindableObject, INotifyPropertyChanged
    {
        // HERE WOULD BE A TON OF DEPENDENCIES

        public ImagePickerViewModel()

        {
            // Commands
            TakePictureCmd = new Command(TakePicture);

            //ObservableCollection
            TakenPicturesList = new ObservableCollection<Model.ImagePicker>();
            AttachmentList = new List<BaseObject>();   
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public Model.ImagePicker NewPicture { get; set; }

        public BaseObject attachment { get; set; }
        public List<BaseObject> AttachmentList { get; set; }

        public ObservableCollection<Model.ImagePicker> TakenPicturesList { get; set; }

        public string selectedImage { get; set; }
        public string Path
        {
            get { return NewPicture.ImagePath; }
            set { NewPicture.ImagePath = value; OnPropertyChanged(nameof(Path)); }
        }

        public Command TakePictureCmd { get; set; }


        private async void TakePicture()
        {
            var path = await _cameraService.TakePictureAsync();
            TakenPicturesList.Add(new Model.ImagePicker { ImagePath = path });
            GetFilesForAttachment(path);
        }


        private void GetFilesForAttachement(string filePath)
        {
            attachment = Attachment.FromFile(filePath);
            if (attachment != null)
            {
                AttachmentList.Add(attachment);
            }
        }

        public void RaisedOnPropertyChanged(string _PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_PropertyName));
        }
    }

ImagePickerView.xaml

<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
             xmlns:flv="clr-namespace:DLToolkit.Forms.Controls;assembly=DLToolkit.Forms.Controls.FlowListView"
             xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
             xmlns:local="clr-namespace:SolIT.Mobile.Client" 
             xmlns:vm="clr-namespace:SolIT.Mobile.Client.Features.Shared.Controls.ImagePicker.ViewModel"
             xmlns:buttons="clr-namespace:Syncfusion.XForms.Buttons;assembly=Syncfusion.Buttons.XForms"
             x:Class="SolIT.Mobile.Client.Features.Shared.Controls.ImagePicker.View.ImagePickerView"
             x:Name="View_ImagePickerView">
    <ContentView.BindingContext>
        <vm:ImagePickerViewModel ContextView="{x:Reference View_ImagePickerView}"/>
    </ContentView.BindingContext>

    <Frame>
        <ContentView.Content>
            <StackLayout>

                <Grid 
          ColumnSpacing="0"
          HorizontalOptions="CenterAndExpand">

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                    </Grid.RowDefinitions>

                    <buttons:SfButton
            x:Name="Btn_takePicture"
            Grid.Column="1"
            Text="{local:Translate Common.SelectFilesPage.TakePicture}"
            TextColor="Black"
            ImageSource="{Binding TakePictureIcon}"
            ShowIcon="true"
            BackgroundColor="Transparent"
            ImageAlignment="Top"
            HorizontalOptions="FillAndExpand"
            Margin="3"
            CornerRadius="5"
            Command="{Binding TakePictureCmd}"/>
                </Grid>

                <BoxView Grid.ColumnSpan="2" Grid.Row="1" HeightRequest="1" BackgroundColor="{StaticResource Win10Devider}"/>

                <flv:FlowListView x:Name="pictureFlowListView" SeparatorVisibility="None" HasUnevenRows="true"
            FlowColumnMinWidth="110" FlowItemsSource="{Binding TakenPicturesList}">
                                  <!--FlowItemTappedCommand="{Binding PictureTappedCmd}"
                                  FlowLastTappedItem="{Binding SelectedItem}"-->
                    <flv:FlowListView.FlowColumnTemplate>
                        <DataTemplate> 
                            <Grid Padding="3">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <StackLayout>
                                <ffimageloading:CachedImage HeightRequest="100" Aspect="AspectFill"
                            DownsampleHeight="100" DownsampleUseDipUnits="false" x:Name="imageCached"
                            Source="{Binding ImagePath}">
                                    <ffimageloading:CachedImage.GestureRecognizers>
                                        <TapGestureRecognizer Command="{Binding Path=BindingContext.PictureTappedCmd, Source={x:Reference pictureFlowListView}}" CommandParameter="{x:Reference imageCached}"></TapGestureRecognizer>
                                    </ffimageloading:CachedImage.GestureRecognizers>
                                </ffimageloading:CachedImage>
                                </StackLayout>
                            </Grid>
                        </DataTemplate>
                    </flv:FlowListView.FlowColumnTemplate>
                </flv:FlowListView>
            </StackLayout>
        </ContentView.Content>
    </Frame>
</ContentView>

EmailCreatePage.xaml.cs

 [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class EmailCreatePage: ContentPage
    {
        // HERE WOULD BE A TON OF DEPENDENCIES

        public EmailCreateViewModel ViewModel { get; }
        public ImagePickerViewModel ViewModelImagePicker {get;}

        public EmailCreatePage()
        {
            InitializeComponent();
            BindingContext = ViewModel = new EmailCreateViewModel();
            ViewModelImagePicker = new ImagePickerViewModel();
        }
private void CreateAttachment()
        {
 
                var attachmentList = GetFileList();
                foreach (var file in attachmentList)
                {
                    var attachment = Attachment.FromFile(file.ToString());
                    if (attachment != null)
                    {

                        ViewModel.CubesMessage.ConnectedObjects.Add(attachment);
                    }
            }

public List<BaseObject> GetFileList()
        {
// GET LIST FROM OTHER VIEWMODEL HERE
// VIEWMODEL IS ALWAYS NULL
          var attachmentList = ImagePickerViewModel.AttachmentList;
          return attachmentList;
}

// HERE WOULD BE THE CODE THAT SENDS THE EMAIL OFF AFTER A BUTTON CLICK

Upvotes: 0

Views: 456

Answers (1)

Jessie Zhang -MSFT
Jessie Zhang -MSFT

Reputation: 13949

From the following code you posted , we find that you want to access variable AttachmentList from ImagePickerViewModel. But here, you just create a new instance of class ImagePickerViewModel, so the attachmentList will always be null.

   ViewModelImagePicker = new ImagePickerViewModel();

Besides, you also use code ImagePickerViewModel.AttachmentList; to access variable AttachmentList in ImagePickerViewModel, that's confusing. Here, you can remove code ViewModelImagePicker = new ImagePickerViewModel();.

public List<BaseObject> GetFileList()
        {
// GET LIST FROM OTHER VIEWMODEL HERE
// VIEWMODEL IS ALWAYS NULL
          var attachmentList = ImagePickerViewModel.AttachmentList;
          return attachmentList;
}

In fact, if you want to get the value of variable AttachmentList in class ImagePickerViewModel from other page, you can define a global static varible AttachmentList.

Just as follows:

   public stactic List<BaseObject> AttachmentList { get; set; }

Note: Make sure AttachmentList in class ImagePickerViewModel could been assigned values correctly.

Upvotes: 2

Related Questions