I am trying to create an app using Prism in Xamarin Forms.
Xamarin Forms Version: 2.3.3.175
Prism Version: 6.2.0
The hamburger menu works in Android but when I run it UWP it won't display the icon and also when I navigate through menu, the menu totally disappears and I wont have the method go back to other pages too. In other words, I need to close and restart the app.
Here is what I tried so far.
After creating the prism project I added a MasterDetailPage:
<MasterDetailPage.Master>
<ContentPage Title="Default">
<StackLayout>
<Button Text="Billing" Command="{Binding Path=NavigationCommand}" CommandParameter="MyNavigationPage/BillingPage"/>
<Button Text="Your Order" Command="{Binding Path=NavigationCommand}" CommandParameter="MyNavigationPage/PlaceOrderPage"/>
<Button Text="Settings" Command="{Binding Path=NavigationCommand}" CommandParameter="MyNavigationPage/SettingsPage"/>
<Button Text="Settings"/>
</StackLayout>
</ContentPage>
</MasterDetailPage.Master>
MasterDetailPage ViewModel
public class MDPageViewModel : BindableBase
{
private INavigationService _navigationService;
public DelegateCommand<string> NavigationCommand => new DelegateCommand<string>(Navigation);
public MDPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
}
private void Navigation(string page)
{
_navigationService.NavigateAsync(page);
}
}
After that I created a navigation page and also respective pages and view models. Here is App.Xaml.cs file:
public partial class App : PrismApplication
{
public App(IPlatformInitializer initializer = null) : base(initializer) { }
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("MDPage/MyNavigationPage/ItemsPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MDPage>();
Container.RegisterTypeForNavigation<BillingPage>();
Container.RegisterTypeForNavigation<PlaceOrderPage>();
Container.RegisterTypeForNavigation<SettingsPage>();
Container.RegisterTypeForNavigation<MyNavigationPage>();
}
}
So when I run the app in UWP it loads like this
But when I click on the links in menu , menu will disappear and it looks like this.
What I am doing wrong and How can I solve it?
I created a project in github so you can easily view the error.
https://github.com/codemasterblackperl/Hamburger_Menu_Prism_Forms_Repo
This is a bug in the latest version of Xamarin. It works when using 2.3.1.114. I've posted the bug here since i just ran into this.
This will only answer your question partially. Not being able to see the icon got me too, although it's documented in the Prism.Forms docs. In order to get the icon, go to the App.Xaml in your UWP project and add the following data template between <Application.Resources>...</Application.Resources>
<DataTemplate x:Key="ItemTemplate">
<uwp:ItemControl HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</DataTemplate>
Define the uwp prefix at the top like xmlns:uwp="using:Xamarin.Forms.Platform"
You App.Xaml should look something like this:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uwp="using:Xamarin.Forms.Platform">
<Application.Resources>
<DataTemplate x:Key="ItemTemplate">
<uwp:ItemControl HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" />
</DataTemplate>
</Application.Resources>
After you've done that, it will display the icon. However, this will show you that once you click an item in the master, the menu collapses. I've not been able to fix that.
Just use IMasterDetailPageOptions interface in your MasterDetail code-behind:
public partial class ShellView : MasterDetailPage, IMasterDetailPageOptions
{
public ShellView()
{
InitializeComponent();
}
public bool IsPresentedAfterNavigation
{
get { return Device.Idiom != TargetIdiom.Phone; }
}
}
Related
I'm learning the new WinUI platform together with XAML and GUI programming in C# and I'm not sure if I correctly understand how the bindings and updates work, but I can't get the following code (minimal working example) to work:
MainPage.xaml
<Page
x:Class="uitest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<ListView ItemsSource="{x:Bind Items, Mode=OneWay}"></ListView>
<Button Content="Add" Click="Button_OnClick"></Button>
</StackPanel>
</Page>
MainPage.xaml.cs
using System.Collections.ObjectModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace uitest
{
public sealed partial class MainPage : Page
{
public ObservableCollection<string> Items = new ObservableCollection<string>();
public MainPage()
{
this.InitializeComponent();
Items.Add("A");
Items.Add("B");
}
private void Button_OnClick(object sender, RoutedEventArgs e)
{
Items.Add("Next");
}
}
}
When I start the application, I see a list with two items but when I click the button nothing happens. I wanted to use the Live Visual Tree for debugging but it's not working for some reason.
I opened an issue on the official WinUI repository for this and apparently it's a compatibility fault in the latest WinUI 3 preview. I should also note this does not work only on the UWP platform but it works on the Desktop (Win32).
I have built a good working WPF-application which is mvvm compliant. I have one window, in which I am controlling via a property(public Object named CurrentHomeView) which Datatemplate will be shown. For example: If my property equals LoginViewModel, the window will display the LoginControl which is a UserControl. So far so good.
Now I want to take this system and use it in a xamarin app. But the way I made it in my WPF-application seems not to be the right way in xamarin.
What do I have to do, that it will work the same way?
You will the in my code, what I have built so far. My ContantPage is not changing the constant.
My Class to manage the value of my property:
namespace Core.ApplicationStartUp
{
public class StartUp : BindableBase
{
LoginViewModel _loginViewModel;
private bool _isNewUser;
private object _currentMainView;
public object CurrentMainView
{
get => _currentMainView;
set => SetProperty(ref _currentMainView, value);
}
public bool IsNewUser
{
get => _isNewUser;
set => SetProperty(ref _isNewUser, value);
}
public StartUp()
{
_isNewUser = SearchUserData();
if (!IsNewUser)
LoadLoginControl();
}
private bool SearchUserData()
{
return false;
}
private void LoadLoginControl()
{
_loginViewModel = new LoginViewModel(this);
bool isIn = _loginViewModel.CheckStatus();
if (!isIn)
CurrentMainView = _loginViewModel;
else
_loginViewModel.Login();
}
}
}
`
My Window in the WPF application:
<Window.Resources>
<DataTemplate DataType="{x:Type loginCore:LoginViewModel}">
<loginControl:LoginControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type registrationCore:RegistrationViewModel}">
<registrationControl:RegistrationControl/>
</DataTemplate>
<DataTemplate DataType="{x:Type navigationCore:NavigationViewModel}">
<navigationControl:NavigationControl/>
</DataTemplate>
</Window.Resources>
<Border BorderThickness="0.5" BorderBrush="White">
<DockPanel LastChildFill="True">
<ContentControl Content="{Binding CurrentMainView, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DockPanel>
</Border>
And this is my Page in Xamarin, which should use the same Class to mage the active View:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigationControl="clr-namespace:Mobile.Areas.Navigation"
xmlns:navigationCore="clr-namespace:Core.Areas.Navigation;assembly=Core"
xmlns:registrationCore="clr-namespace:Core.Areas.Registration;assembly=Core"
xmlns:registrationControl="clr-namespace:Mobile.Areas.Registration"
xmlns:loginCore="clr-namespace:Core.Areas.Login;assembly=Core"
xmlns:loginControl="clr-namespace:Mobile.Areas.Login"
x:Class="Mobile.MainWindow">
<ContentPage.Resources>
<DataTemplate x:DataType="{x:Type loginCore:LoginViewModel}"
x:Key="LoginControl">
<loginControl:LoginControl/>
</DataTemplate>
<DataTemplate x:DataType="{x:Type registrationCore:RegistrationViewModel}"
x:Key="RegistrationControl">
<registrationControl:RegistrationControl/>
</DataTemplate>
<DataTemplate x:DataType="{x:Type navigationCore:NavigationViewModel}"
x:Key="NavigationControl">
<navigationControl:NavigationControl/>
</DataTemplate>
</ContentPage.Resources>
<StackLayout>
<ContentView Content="{Binding CurrentMainView}"/>
</StackLayout>
</ContentPage>
I do not get any exceptions or errors. Further more there is no information in the output.
There is no equivalent to a WPF ContentControl in Xamarin.Forms. You could create a custom ContentView as suggested here:
public class ContentPresenter : ContentView
{
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(ContentPresenter), null, propertyChanged: OnItemTemplateChanged);
private static void OnItemTemplateChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var cp = (ContentPresenter)bindable;
var template = cp.ItemTemplate;
if (template != null)
{
var content = (View)template.CreateContent();
cp.Content = content;
}
else
{
cp.Content = null;
}
}
public DataTemplate ItemTemplate
{
get
{
return (DataTemplate)GetValue(ItemTemplateProperty);
}
set
{
SetValue(ItemTemplateProperty, value);
}
}
}
There is also no support for implicit data templates. You can work around this by using a converter as suggested here.
As I understand, you want to reuse your ViewModels from WPF app in Xamarin app - this is doable, but not easy.
The way you did view switching in WPF works and is OK, but it is not "how it's done" when you want to go multiplatform.
First thing you should do, is to make sure that ViewModels are platform agnostic - put them all in a PCL. Proper MVVM app should be able to run and do stuff even without any views attached - this includes switching the active ViewModel.
You achieve this by using Navigation/Routing service - most MVVM frameworks have that. Basically, it is a thing that you ask to show some ViewModel - if you want to learn a lot, write your own, otherwise use of the existing, it's not worth your time to invent it one more time. How the VM is shown depends on what platform the program is running - there should be no difference from VM point of view though. Still, you will need seperate Views (UserControls in WPF, Pages in Forms) for each platform.
One of the best MVVM frameworks in my opinion is ReactiveUI, written with Xamarin in mind, so it's fast, has low startup time etc. It also works with most platforms: WPF, WinForms, Windows Universal Apps, even Avalonia and some others. RxUI even supports views activation out of the box which is more than handy. Also, once you go reactive, there is no way back :)
This article conveys the basics of the routing concept.
Happy digging from there :)
I'm using the WPF Ribbon Application Menu:
https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11877
https://msdn.microsoft.com/de-de/library/ff799534(v=vs.110).aspx
How can I close the Application (File) Menu programmatically?
How can I detect if the user opens the Application Menu? I didn't found an appropriated event
You need IsDropDownOpen property and related event(s).
XAML (this is for .NET 4.5+, but for 4.0 it will be almost the same, the difference will be in namespace prefix):
<StackPanel>
<Ribbon>
<Ribbon.ApplicationMenu>
<RibbonApplicationMenu x:Name="Menu" DropDownOpened="RibbonApplicationMenu_DropDownOpened">
<RibbonApplicationMenuItem Header="Foo"/>
<RibbonApplicationMenuItem Header="Bar"/>
</RibbonApplicationMenu>
</Ribbon.ApplicationMenu>
</Ribbon>
</StackPanel>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void RibbonApplicationMenu_DropDownOpened(object sender, EventArgs e)
{
// user has opened menu
Debug.WriteLine("Menu opened.");
// let's close it from code
Menu.IsDropDownOpen = false;
}
}
Also, you may want to disable entire menu. This can be done using IsEnabled property.
I am setting the GridView Data Context in XAML similar to so:
<Grid x:Name="pageGrid">
<Grid.DataContext>
<local:ViewModel/>
</Grid.DataContext>
</Grid>
This "ViewModel" class is a static class that is fill during:
public MainPage()
{
this.InitializeComponent();
FillViewModel():
}
This allows for the view model to bound on screen load which works.
However, I want to load FillViewModel after the MainPage has initially ran.
Is there a way to programatically refresh the pageGrid XAML databinding? I have to be just missing it.
However, I want to load FillViewModel after the MainPage has initially ran.
Sounds like your looking for the MainWindow / Control "Loaded" Event.
XAML:
<Grid x:Name="Grid1" Loaded="Grid1_Loaded"/>
Code Behind:
private void Grid1_Loaded(object sender, RoutedEventArgs e)
{
// Do Stuff
}
EDIT:
The problem was that my View Models were not public, so the bindings were not found. For some reason they need to be public in Silverlight, even though private view models work for WPF. More detail in my answer below.
I am trying to implement an MVVM WPF app in Silverlight, but I have never used Silverlight before. I am struggling to get the bound content to display. I am implementing it almost exactly how I did with WPF, except that the Main bit is a UserControl, not a Window, the app is hosted by a Web Site, and I had to remove the x:Type in DataTemplate DataType
My Website-to-MainPage is working properly, because I can display a text box or something in that page, however the Binding is just not displayed. Any suggestions?
My MainPage.xaml.cs:
public partial class MainPage : UserControl
{
private MainPageViewModel _viewModel;
public MainPage()
{
InitializeComponent();
_viewModel = new MainPageViewModel();
this.DataContext = _viewModel;
}
}
My MainPage.xaml:
<UserControl x:Class="TransformationServices.Silverlight.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"
xmlns:view="clr-namespace:TransformationServices.Silverlight.View"
xmlns:viewModel="clr-namespace:TransformationServices.Silverlight.ViewModel"
xmlns:local="clr-namespace:TransformationServices.Silverlight"
Background="#FF2D2D30">
<UserControl.Resources>
<local:ViewConverter x:Key="viewConverter"/>
<DataTemplate DataType="viewModel:InputSelectViewModel">
<TextBlock Text="Hello World!"/>
<!-- This textblock is just for testing. It doesn't work, and neither does the following line-->
<view:InputSelect/>
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{Binding CurrentView}" />
</UserControl>
My MainPageViewModel.cs
class MainPageViewModel : ViewModelBase, INotifyPropertyChanged
{
private ViewModelBase _currentView;
private List<ViewModelBase> _viewModels;
private int _viewIndex;
public ViewModelBase CurrentView
{
get { return _currentView; }
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
public MainPageViewModel()
{
_viewModels = new List<ViewModelBase>();
// Add view models here
_viewModels.Add(new InputSelectViewModel());
_viewIndex = 0;
CurrentView = _viewModels[_viewIndex];
}
}
It would appear as though the problem is that my ViewModels were not public. I noticed in the Output the following type of message:
System.Windows.Data Error: Cannot get '<Whatever property of View Model>' value ...
This message was coming up for every property that I was binding to, including CurrentView. After making the View Models public, the bindings worked perfectly. It seems odd to me that Silverlight would need the classes to be public when they worked fine as private for WPF, but this is my first time working with Silverlight, so this intricacy tripped me up. Hopefully this answer can help someone in the future. Anyway, this is the main reason for why the bindings were not working, and thus the CurrentView was not being displayed.
Another reason that it was hard for me to debug this issue is that my page was accidentally caching everything, so some of my changes would not display. This link helped me with that:
http://www.codeproject.com/Articles/143414/Prevent-your-Silverlight-XAP-File-from-Caching-in