.NET MAUI QueryProperty: Data not passed to another page

Szepesi Szilvia Vanda 0 Reputation points
2024-12-01T12:13:55.06+00:00

I'm developing a drawing application using .NET MAUI and Firestore. On the HomePage, I display a list of existing drawings. When I select one, I want to navigate to the DrawPage and open the selected drawing for modification.

However, the DrawingID doesn't seem to pass correctly to the DrawPage. On the HomePage, I can see that the DrawingID is correctly identified, but when navigating, it doesn't reach the DrawPage.

Additionally, the DrawPage is also used for starting new drawings, so it initializes without requiring an ID when creating a new drawing.

Any ideas why the DrawingID isn't being passed, and how I can fix this? Thanks in advance! ( sorry some of the comment are in my language)

HomePgae.Xam.cs

public partial class HomePage : ContentPage
{
        public HomePage(HomeViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;

        // Betöltjük a rajzokat, amikor az oldal megjelenik
        this.Appearing += async (s, e) => await viewModel.LoadUserDrawingsAsync();
    }

    private async void OnDrawingSelected(object sender, SelectionChangedEventArgs e)
    {
        if (e.CurrentSelection.FirstOrDefault() is DrawingData selectedDrawing)
        {
            //Shell.Current.GoToAsync($"DrawPage?drawingId={selectedDrawing.Id}");
            await Shell.Current.GoToAsync($"///DrawPage", new Dictionary<string, object> { { "DrawingId", selectedDrawing.Id } });
            ((CollectionView)sender).SelectedItem = null;
        }
    }   
}

DrawingPage.Xaml.cs

[QueryProperty(nameof(DrawingId), "DrawingId")]
public partial class DrawPage : ContentPage
{
    private readonly DrawableCanvas _drawableCanvas;
    private readonly DrawViewModel _viewModel;
    public string DrawingId { get; set; }
    public DrawPage(DrawViewModel viewModel, FirestoreService firestoreService, FirebaseAuthClient firebaseClient)
    {
        InitializeComponent();

        _drawableCanvas = new DrawableCanvas();
        _viewModel = new DrawViewModel(_drawableCanvas, firestoreService, firebaseClient);
        BindingContext = _viewModel;

        DrawingCanvas.Drawable = _drawableCanvas;
      
        DrawingCanvas.StartInteraction += OnInteractionStart;
        DrawingCanvas.DragInteraction += OnInteractionDrag;
    }
    protected override async void OnAppearing()
    {
        base.OnAppearing();

        if (!string.IsNullOrEmpty(DrawingId))
        {
            Console.WriteLine($"DrawingId: {DrawingId}"); // Debug
            await _viewModel.LoadDrawingAsync(DrawingId);
        }
        else
        {
            Console.WriteLine("DrawingId is null or empty!");
        }
    }
    
    private void OnInteractionStart(object sender, TouchEventArgs e)
    {
        if (e.Touches.Any())
        {
            _drawableCanvas.StartNewLine(_viewModel.CurrentColor, _viewModel.LineThickness, _viewModel.Opacity);
            var touch = e.Touches.First();
            _drawableCanvas.AddPoint(new PointF((float)touch.X, (float)touch.Y));
            DrawingCanvas.Invalidate();
        }
    }

    private void OnInteractionDrag(object sender, TouchEventArgs e)
    {
        if (e.Touches.Any())
        {
            var touch = e.Touches.First();
            _drawableCanvas.AddPoint(new PointF((float)touch.X, (float)touch.Y));
            DrawingCanvas.Invalidate();
        }
    }

DrawingViewModel.cs the Load method:

public async Task LoadDrawingAsync(string drawingId)
 {
     try
     {
         var userId = _authClient.User.Uid;
         var lines = await _firestoreService.LoadDrawingAsync(userId, drawingId);

         if (lines != null)
         {
             _drawableCanvas.Clear(); // Meglévő rajz törlése
             foreach (var line in lines)
             {
                 var points = line.Points.Select(p => new PointF(p["X"], p["Y"])).ToList();
                 _drawableCanvas.AddLine(points, Color.FromHex(line.Color), line.Thickness, line.Opacity);
             }
         }
     }
     catch (Exception ex)
     {
         await Application.Current.MainPage.DisplayAlert("Hiba", $"Rajz betöltése nem sikerült: {ex.Message}", "OK");
     }
 }

FireStoreSrvice.cs Load method

  public async Task<List<LineData>> LoadDrawingAsync(string userId, string drawingId)
       {
           var drawingDoc = _firestoreDb.Collection("users")
                                       .Document(userId)
                                       .Collection("drawings")
                                       .Document(drawingId);

           var snapshot = await drawingDoc.GetSnapshotAsync();

           if (!snapshot.Exists) return null;

           var lines = snapshot.GetValue<List<LineData>>("Lines");
           return lines;
       }

.NET
.NET
Microsoft Technologies based on the .NET software framework.
4,007 questions
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,763 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,152 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
824 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 46,206 Reputation points Microsoft Vendor
    2024-12-02T02:49:03.58+00:00

    Hello,

    I noticed that you are passing an object to the page as a parameter. According to Maui's official documentation, in this case, you need to use the OnPropertyChanged method to notify the page of property changes.

    The class that represents the page being navigated to, or the class for the page's BindingContext, can be decorated with a QueryPropertyAttribute for each query parameter. For more information, see Process navigation data using query property attributes.

    
    [QueryProperty(nameof(Bear), "Bear")]
    
    public partial class BearDetailPage : ContentPage
    
    {
    
        Animal bear;
    
        public Animal Bear
    
        {
    
            get => bear;
    
            set
    
            {
    
                bear = value;
    
                OnPropertyChanged();
    
            }
    
        }
    
        public BearDetailPage()
    
        {
    
            InitializeComponent();
    
            BindingContext = this;
    
        }
    
    }
    
    

    According to the code you provided, you need to transfer a string ID to the page. In this case, using primitive navigation is more suitable for this task.

    Primitive data can be passed as string-based query parameters when performing URI-based programmatic navigation. This is achieved by appending ? after a route, followed by a query parameter id, =, and a value:

    async void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        string elephantName = (e.CurrentSelection.FirstOrDefault() as Animal).Name;
        await Shell.Current.GoToAsync($"elephantdetails?name={elephantName}");
    }
    

    In addition, Maui officially provides a runnable example for Shell parameter passing navigation. You can refer to the official example to fix your code.

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.