.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
4,104 questions
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
In have my viewModel
using System.Collections.ObjectModel;
namespace FireChat.ViewModels;
public partial class AppShellViewModel : BaseViewModel {
readonly FirebaseAuthClient _authClient;
readonly WeakReferenceMessenger _messenger;
readonly IMediaPicker _mediaPicker;
[ObservableProperty]
LocalUser localUser = new();
[ObservableProperty]
string? _selectedItem;
public ObservableCollection<string>? OptionsColletion { get; set; }
public AppShellViewModel(FirebaseAuthClient authClient, WeakReferenceMessenger messenger, IMediaPicker mediaPicker) {
_authClient = authClient;
if(_authClient.User != null) {
localUser.Username = _authClient.User.Info.DisplayName;
localUser.Email = _authClient.User.Info.Email;
}
_messenger = messenger;
_mediaPicker = mediaPicker;
OptionsColletion = GetOptions();
}
[RelayCommand]
async Task SignOut() {
_authClient.SignOut();
await Shell.Current.GoToAsync("..");
}
[RelayCommand]
void OpenProfile() {
_messenger.Send("OpenProfile");
}
[RelayCommand]
void SavePopUpContent() {
_messenger.Send("SavePopUpContent");
}
[RelayCommand]
void SelectedOption() {
Action action = SelectedItem switch {
"Take picture" => async () => await TakePicture(),
"Remove picture" => RemovePicture,
"Change picture" => async () => await ChangePicture(),
_ => () => throw new InvalidOperationException("Invalid selection.")
};
// Execute the selected action
action();
}
private async Task ChangePicture() {
if(LocalUser.ImagePath != null) {
var NewImagePath = await _mediaPicker.PickPhotoAsync(new MediaPickerOptions() {
Title = "Select a new profile picture"
});
if(NewImagePath != null) {
LocalUser.ImagePath = NewImagePath.FullPath;
}
}
}
private void RemovePicture() {
LocalUser.ImagePath = string.Empty;
}
private async Task TakePicture() {
if(_mediaPicker.IsCaptureSupported) {
var photo = await _mediaPicker.CapturePhotoAsync();
if(photo != null) {
string newFileName = $"{LocalUser.Username}-{DateTime.Today.ToString("yyyy-MM-dd")}.jpg";
string localFilePath = Path.Combine(FileSystem.AppDataDirectory, newFileName);
using Stream sourceStream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath);
await sourceStream.CopyToAsync(localFileStream);
Debug.WriteLine($"Image saved to: {localFilePath}");
LocalUser.ImagePath = new Uri(localFilePath).AbsoluteUri;
}
}
else {
Debug.WriteLine("Camera not supported");
}
}
public ObservableCollection<string> GetOptions() {
OptionsColletion = ["Take picture", "Remove picture", "Change picture"];
return OptionsColletion;
}
}
and my view
<Shell
x:Class="FireChat.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:FireChat.Controls"
xmlns:icons="clr-namespace:FireChat.Icons"
xmlns:model="clr-namespace:FireChat.Model"
xmlns:sfPopup="clr-namespace:Syncfusion.Maui.Popup;assembly=Syncfusion.Maui.Popup"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:views="clr-namespace:FireChat.Views"
xmlns:vm="clr-namespace:FireChat.ViewModels"
x:Name="AppShellFireChatPage"
x:DataType="vm:AppShellViewModel"
FlyoutBehavior="{OnIdiom Desktop=Locked,
Phone=Disabled}"
Shell.FlyoutWidth="60">
<ShellContent
ContentTemplate="{DataTemplate views:LoginPage}"
FlyoutItemIsVisible="False"
Route="LoginPage"
Shell.FlyoutBehavior="Disabled" />
<ShellContent
ContentTemplate="{DataTemplate views:ChatPage}"
Route="ChatPage">
<ShellContent.Icon>
<FontImageSource
FontFamily="MaterialSymbol"
Glyph="{Static icons:MateriallFontGlyphs.Chat}"
Color="White" />
</ShellContent.Icon>
</ShellContent>
<Shell.FlyoutFooter>
<Grid
Padding="10"
BackgroundColor="Transparent"
HeightRequest="48">
<Image x:Name="UserProfileImage">
<Image.Source>
<FontImageSource
FontFamily="MaterialSymbol"
Glyph="{Static icons:MateriallFontGlyphs.Account_circle}"
Color="White" />
</Image.Source>
<Image.GestureRecognizers>
<TapGestureRecognizer
x:DataType="vm:AppShellViewModel"
Command="{Binding OpenProfileCommand}" />
</Image.GestureRecognizers>
</Image>
<sfPopup:SfPopup
x:Name="AvatarPopUp"
AbsoluteX="-20"
AbsoluteY="-20"
AutoSizeMode="Height"
RelativePosition="AlignTop"
ShowFooter="False"
ShowHeader="False"
ShowOverlayAlways="False"
WidthRequest="150">
<sfPopup:SfPopup.PopupStyle>
<sfPopup:PopupStyle
CornerRadius="8"
PopupBackground="{AppThemeBinding Dark={DynamicResource PrimaryDarkText},
Light={DynamicResource White}}" />
</sfPopup:SfPopup.PopupStyle>
<sfPopup:SfPopup.ContentTemplate>
<DataTemplate>
<Border StrokeThickness="2">
<CollectionView
x:Name="OptionCollectionView"
Margin="10"
ItemsSource="{Binding OptionsColletion}"
SelectedItem="{Binding SelectedItem}"
SelectionChangedCommand="{Binding SelectedOptionCommand}"
SelectionMode="Single" />
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
</Border>
</DataTemplate>
</sfPopup:SfPopup.ContentTemplate>
</sfPopup:SfPopup>
<sfPopup:SfPopup
x:Name="UserProfilePopup"
AbsoluteX="-30"
AbsoluteY="30"
AcceptCommand="{Binding SavePopUpContentCommand}"
HeaderHeight="90"
HeightRequest="400"
RelativePosition="AlignTopRight"
RelativeView="{x:Reference UserProfileImage}"
ShowFooter="True"
ShowHeader="True"
ShowOverlayAlways="False"
WidthRequest="400">
<sfPopup:SfPopup.HeaderTemplate>
<DataTemplate x:DataType="model:LocalUser">
<toolkit:AvatarView
Margin="20,10,0,0"
BorderColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource PrimaryDarkText}}"
BorderWidth="1"
CornerRadius="80"
FontFamily="MaterialSymbol"
FontSize="20"
HeightRequest="80"
HorizontalOptions="Start"
ImageSource="{Binding ImagePath}"
Text="{Static icons:MateriallFontGlyphs.Add_a_photo}"
TextColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource PrimaryDarkText}}"
WidthRequest="80">
<toolkit:AvatarView.GestureRecognizers>
<PointerGestureRecognizer
x:Name="AvatarImage"
PointerEntered="AvatarImage_PointerEntered"
PointerExited="AvatarImage_PointerExited" />
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped" />
</toolkit:AvatarView.GestureRecognizers>
</toolkit:AvatarView>
</DataTemplate>
</sfPopup:SfPopup.HeaderTemplate>
<sfPopup:SfPopup.PopupStyle>
<sfPopup:PopupStyle
CornerRadius="0"
PopupBackground="{AppThemeBinding Dark={DynamicResource PrimaryDarkText},
Light={DynamicResource White}}" />
</sfPopup:SfPopup.PopupStyle>
<sfPopup:SfPopup.ContentTemplate>
<DataTemplate>
<Grid
Padding="20"
HorizontalOptions="Center"
RowDefinitions="80,80"
RowSpacing="5"
VerticalOptions="Center">
<controls:MaterialEntry
Hint="Username"
HintColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource Black}}"
HorizontalOptions="Start"
ShowIcon="False"
Text="{Binding LocalUser.Username}"
TextColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource Black}}" />
<controls:MaterialEntry
Grid.Row="1"
Hint="Status"
HintColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource Black}}"
HorizontalOptions="Start"
ShowIcon="False"
Text="{Binding LocalUser.StatusMessage}"
TextColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource Black}}" />
</Grid>
</DataTemplate>
</sfPopup:SfPopup.ContentTemplate>
<sfPopup:SfPopup.FooterTemplate>
<DataTemplate>
<Grid>
<Button
Margin="20"
BackgroundColor="{AppThemeBinding Dark={DynamicResource Gray900},
Light={DynamicResource White}}"
HorizontalOptions="End"
Text="Save"
TextColor="{AppThemeBinding Dark={DynamicResource White},
Light={DynamicResource PrimaryDarkText}}"
VerticalOptions="Center"
WidthRequest="200">
<Button.Behaviors>
<toolkit:TouchBehavior HoveredBackgroundColor="{AppThemeBinding Dark={DynamicResource FireOrange}, Light={DynamicResource White}}" />
</Button.Behaviors>
</Button>
</Grid>
</DataTemplate>
</sfPopup:SfPopup.FooterTemplate>
</sfPopup:SfPopup>
</Grid>
</Shell.FlyoutFooter> </Shell>
I am focusing on widows first
So, this is only accessible on a desktop.
This works as follows: You click on the avatar icon (This is where the error comes), and I display a collection of options
The strange part is, that this is working fine, but I want to get rid of the error
this is the "error"
`Severity Code Count Data Context Binding Path Target Target Type Description File Line Project
Error Binding 1 null Mismatch between the specified x:DataType (FireChat.Model.LocalUser) and the current binding context (FireChat.ViewModels.AppShellViewModel).
But I can click on the Avatar View So I don’t understand
for reference, this is my class
[FirestoreData]
public partial class LocalUser : ObservableObject {
private string _id;
private string _username;
private string _email;
private string _password;
private string _confirmPassword;
private string _imagePath;
private string _statusMessage = "Hey there! I'm using FireChat!";
private bool _onlineStatus = true;
[FirestoreProperty]
public string Id {
get => _id;
set {
_id = value;
OnPropertyChanged(nameof(Id));
}
}
[FirestoreProperty]
public string Username {
get => _username;
set {
_username = value;
OnPropertyChanged(nameof(Username));
}
}
[FirestoreProperty]
public string Email {
get => _email;
set {
_email = value;
OnPropertyChanged(nameof(Email));
}
}
[FirestoreProperty]
public string Password {
get => _password;
set {
_password = value;
OnPropertyChanged(nameof(Password));
}
}
[FirestoreProperty]
public string ConfirmPassword {
get => _confirmPassword;
set {
_confirmPassword = value;
OnPropertyChanged(nameof(ConfirmPassword));
}
}
[FirestoreProperty]
public string ImagePath {
get => _imagePath;
set {
_imagePath = value;
OnPropertyChanged(nameof(ImagePath));
}
}
[FirestoreProperty]
public string StatusMessage {
get => _statusMessage;
set {
_statusMessage = value;
OnPropertyChanged(nameof(StatusMessage));
}
}
[FirestoreProperty]
public bool OnlineStatus {
get => _onlineStatus;
set {
_onlineStatus = value;
OnPropertyChanged(nameof(OnlineStatus));
}
}
}
}