Navigating to pages with the AppBar with MvvmLight, WinRt - c#

I have a top app bar customer user control that is used to navigate in my WinRt MvvmLight application. This custom control is added to all my pages
TopAppBar User Control
<AppBarButton
x:Uid="HomeTopAppBar"
Command="{Binding HomeCommand}"
Icon="Home"/>
<AppBarButton
x:Uid="LibraryTopAppBar"
Command="{Binding LibraryCommand}"
Icon="Library"/>
These commands are added to all my ViewModels. For example,
HomeViewModel
public RelayCommand LibraryCommand {
get {
return new RelayCommand(
() => _navigationService.Navigate(typeof(LibraryPage)));
}
}
public RelayCommand HomeCommand {
get {
return new RelayCommand(
() => _navigationService.Navigate(typeof(HomePage)));
}
}
The problem I'm having is that if the user uses the top app bar, the first click works (i.e., the user is navigated to the correct page.) On the second click, the program crashes in NavigationHelper on the OnNavigatedFrom method (taken from MvvmLight). The program complains that the _pageKey is null. Is there something that I'm doing incorrectly? How exactly does a user navigate to other pages from the App Bar?

Instead of adding a command in my Appbar, i decided to use a click event. The reason why i did this is because by using the command, every viewmodel that uses my appbar needs to create that command. Here is my solution:
TopAppBar.xaml
<AppBarButton
x:Uid="HomeTopAppBar"
Click="AppBarButton_Home"
Icon="Home"/>
<AppBarButton
x:Uid="LibraryTopAppBar"
Click="AppBarButton_Library"
Icon="Library"/>
TopAppBar.xaml.cs
private void AppBarButton_Home(object sender, RoutedEventArgs e) {
SimpleIoc.Default.GetInstance<INavigationService>().Navigate(typeof(HomePage));
}
private void AppBarButton_Library(object sender, RoutedEventArgs e) {
SimpleIoc.Default.GetInstance<INavigationService>().Navigate(typeof(LibraryPage));
}
Any page navigated to from Appbar (Ex. HomePage)
public sealed partial class HomePage {
public HomePage() {
InitializeComponent();
}
public HomeViewModel ViewModel {
get {
return (HomeViewModel)DataContext;
}
}
//needed for navigation
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
}
//needed for navigation
protected override void OnNavigatedFrom(NavigationEventArgs e) {
base.OnNavigatedFrom(e);
}
}

Related

event when the left menu is opened in xamarin forms shell

I am working with xamarin forms shell. I have 1 image and 1 label to assign the name and profile picture of the user.
I want every time the menu opens (press the button or drag to the right) to update that value again, but I can't find any events related to it. Someone please help me badly
Since there is no and won't be events such OnFlyoutOpened OnFlyoutClosed, you can listen to your Shell PropertyChanged event, if the property is FlyoutIsPresented then execute your code:
public AppShell()
{
InitializeComponent();
PropertyChanged += Shell_PropertyChanged;
}
private void Shell_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("FlyoutIsPresented"))
if (FlyoutIsPresented)
OnFlyoutOpened(); //you will execute your code here
else
OnFlyoutClosed();
}
Depending on your requirement you will define OnFlyoutOpened() and OnFlyoutClosed() methods.
Thanks to #PureWeen guidance in discussion.
You could use custom renderer to get the event when you press the shell hamburger icon.
[assembly: ExportRenderer(typeof(AppShell), typeof(ShellCustomRenderer))]
namespace ShellDemo.Droid
{
public class ShellCustomRenderer : ShellRenderer
{
public ShellCustomRenderer(Context context) : base(context)
{
}
protected override IShellToolbarAppearanceTracker CreateToolbarAppearanceTracker()
{
return new ToolbarAppearance();
}
}
public class ToolbarAppearance : IShellToolbarAppearanceTracker
{
public void Dispose()
{
}
public void ResetAppearance(Android.Support.V7.Widget.Toolbar toolbar, IShellToolbarTracker toolbarTracker)
{
toolbar.NavigationClick += Toolbar_NavigationClick1;
}
private void Toolbar_NavigationClick1(object sender, Android.Support.V7.Widget.Toolbar.NavigationClickEventArgs e)
{
//this event would be fired when the hamburger icon clicked.
}
private void Toolbar_NavigationClick(object sender, Android.Support.V7.Widget.Toolbar.NavigationClickEventArgs e)
{
}
public void SetAppearance(Android.Support.V7.Widget.Toolbar toolbar, IShellToolbarTracker toolbarTracker, ShellAppearance appearance)
{
}
}
}

Not navigating from one page to another page in Xamarin.Forms while using using Xamarin.Essentials shake detect event

