how to bind a click handler for a Button in cppwinrt - c++-winrt

according to this page https://learn.microsoft.com/en-us/windows/uwp/cpp-and-winrt-apis/binding-property
I'm writing the following code
/*cl /nologo /Yupch.h /bigobj /await /std:c++latest /EHsc /I. demo.cpp pch.obj /link /winmd /out:appx\App.exe /subsystem:console /appcontainer*/
struct MainPage : PageT<MainPage>
{
MainPage()
{
Application::LoadComponent(*this, Uri(L"ms-appx:///MainPage.xaml"));
}
void ClickHandler(Windows::Foundation::IInspectable const& /* sender */, Windows::UI::Xaml::RoutedEventArgs const& /* args */)
{
cout << __func__ << endl;
}
};
and the MainPage.xaml page is as follow
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Click="ClickHandler">click</Button>
</Page>
but it seems that the MainPage::ClickHandler never got called , so , what is the right way ? many thanks!

Your MainPage.xaml is missing an x:Class attribute on the root Page declaration:
<Page x:Class="MyNamespace.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Click="ClickHandler">click</Button>
</Page>
Without that attribute, the XAML platform doesn't know, where to look for the ClickHandler to bind to the Click event.

Related

In .NET MAUI is there a way to choose a different XAML view based upon whether the device is in Landscape or Portrait

I am using .NET MAUI and I have a particular view that is rather complicated and I would rather have a different layout if the device orientation is in Portrait vs if the device orientation is in landscape.
I tinkered around with Android programming a long time ago and for Android Studio there was a way to choose a XAML file when the device was in landscape and a different XAML file when the device was in portrait.
Is this possible with MAUI?
If not what is the best practice in regards to this?
Here is my layout and in landscape mode I can fit 3 major sections in one row but this won't work in portrait and in portrait I would like the middle major element to be on the next row.
Here are examples of my portrait vs landscape mockup I created on Photoshop:
UPDATE WITH SOLUTION***************
I'm attempting the solution that FreakyAli posted and have a mostly working prototype, so anyone who is wanting to use a different XAML layout based upon the screen orientation can use this approach.
I created a new folder called "ContentViews" in my solution.
I added 3 new ContentViews (the XAML with the code behind):
HomePageLandscape
HomePagePortrait
HomePageOrientationViewLoader
The HomePageOrientationViewLoader will get loaded directly into the HomePage.xaml file later on. This is the control that will load either the HomePagePortrait ContentView when in portrait mode or HomePageLandscape ContentView when in landscape mode.
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePageOrientationViewLoader : ContentView
{
public ContentView homePagePortraitContentView;
public ContentView homePageLandscapeContentView;
public HomePageOrientationViewLoader()
{
InitializeComponent();
homePagePortraitContentView = new HomePagePortrait();
homePageLandscapeContentView = new HomePageLandscape();
this.Content = homePageLandscapeContentView;
DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? homePagePortraitContentView : homePageLandscapeContentView;
}
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
{
// if (this.Content.GetType() is not typeof(HomePageLandscape))
// {
this.Content = homePageLandscapeContentView;
// }
}
else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
{
// if (this.Content.GetType() is not typeof(HomePagePortrait))
// {
this.Content = homePagePortraitContentView;
// }
}
else
{
//Whatever you would like to do if the orientation is unknown.
}
}
}
The HomePageOrientationViewLoader.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePageOrientationViewLoader">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
Here is the HomePagePortrait.xaml.cs file:
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePagePortrait : ContentView
{
public HomePagePortrait()
{
InitializeComponent();
}
}
Here is the HomePagePortrait.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePagePortrait">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI portrait"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
Here is the HomePageLandscape.xaml.cs file:
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePageLandscape : ContentView
{
public HomePageLandscape()
{
InitializeComponent();
}
}
Here is the HomePageLandscape.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ScoreKeepersBoard.ContentViews.HomePageLandscape">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI landscape"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>
My project had an initial home Content Page called HomePage. We are loading the HomePageOrientationViewLoader ContentView into the xaml of HomePage Content Page as a custom control. Note that I had to define the namespace that the ContentViews were located in and use that when defining the control in the xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
x:Class="ScoreKeepersBoard.Views.HomePage"
Title="HomePage">
<VerticalStackLayout>
<Label
Text="Welcome to .NET MAUI Home Page Content Page"
VerticalOptions="Center"
HorizontalOptions="Center" />
<controls:HomePageOrientationViewLoader></controls:HomePageOrientationViewLoader>
</VerticalStackLayout>
</ContentPage>
Here is the code behind for the home page
namespace ScoreKeepersBoard.Views;
public partial class HomePage : ContentPage
{
public HomePage(HomeViewModel homeViewModel)
{
InitializeComponent();
}
}
and when the project runs on my iphone simulator in portrait mode:
You will see the second label shown says "Welcome to .NET MAUI portrait" which is the view from the portrait content view and when I switch to landscape:
You will see the second label shown says "Welcome to .NET MAUI landscape" which is the view from the landscape content view.
ISSUES
This works on my iPhone simulator but when I switch to my Android pixel 5 simulator and toggle my switch phone orientation it doesn't work and putting in line breaks the code defined in HomePageOrientationViewLoader is not triggered. NEW NOTE: I tried this on a physical Android phone and it is working so it must have just been the emulator.
I will need to use this for a non trivial example that has a view model that will be holding data on a sports game score, timing, etc. I guess I will just need to inject a singleton of the view model into each and they will just share and if the orientation switches the other Content View will load and the view model will bind to the appropriate controls?
The initial suggested code by FreakyAli had this check:
if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
{
if (this.Content.GetType() is not typeof(HomePageLandscape))
{
this.Content = homePageLandscapeContentView;
}
}
but the part "typeof(HomePageLandscape) gives me an error and says a constant is expected.
Other than that the different views for different orientations is working and I thank FreakyAli mightily! I am sure I will figure out why the Android emulator is not triggering the orientation switch code, but suggestions would be awesome.
Ideally this is how i would handle such a scenario:
In my constructor, I would get the DisplayInfoChanged event which notifies me if this info changes and i would also assign my current ContentView based on the current Orientation:
DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? potraitView : landscapeView;
Here PortraitView is a ContentView that is the View I would display when my device is in Portrait and Vice Versa.
And then handle the runtime change of the orientation as follows:
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if(e.DisplayInfo.Orientation==DisplayOrientation.Landscape)
{
if(this.Content.GetType() is not typeof(LandscapeView))
{
this.Content = landscapeView;
}
}
else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
{
if (this.Content.GetType() is not typeof(PortraitView))
{
this.Content = portraitView;
}
}
else
{
//Whatever you would like to do if the orientation is unknown.
}
}
Hope this helps you!
The proper way to do this is through ContentViews where you have 2 ContentViews, one for portrait and one for landscape. You have another ContentView that is used to choose to load in either landscape or portrait depending on the orientation.
I created a tutorial that puts all of the pieces together:
https://codeshadowhand.com/net-maui-different-layouts-for-portrait-vs-landscape/
Thanks a million to FreakyAli for pointing me in the right direction!!!

