I started working with .NET MAUI. I ran into a problem just by starting my development.
I want to show a popup and I'm using the Community Toolkit.
All I did is:
I created a new .NET MAUI Application Project, installed the Community Toolkit NuGet Package (of course also the .UseMauiCommunityToolkit in the start up class) and added a XAML File for the Popup:
<?xml version="1.0" encoding="utf-8" ?>
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="TestApp.ProfilePopup">
<VerticalStackLayout>
<Label Text="This is a very important message!" />
</VerticalStackLayout>
</toolkit:Popup>
I've no partial class for this popup
I just modified the button on MainPage to display the popup:
private void OnCounterClicked(object sender, EventArgs e)
{
var popup = new ProfilePopup();
this.ShowPopup(popup);
}
If I run this application and click on the button to display the popup I'll get the error message:
CommunityToolkit.Maui.Core.Handlers.PopupHandler found for TestApp.ProfilePopup is incompatible
If I create the popup in C#, it works:
private void OnCounterClicked(object sender, EventArgs e)
{
var popup = new Popup
{
Content = new VerticalStackLayout
{
Children =
{
new Label
{
Text = "This is a very important message!"
}
}
}
};
this.ShowPopup(popup);
}
Any idea what I'm doing wrong?
Thank you!
Markus
I reproduced the error message.
THE CAUSE: "I've no partial class for this popup".
That won't work. without that, there is no InitializeComponent call. The result is not a valid View.
To fix the problem,
First make sure you have got the toolkit registered in MauiProgram.cs:
using CommunityToolkit.Maui;
...
builder.UseMauiApp<App>().UseMauiCommunityToolkit();
then you must have
file ProfilePopup.xaml.cs containing:
public partial class ProfilePopup : CommunityToolkit.Maui.Views.Popup
{
public ProfilePopup()
{
InitializeComponent();
}
}
I generate custom views using these steps:
Project / Add New Item / .NET MAUI ContentView (XAML).
Give name "MyView". This adds TWO files to project: MyView.xaml and MyView.xaml.cs.
In MyView.xaml, add needed xmlns and change base class.
Was:
<ContentView xmlns=...
...
x:Class=...>
...
</ContentView>
change to:
<toolkit:Popup xmlns=
...
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class=...>
...
</toolkit:Popup>
In MyView.xaml.cs, change base class.
Was:
public partial class ProfilePopup : ContentView
change to:
public partial class ProfilePopup : CommunityToolkit.Maui.Views.Popup
The documentation on Microsoft page should be completed (I used as template):
https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/views/popup
I was having the same problem. I forgot to add .UseMauiCommunityToolkit(); to MauiProgram.cs.
You can try this, as well:
var popup = new PopupPage();
await PopupExtensions.ShowPopupAsync<PopupPage>(this, popup);
Related
Fairly new to UWP and MVVM I came across a problem which might seem obvious to many of you.
In my project I have 3 folders named Views, ViewModels and Models which include some files as seen in the image bellow:
Can't upload image yet (reputation):
http://i.imgur.com/42f5KeT.png
The problem:
I am trying to implement MVVM. I have searched hours for articles and videos but it seems I am always missing something. I have some bindings in the LoginPage.xaml which I then modify in a class inside Models/LoginPageModel.cs. I have an INotifyPropertyChanged class in my LoginPageViewModel.cs where every time a property changes in my LoginPageModel.cs I want the INotifyPropertyChanged class to trigger which will then change the property in the LoginPage.xaml View. Below I have the content of those files.
This is a sample of my LoginPageModel.cs code:
using System;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace App_Name.Models
{
class LoginPageModel
{
private NotifyChanges notify;
public async void LogIn()
{
if (something is true)
notify.LoginUIVisibility = Visibility.Visible;
}
}
}
This is my LoginPageViewModel.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml;
namespace App_Name.ViewModels
{
public class NotifyChanges : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Visibility loginUIVisibility = Visibility.Collapsed;
public Visibility LoginUIVisibility
{
get
{
return loginUIVisibility;
}
set
{
if (value != loginUIVisibility)
{
loginUIVisibility = value;
NotifyPropertyChanged("LoginUIVisibility");
}
}
}
}
}
Here is an example of LoginPage.xaml:
<Page
x:Class="App_Name.LoginPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App_Name"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:App_Name.ViewModels"
mc:Ignorable="d">
<Page.DataContext>
<vm:NotifyChanges/>
</Page.DataContext>
<StackPanel Visibility="{Binding LoginUIVisibility}">
Here is my LoginPage.xaml.cs:
namespace App_Name
{
public sealed partial class LoginPage : Page
{
private LoginPageModel login;
public LoginPage()
{
InitializeComponent();
login.LogIn();
}
}
}
I don't know why this is not working. Bindings used not to work, but now at runtime it gives me an unhandled exception and I think it has to do with not assigning any value to the private NotifyChanges notify and private LoginPageModel login objects, but I don't know what. Thanks everyone for your time in advance!
Please if you need clarifications for my question just write a comment. Thank you!
I am trying to implement MVVM.
And you're not getting it right yet. Forget about the Bindings for a moment, let's focus on the architecture.
Going down the acronym, you need
a Model. It supports your business logic and usually is defined by your backend (database). It should not depend on (be aware of) the Views or ViewModels. A lightweight UWP app could do without a Model layer.
a View. This is the XAML part that we like to keep as simple as possible, a.o. reasons because it's hardest to test.
a ViewModel. It's purpose is to serve the View. It should contain properties and commands the View can directly bind to. It does as much conversion and aggregation as possible to keep the View light. It usually relies on (0 or more) Models or Services.
Given this, it is not the case that you should always have 1 Model for 1 ViewModel. A ViewModel could use multiple Models, or none.
It is clear that your LoginPageModel.Login() is in the wrong place. Login() should be a method (Command) on your ViewModel.
Your story should go like this:
I want a LoginView
So I need to support it with a LoginViewModel, implementing INPC
The ViewModel probably needs to use a LoginService or a UserModel. But it would only need a Model instance after a successful login. A LoginModel doesn't sound right.
Have a look at Template10 to get started with View, ViewModel and a thread-safe BindableBase.
You could also look a the picture over here for a full (over the top maybe) layout of MVVM.
And here is the call for change in the main class:
NotifyChanges notifyChanges = new NotifyChanges();
notifyChanges.LoginUIVisibility = Visibility.Visible;
You have instantiated notifyChanges in the XAML file by adding <vm:NotifyChanges/>. And add a binding to StackPanel by using <StackPanel Visibility="{Binding LoginUIVisibility}">. But you created a new notifyChanges, and you did not bind the new notifyChanges to StackPanel. So it won't work. You could initialize viewModel just like following code.
MainPage
private LoginViewModel viewModel;
public MainPage()
{
this.InitializeComponent();
viewModel = this.DataContext as LoginViewModel;
}
private void showDetail_Click(object sender, RoutedEventArgs e)
{
viewModel.LoginUIVisibility = Visibility.Visible;
}
MainPage.xaml
<Page.DataContext>
<vm:LoginViewModel />
</Page.DataContext>
<StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button x:Name="loginButon" Content="Login" Visibility="{Binding LoginUIVisibility}" />
<Button x:Name="showDetail" Content="Show" Click="showDetail_Click" />
</StackPanel>
I create a file creation page by extending class WizardNewFileCreationPage. What I am trying to do is to hide the help button on the left bottom corner. Any suggestions on how to do this?
Thank you.
Call this method: Wizard#setHelpAvailable(false)
Refer API here
I called this method but not working. See the following:
public class NewTDLDiagram extends Wizard implements INewWizard {
private NewDiagramFilePage page;
public NewTDLDiagram() {
// TODO Auto-generated constructor stub
setHelpAvailable(false);
}
...
}
This class is registered as an extension point of org.eclipse.ui.newWizards:
<extension
point="org.eclipse.ui.newWizards">
<wizard
class="com.abc.graphicview.ui.NewDiagram"
icon="icons/NewSmdWizard.gif"
id="com.abc.graphicview.ui.diagrawizard"
name="Diagram"
project="false">
<selection
class="org.eclipse.core.resources.IResource">
</selection>
</wizard>
</extension>
Scenario:
MainWindow has a Menu About which relates to AboutWindow.
About Meny is triggered by command:
<MenuItem Header="_About" Command="{Binding OpenAbout}"/>
OpenAbout is property like that:
private RelayCommand _openAbout;
public RelayCommand OpenAbout
{
get
{
return _openAbout ?? (_openAbout = new RelayCommand(() => Messenger.Default.Send(new NotificationMessage("ShowAboutView"))));
}
}
Notification message is registered in App.cs class as follows:
static App()
{
DispatcherHelper.Initialize();
}
public App()
{
RegisterMessenger();
}
public void RegisterMessenger()
{
Messenger.Default.Register<NotificationMessage>(this, ProcessShowAboutView);
}
private void ProcessShowAboutView(NotificationMessage message)
{
AboutWindow view = new AboutWindow();
view.Show();
}
I analysed another questions like that:
How to open a new window using MVVM Light Toolkit
WPF MVVM - How to Show a view from MainWindowViewModel upon Clicking on button
I like Messenger functionality but however I am not sure If above solution is a good one.
I would be thankful for any advise!
As depicted above, Registering messages is done in App Config.
I consider it not be a good place therefore I need to know what place would be better.
Another place to consider would be Locator
I personaly would register the messages in App.xaml.cs in the OnStartup method (WPF) and in the set up method of the unit test (dont forget to unregister everything in the tear down method).
I have spend almost two day trying to figure out why this is happening but couldn't find a solution so far. So I am posting it here.
I have a pretty generic Sliding Tab which is created in Eclipse for newer Android APIs. Inside one of the sliding tabs I call a ListFragment. This ListFragment uses CursorLoader to load some data.
Now when the app exits, it gives: 05-28 11:34:00.327: E/AndroidRuntime(31994): java.lang.RuntimeException: Unable to destroy activity {com.example.myapp/com.example.myapp.main.HomeActivity}: java.lang.NullPointerException
I have tried using ChildFragmentManager and also the latest support package, but to no avail.
This is the only tab which calls another fragment, otherwise rest of the tabs which just call static XML content work just fine. App works fine if I remove this tab.
As I understand I need to destroy the CursorLoader or somehow detach this particular fragment before the app exits. CursorLoader seems to get destroyed, and the error is caught in HomeActivty. Maybe I should call onDestroy in HomeActivity, but really don't know how and where exactly.
Code of the calling HomeActivity is pretty standard:
ViewPager mViewPager;
private static final String DEBUG_TAG = "MY APP";
private static boolean logged_in;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
logged_in = sharedPrefs.getBoolean("logged_in", false);
Log.v(DEBUG_TAG, "logged_in: " + logged_in);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.home, menu);
return true;
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
Fragment fragment;
switch(position) {
case 0:
fragment = new MySectionFragment();
break;
case 1:
...
The code which calls the ListFragment from the FragmentActivity is as follows:
public static class MySectionFragment extends Fragment {
public MySectionFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_my_section, container, false);
}
public void onDestroyView() {
super.onDestroyView();
Fragment fragment = (getFragmentManager().findFragmentById(R.id.fragment_my_section));
if (fragment != null) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commitAllowingStateLoss();
}
}
}
Finally I found the answer and it was in the most unexpected place in the code. I still don't understand the mechanics, but I have confirmed throughly that it works.
I am using Android 4.0.3 with support library rev 12.
Turned out that my code was perfectly fine and I didn't even need onDestroyView(). Views get destroyed and regenerated fine. There was no need to deal with FragmentManager, SupportFragmentManager or ChildSupportFragmentManager. The default code which is generated in Eclipse when selecting swipable tabs is just fine.
The only place where I made a change, which fixed it all was the XML template. I moved the android:id from 'fragment' section to 'RelativeLayout' section.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="#+id/fragment_my_section"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="4dp"
android:background="#drawable/bg">
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:drawSelectorOnTop="false"
android:cacheColorHint="#00000000"/>
<fragment
android:name="com.example.MyFragmentActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</fragment>
</RelativeLayout>
Previously the id was in the 'fragment' section. This was when there was no RelativeLayout and ListView defined in the XML file.
Needless to say I hate XMLs in this Android development world. Majority of the time it has always been an XML file which has wasted my time, without an exception. IDEs don't catch these XML issues well either. I wish Android development get rid or XMLs altogether..
I would like to implement menus (MenuBar, MenuItem) using the declarative approach via UiBinder in GWT 2.0.
I have run into two problems:
Is there a way to add MenuItemSeparators in the .ui.xml file? So
far, I have only managed to put MenuBar- and MenuItem-tags into the
file.
Using #UiHandler, GWT writes the boilerplate code for event
handlers for me. For menus, I need to write commands. How am I
supposed to do this using the UiBinder approach? Is there a command
tag to put in the .ui.xml file? Do I have to write the boilerplate
code for the command handlers myself?
Thanks for thinking about these questions!
I agree, if you try to put a MenuItemSeparator in, it will complain stating only a MenuItem can be a child when GWT tries to create the widget . Since this is not currently supported, I suggest that you request this as a future enhancement to the GWT team.
In the meantime, you can add a separator programmatically and add a command in the following manner:
The XML file:
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:HTMLPanel>
<g:MenuBar ui:field="menuBar">
<g:MenuItem ui:field="helpMenuItem">Help</g:MenuItem>
<g:MenuItem ui:field="aboutMenuItem">About</g:MenuItem>
<g:MenuItem ui:field="siteMapMenuItem">Site Map</g:MenuItem>
</g:MenuBar>
</g:HTMLPanel>
The Java file(s):
public class Menu extends Composite {
...
#UiField MenuBar menuBar;
#UiField MenuItem helpMenuItem;
...
public Menu() {
initWidget(uiBinder.createAndBindUi(this));
// insert a separator
menuBar.insertSeparator(1);
// attach commands to a menu item
helpMenuItem.setCommand(new MenuCommand(HistoryToken.Help));
...
}
public class MenuCommand implements Command {
final HistoryToken historyToken;
public MenuCommand(HistoryToken historyToken) {
this.historyToken = historyToken;
}
#Override
public void execute() {
historyToken.fire();
}
}
public enum HistoryToken {
Help,About,SiteMap;
public void fire(){
History.newItem(this.toString());
}
}
Elsewhere in my code, I implemented a HistoryListener to catch any changes, i.e.
class HistoryManager implements ValueChangeHandler<String> {
// 1. get token
// 2. change it into a HistoryToken
// 3. perform switch statement
// 4. change contents based upon HistoryToken found
...
}
For (1) JavaDoc says:
Use in UiBinder Templates
MenuBar elements in UiBinder template files can have a vertical boolean attribute (which defaults to false), and may have only MenuItem elements as children. MenuItems may contain HTML and MenuBars.
For example:
<g:MenuBar>
<g:MenuItem>Higgledy
<g:MenuBar vertical="true">
<g:MenuItem>able</g:MenuItem>
<g:MenuItem>baker</g:MenuItem>
<g:MenuItem>charlie</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
<g:MenuItem>Piggledy
<g:MenuBar vertical="true">
<g:MenuItem>foo</g:MenuItem>
<g:MenuItem>bar</g:MenuItem>
<g:MenuItem>baz</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
<g:MenuItem><b>Pop!</b>
<g:MenuBar vertical="true">
<g:MenuItem>uno</g:MenuItem>
<g:MenuItem>dos</g:MenuItem>
<g:MenuItem>tres</g:MenuItem>
</g:MenuBar>
</g:MenuItem>
</g:MenuBar>
Taking the hint from the words "only MenuItem elements as children", my guess is that MenuItemSeparators are not supported
Here's an example of my solution to this, which seems to work pretty well with GWT 2.4.0.
UiBinder:
<g:MenuBar vertical='true' ui:field='mainMenu'>
<g:MenuItem ui:field='item1'>Item 1</g:MenuItem>
<g:MenuItem ui:field='item2'>Item 2</g:MenuItem>
<g:MenuItemSeparator />
<g:MenuItem ui:field='sub' enabled='false'>
Submenu
<g:MenuBar vertical='true' ui:field='subMenu' />
</g:MenuItem>
</g:MenuBar>
Java:
#UiField MenuItem item1;
#UiField MenuItem item2;
#UiField MenuBar subMenu;
#UiField MenuItem sub;
...
this.setWidget(uiBinder.createAndBindUi(this));
item1.setCommand(new Command() {
public void execute() {
History.newItem("item1");
}
});
Overall not too bad.
I know this question is OLD, but I keep running across this question in my google searches, so i thought it would be important to note that although i haven't seen it documented anywhere yet, i have been using:
<g:MenuItemSeparator/>
successfully in my uibinder template. The gwt eclipse plugin gives me a red error marker, but the MenuItemSeparator compiles and shows up ok. I'm using gwt 2.1.
Just thought someone might be interested to know.
Unfortunately I haven't seen a solution for #2 yet - but I hope they give us something soon.
It is possible to add a menuItemSeparator in the ui.xml file. Here is an example with separator and submenu from the official GWT-API page.
Well, i think i found a way to implement this. (This is a solution if you want to declare the separator inside the *.ui.xml file. )
HocusView.java
...
#UiField MenuBar menuBar;
#UiField MenuItem item1;
#UiField MenuItem item2;
#UiField MenuItem item3;
#UiField MenuItem item4;
...
private static HocusViewUiBinder uiBinder = GWT.create(HocusViewUiBinder.class);
#UiTemplate("Hocus.ui.xml")
interface HocusViewUiBinder extends UiBinder<Widget, HocusView>{}
public HocusView()
{
initWidget(uiBinder.createAndBindUi(this));
// Attach commands to menu items
menuItem1.setScheduledCommand(cmd_menuItem1);
menuItem2.setScheduledCommand(cmd_menuItem2);
menuItem3.setScheduledCommand(cmd_menuItem3);
menuItem4.setScheduledCommand(cmd_menuItem4);
}
Command cmd_menuItem1= new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem2 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem3 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
Command cmd_menuItem4 = new Command(){
#Override
public void execute() {
Window.alert(" Gifts ");
}
};
});
HocusView.ui.xml
<gwt:MenuBar ui:field="menuBar" >
<gwt:MenuItem ui:field="menuItem1">Search</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem2">Ingestion</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem3">Analysis</gwt:MenuItem>
<gwt:MenuItemSeparator></gwt:MenuItemSeparator>
<gwt:MenuItem ui:field="menuItem4">About</gwt:MenuItem>
</gwt:MenuBar>
Its as simple as that.This will add a separator between the menu items.
Cheers!