I have two class MainPage.xaml and Home.xaml. I have written Home.xaml using Xamarin.Essentials shake detect code and want to move from Home to MainPage.xaml but it is not working.
Home.xaml.cs
namespace StarRatingApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Home : ContentPage
{
public Home()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
Accelerometer.ShakeDetected += Accelerometer_ShakeDetected;
Accelerometer.Start(SensorSpeed.Game);
}
private void Accelerometer_ShakeDetected(object sender, EventArgs e)
{
MainThread.BeginInvokeOnMainThread(() =>
{
new NavigationPage(new MainPage());
});
}
}
What am I doing wrong in Accelerometer_ShakeDetected method ??
You are not navigating in the method. You just create a new NavigationPage. And this won't do anything. You have to tell the system, where you want to navigate and how.
You have two options:
1) Navigate to your page (this adds the page to the Navigation-Stack)
await Navigation.PushAsync(new MainPage());
or 2) Overwrite your mainpage with the new NavigationPage (this removes the whole NavigationStack and starts a "new" navigation)
Application.Current.MainPage = new NavigationPage(new MainPage());
You can find more infos about navigation in the documentation >> https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical

How to set title in WPF without refresh all window page

Hi !
I have a WPF application and I want to set the title of the window page without refresh the all page, because in this page I have two buttons that list a DataRow belong the Title when I press it.
void refreshStatusBar()
{
this.Title= "Holaaa";
}
WPF class:
<Height=.... Title="Prueba"...> the initial value
The problem is when I press a button (next or back) I need to set the Title of the page and never change when I call to refreshStatusBar() in the btNext or btBack method.
I tryed to binding the Title, but don´t work. Always show the same value, the initial:
Title="{Binding Path="windowTitleBar"}"
public String windowTitleBar {get; set;}
void refreshStatusBar(){
windowTitleBar="Holaaa";
}
I want the title change when I press some button. I don´t have pages inside the window page, just show one thing or another thing.
I tryed too:
Title="{Binding Path=windowTitleBar, RelativeSource={RelativeSource Mode=Self}}"
and don´t work neither.
Please, any solution to fix it?
Sorry for my english !
Thanks !
This works for me without a binding:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.Title = "Hellooo";
}
void RefreshStatusBar()
{
this.Title = "Holaaa";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
RefreshStatusBar();
}
}
If you want to use a binding, set it up like you did with Title="{Binding Path=WindowTitleBar, RelativeSource={RelativeSource Mode=Self}}"
But as it is, WPF has no way of knowing when your property value changes. You can implement INotifyPropertyChanged to solve this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _windowTitleBar = "Hellooo";
public MainWindow()
{
this.WindowTitleBar = "Hellooo";
InitializeComponent();
}
public string WindowTitleBar
{
get { return _windowTitleBar; }
set
{
_windowTitleBar = value;
OnPropertyChanged("WindowTitleBar");
}
}
void RefreshStatusBar()
{
this.WindowTitleBar = "Holaaa";
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private void button1_Click(object sender, RoutedEventArgs e)
{
RefreshStatusBar();
}
}
Edit:
I just noticed you said "Page". I've never used Pages, but it looks like to set the title of the window containing your page, you have to set the WindowTitle property. Unfortunately it's not a DependencyProperty so you can't use a binding. You can set it directly, though:
void RefreshStatusBar()
{
this.WindowTitle = "Holaaa";
}

Load a method of User Control that is calling in another page (Windows Phone7)

I have a UserControl with name UcMdisaese inside MainPage that execute an animation.
public partial class MDisaese : UserControl
{
Public void Fases(int i)
{
_mdmodel.enojo();
enojado = 0;
_timer1.Interval = TimeSpan.FromSeconds(i);
}
}
Also I have a Page with name "Agrega_m" inside when ocurrs the event guardar_agrega_Click activate the animation I suppose, but doesn't work the method Fases to call the animation when I return to Mainpage that contain the User Control.
The question, How can load the method?
public partial class Agrega_m: PhoneApplicationPage
{
private void guardar_agrega_Click(object sender, EventArgs e)
{
MDisaese hi = new MDisaese();
hi.fases(30);
}
}

Dialogs with regions in a WPF Application using PRISM