How can I access a property from a ViewModel to a ButtonClickEvent which is inside the CodeBehind of Xaml View?

I want to access the properties created in ViewModel to the xaml code behind file. Please have a look at the attached screenshot for better understanding on my question.
Please Click Here to View the Screenshot of my Xaml code
Click Here for the Properties code
I have bind the "EntryText" property to an Entry field and "LblText" property to a Label. So, now I just want to transfer the value of Entry to the Label on a button click event.
You're on the right track, just need to search slightly differently.
There's multiple ways of doing this. I will tell you the simplest way since that's also suggested in the Xamarin Official Docs. So your Xaml code will look like this
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ButtonDemos.BasicButtonClickPage"
Title="Basic Button Click">
<StackLayout>
<Label x:Name="label"
Text="Click the Button below"
FontSize="Large"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center" />
<Button Text="Click to Rotate Text!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />
</StackLayout>
</ContentPage>
And your C# file would look like this
public partial class BasicButtonClickPage : ContentPage
{
public BasicButtonClickPage ()
{
InitializeComponent ();
}
async void OnButtonClicked(object sender, EventArgs args)
{
await label.RelRotateTo(360, 1000);
}
}
You can use code-behind to invoke a method in the view model. So in that method, you can change the LblText. Refer below code.
<Button x:Name="btn1" Clicked="btnClicked" />
In code-behide
private void btnClicked(object sender, EventArgs e){
_viewModel.ChangeLabelText();
}
In the View Model
public void ChangeLabelText() {
LblText = EntryText;
}
You can use the page's BindingContext and cast it to your model. And, then access the property from there.
var myModel = this.BindingContext as MainPageProperties;
if(myModel!=null)
{
//Access your property here!
var text = myModel.LblText;
}

I cannot call another screen when I click on the image

I have two images to simulate the click on the button, but I want is when clicking the image and change call another screen. And the process does not happen because the application is stopping
<StackLayout>
<!-- Place new controls here -->
<Image Source="botaocadastrolivre.png">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnTapGestureRecognizerTapped"
NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
void OnTapGestureRecognizerTapped(object sender, EventArgs args)
{
tapCount++;
var imageSender = (Image)sender;
// watch the monkey go from color to black&white!
if (tapCount % 2 == 0)
{
imageSender.Source = "botaocadastrolivre.png";
}
else
{
imageSender.Source = "botaocadastroPresed.png";
Navigation.PushAsync(new Nova());
}
// Task.Delay(100)
//Navigation.PushAsync(new Nova());
}
Go to your App.xaml.cs file, find the line that says something like:
MainPage = new MainPage();
and change it into:
MainPage = new NavigationPage(new MainPage());
This will wrap your page into a navigation page and then the Navigation object will know how to navigate from one page to the other.
It might be wise to read up on navigation concepts in Xamarin.Forms: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/