I am developing a WPF-Application using PRISM/Unity and the MVVM approach. So far everything is fine, but I think I have a conceptual problem.
Since I need modal dialogs I implemented a dialogservice, which gets injected into the viewmodels and can be used to open up a modal dialog with a corresponding view. This dialog needs to be navigable ( = contains a region), so that I am able to switch views within the dialog. For this the service creates an instance of a window and navigates to the requestd view in a DialogRegion. This also works.
My problem now is, that at some point I need to open a second modal dialog which also needs to be navigable. If I use my dialogservice I would get a new instance of the dialog window and an exception is thrown, since the region was already registered with the regionmanager (It's done in the code behind of the dialog window). This makes sense, since the regionmanager would not be able to distinguish the regions otherwise, but prevents me from opening a second navigable dialog.
What would be a better approach? I can only think of creating a second dialog window with a different name for the region, but that seems to be a very ugly solution...
EDIT 1:
Code for showing a dialog:
dialogService.ShowDialog<MyViewModel>();
Code in the dialog service for showing a dialog:
public void ShowDialog<TDialogViewModel>() where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
dialog.ShowDialog();
}
Code in the dialog-window:
public DialogWindow(IRegionManager regionManager) : this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
Code in the view for construction, navigation and showing other dialogs
public MyViewModel(IRegionManager regionManager, IModalDialogService dialogService) : this()
{
this.regionManager = regionManager;
this.dialogService = dialogService;
}
void NavigateToSecondView()
{
regionManager.RequestNavigate<MyViewModel2>(Regions.DialogRegion);
}
void ShowDialog2()
{
// This is where a second dialog window with MyViewModel3 should be shown
dialogService.ShowDialog<MyViewModel3>();
}
EDIT 2:
XAML for the Window:
<Window.Resources>
<DataTemplate DataType="{x:Type localModels:MyViewModel}">
<localViewsProject:MyView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Grid.Row="0" x:Name="DialogContent" Margin="10,10,10,10"
prismRegions:RegionManager.RegionName="{x:Static navi:Regions.DialogRegion}">
</ContentControl>
</Grid>
EDIT 3 - Code for IModalDialog:
I got the idea for IModalDialog somewhere in the web, but I can't remember where. The idea is to have an interface that is implemented by some window-instances and allows for interaction with the user. My dialog only has one content-region (plus a region for status/progress notifications) that can be filled through prism.
Here some code to explain the idea:
public partial class DialogWindow : Window, IModalDialog
{
internal IRegionManager regionManager;
public DialogWindow()
{
InitializeComponent();
}
public DialogWindow(IRegionManager regionManager)
: this()
{
RegionManager.SetRegionManager(this, regionManager);
this.regionManager = regionManager;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Load Model, when window is loaded
BaseViewModel model = this.DialogContent.Content as BaseViewModel;
if (model != null)
{
model.LoadData();
}
}
private void Window_Unloaded(object sender, RoutedEventArgs e)
{
regionManager.Regions.Remove(Regions.DialogRegion);
regionManager.Regions.Remove(Regions.DialogStatusRegion);
}
}
So in order to use it I have a static ModalDialogService, which receives a model and some action to be done after the dialog is closed. Something like this:
public void ShowDialog<TDialogViewModel>(Action<TDialogViewModel> initializeAction = null, Action<TDialogViewModel> actionAfterClose = null) where TDialogViewModel : class
{
IModalDialog dialog = ServiceLocator.Current.TryResolve<IModalDialog>();
if (actionAfterClose != null)
{
WeakEventManager<IModalDialog, EventArgs>.AddHandler(
dialog,
"Closed",
(sender, e) => dialogView_Closed(sender, e, typeof(TDialogViewModel), Regions.DialogRegion, actionAfterClose));
}
dialog.Owner = Application.Current.MainWindow;
regionManager.RequestNavigate(typeof(TDialogViewModel), Regions.DialogRegion);
TDialogViewModel model = null;
var region = regionManager.Regions[Regions.DialogRegion];
foreach (var view in region.ActiveViews)
{
if (view is TDialogViewModel)
{
model = (TDialogViewModel)view;
if (initializeAction != null)
{
initializeAction(model);
}
}
}
// Attach an event listener to the closing event in order to prevent closing
// if the form is not in a closeable state.
if (typeof(IConfirmNavigationRequest).IsAssignableFrom(typeof(TDialogViewModel)))
{
WeakEventManager<IModalDialog, CancelEventArgs>.AddHandler(
dialog,
"Closing",
(sender, e) => dialogView_Closing(sender, e, Regions.DialogRegion));
}
// This event can be published by a viewmodel in order to close the dialog
eventAggregator.GetEvent<DialogCloseRequestedEvent>().Subscribe((containingDialog) =>
{
if (containingDialog == model)
{
dialog.Close();
}
});
dialog.ShowDialog();
}
}
Instead of re-registering a Region with the RegionManager. You could reuse the initial Region that is registered and create multiple instances of it. These are known as Scoped Regions (under the section Creating Multiple Instances of a Region). For example:
// You may need to keep track of these scopes
IRegionManager scopedRegionManager = _regionManager.Regions["DialogRegion"].Add(view, viewName, true);
scopedRegionManager.Navigate(viewName);
EDIT
I believe it's as simple as changing the constructor of your DialogWindow to this:
public DialogWindow(IRegionManager regionManager) : this()
{
var scopedRegionManager = regionManager.Regions[Regions.DialogRegion].Add(this, null, true);
this.regionManager = scopedRegionManager;
}

Categories