Xamarin: make Rg Plugins Popup in Xaml?

I am getting all sorts of weird errors trying to instantiate a popup page in Xaml. This is the most vexing:
No property, bindable property, or event found for 'Content', or mismatching type between value and property.
Here's the page presenting the popup:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns ="http://xamarin.com/schemas/2014/forms"
xmlns:x ="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class ="PopupPages.PopupBackingPage"
xmlns:popup ="clr-namespace:PopupPages"
BackgroundColor="Transparent">
<popup:PopupTest />
</ContentPage>
Here's its code-behind:
namespace PopupPages
{
public partial class PopupBackingPage : ContentPage
{
public PopupBackingPage()
{
InitializeComponent();
}
}
}
Here's the popup page itself, in xaml:
<?xml version="1.0" encoding="utf-8" ?>
<pages:PopupPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
x:Class="PopupPages.PopupTest">
</pages:PopupPage>
And here's the code-behind for the popup page itself:
namespace PopupPages
{
public partial class PopupTest : Rg.Plugins.Popup.Pages.PopupPage
{
public PopupTest()
{
InitializeComponent();
}
}
}
What's going on? What am I doing wrong? These are as dead simple as can be.
A Rg.Plugins.Popup is a page itself and can't be shown in a ContentPage. When wanting to display a popup page, display them using code like below:
await PopupNavigation.Instance.PushAsync(new PopupTest());
Put this code in the event that you were going to show the PopupBackingPage. A backing page is not needed and won't work with the Rg.Plugins.Popup pages.
Hopefully this helps!

Xamarin.Forms adding children to TabbedPage within OnBindingContextChanged

I'm trying to implement a TabbedPage using MvvmCross for my navigation. The problem is MvvmCross uses ViewModel first navigation and this doesn't seem to play well with the general approach one might take to add children to a TabbedPage; because I do not have access to a non-null ViewModel during page construction, but I do have access to it within OnBindingContextChanged.
Here's what I have so far...
DashboardPage.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DashboardPage"
xmlns:local="clr-namespace:CoreUI;assembly=CoreUI"
SelectedItem="{Binding CurrentSection, Mode=TwoWay}">
</TabbedPage>
DashboardPage.xaml.cs:
public partial class DashboardPage : TabbedPage
{
public DashboardPage()
{
InitializeComponent();
}
protected override void OnBindingContextChanged()
{
var vm = (BindingContext as DashboardViewModel);
if (vm == null)
{
return;
}
ObservableCollection<MainMenuSection> sections = vm.MenuSections;
foreach (var section in sections)
{
MainMenuViewModel main_menu_vm = new MainMenuViewModel
{
Section = section
};
// Question 2:
// Going against the MvvmCross grain here by referring to other pages from within a page, as opposed to doing everything from a ViewModel. How do I get around this?
Children.Add(new MainMenuPage(main_menu_vm));
}
}
}
MainMenuPage.xaml (pay attention to the comments here):
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MainMenuPage"
xmlns:local="clr-namespace:CoreUI;assembly=CoreUI"
Title="{Binding Title}" > <!-- The tabs that are displayed on Dashboard have the correct labels, so Binding appears to be working here. -->
<ContentPage.Content>
<StackLayout x:Name="Body" IsVisible="false">
<Label Text="{Binding Title}"/> <!-- Label doesn't get displayed, but does get displayed if Text is bound to something static, so Binding not quite working here. -->
</StackLayout>
</ContentPage.Content>
</ContentPage>
MainMenuPage.xaml.cs:
public partial class MainMenuPage : ContentPage
{
public MainMenuPage(MainMenuViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
protected override void OnBindingContextChanged()
{
Body.IsVisible = true;
}
}
The above MainMenuPage is a simplified version of what I have to illustrate my point, which is that I get a blank page for each tab within DashboardPage.
Question 1: Why are the tab pages blank?
Question 2: Refer to comment in DashboardPage.xaml.cs.
Why would you do this whole thing yourself? As you can see in the samples (https://github.com/MvvmCross/MvvmCross/tree/develop/TestProjects/Playground/Playground.Forms.UI/Pages) you can just decorate your view with a MvxTabbePagePresentation attribute and MvvmCross will handle the rest for you!
I would also advice to use the Mvx type pages to take advantage of lots of features.