I'm having trouble with navigation outside of the typical use cases for navigation controllers and tabbar controllers. Here's a drawing of a simplified version. (real version has a TabBarController that feeds into 9 NavControllers)
In my Home Nav Controller, there's a table view where the user could select a row and that should navigate them to a particular Events Detail View Controller. At that point, the user should be able to navigate back to the Events TableView Controller to see the list of events or use the TabBar to navigate to another section.
So far with my code I can push the correct detail view controller and select the correct index of the tabbar but it only works the first time:
public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
tableView.DeselectRow(indexPath, true);
var eventsDetailView = new EventsDetailController(eventPosts[indexPath.Row]);
//loop through ViewControllers array in case the user has set a custom tabbar order via the More Tab
foreach (UIViewController viewC in tbController.ViewControllers)
{
UINavigationController navController = viewC as UINavigationController;
if (navController.TabBarItem.Tag.Equals(9))
{
navController.PushViewController(eventsDetailView, false);
tbController.SelectedIndex = 9;
}
}
}
After the user has navigated to the events detail view controller, if they then:
travel to the Home ViewController via the TabBar
select a new TableRow to navigate to a different Events detail view controller
... then the same previous events detail view shows up. It doesn't matter which row the user selects in the Home View Controller. The initial detail view controller will always be the same one to be presented.
Any help pointing me in the right direction would be greatly appreciated!
Solution:
In my understanding, what you want is :
HomePageController --> EventDetailPage --> EvevtTableViewControll
So in the first step, you could push to EventDetailPage in HomePageController:
this.NavigationController.PushViewController(new HomeDetailUIViewController(), true);
Then, in your EventDetailPage, you can customize a back button, for example:
UIBarButtonItem backBtn = new UIBarButtonItem();
backBtn.Title = "back";
backBtn.Style = UIBarButtonItemStyle.Plain;
this.NavigationItem.LeftBarButtonItem = backBtn;
backBtn.Clicked += (sender, e) =>
{
// 1 here is an example, it should be the index of your EvevtTableViewControll
NavigationController.TabBarController.SelectedIndex = 1;
NavigationController.PopToRootViewController(true);
};
Set the NavigationController.TabBarController.SelectedIndex=1 first to make sure EvevtTableViewControll will show after you back from EvevtDetailViewControll, then your PopToRootViewController to back to the EvevtTableViewControll.
Try it and let me know if you have any problem.
Related
I need to implement a simple UIPageViewController using Xamarin in Visual Studio 2017 for mac.
As the documentation says in https://developer.xamarin.com/api/type/MonoTouch.UIKit.UIPageViewController/ I need to connect the previous and next views, using UIPageViewControllerDataSource.GetNextViewController and UIPageViewControllerDataSource.GetPreviousViewController from the UIPageViewControllerDataSource delegate, but I'm not clear where should I do that, nor I see any where to do that using the storyboard designer.
The idea to use the UIPageViewController is to add a quick tutorial of 4 pages before advancing to another View, at the last view of the UIPageViewController there would be a button that calls the segway to the next page.
You can add a UIPageViewController in the Storyboard. Configure the style in the Properties window => Widget(Navigation, Transition Style, Spine Location).
Then you can add the DataSource in the corresponding CS file. Add the firstly shown UIViewController using: SetViewControllers(new UIViewController[] { viewController }, UIPageViewControllerNavigationDirection.Forward, true, null);. Please notice that when you set the Spine Location to Mid, this array should contain at least two UIViewControllers. If not, add just one.
but I'm not clear where should I do that, nor I see any where to do
that using the storyboard designer.
When you want to navigate to the next page, the event GetNextViewController() will fire(i.e. first page - second page). And you should return which Controller will show in this event. Also GetPreviousViewController() means which Controller you want to show when navigate back. Here is my sample:
public class MyPageViewDataSource : UIPageViewControllerDataSource
{
List<UIViewController> pageList;
public MyPageViewDataSource(List<UIViewController> pages)
{
pageList = pages;
}
public override UIViewController GetNextViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = pageList.IndexOf(referenceViewController);
if (index < pageList.Count - 1)
{
return pageList[++index];
}
else
{
//when navigate to the last page, return null to disable navigate to next.
return null;
}
}
public override UIViewController GetPreviousViewController(UIPageViewController pageViewController, UIViewController referenceViewController)
{
var index = pageList.IndexOf(referenceViewController);
if (index == 0)
{
//when navigate to the first page, return null to disable navigate to previous.
return null;
}
else
{
return pageList[index - 1];
}
}
}
At last set your UIPageViewController's DataSource to this:DataSource = new MyPageViewDataSource(pageList);
I am creating an application for Xamarin.android in c# and ran into an odd problem.
I use a wrapper class which inflates a view. After that I add the view to a linearlayout with AddView. Sometimes the gui doesn’t update for no apparent reason. The Elements aren’t added. When I call the task button (I don’t know if that’s the right name – the button left of the home button) and then navigate back to my app the elements will appear. I read somewhere that you could use Invalidate() to update the gui but that does not seem to work.
Any suggestions are highly appreciated.
This is my constructor of the wrapper class:
public BoxDisplay(MainActivity activity, Product product, ViewGroup root)
{
View = activity.LayoutInflater.Inflate(Resource.Layout.BoxItem, root, false);
shelf = View.FindViewById<TextView>(Resource.Id.Shelf);
name = View.FindViewById<TextView>(Resource.Id.Name);
barcode = View.FindViewById<TextView>(Resource.Id.Barcode);
Stock = View.FindViewById<EditText>(Resource.Id.Stock);
typ = View.FindViewById<TextView>(Resource.Id.type);
this.activity = activity;
SetProduct(product);
Stock.ClearFocus();
Stock.Click += (o, arg) =>
{
Stock.SelectAll();
};
Stock.EditorAction += EditorAction;
}
and here I add the view long after the activity was created:
private void GenerateView(List<Product> products)
{
var boxlist = FindViewById<LinearLayout>(Resource.Id.BoxList);
foreach (var product in products)
{
var box = new BoxDisplay(this, product, boxlist);
boxDisplays.Add(box);
box.EnableEditText(false);
boxlist.AddView(box.View);
box.View.Click += (sender, e) => { box.UpdateBox(); };
}
}
A couple of ideas more than an answer, and maybe could be bad practice, but I do not run into your issue :
I usually add programmatically created Views in OnViewCreated (Fragment) / OnCreate(Activity)
Make sure your LinearLayout has a parent. It must be added to a parent view (root Layout, another Layout, a scrollview, ...).
Make sure you do not have a blocking, manually launched thread involving UI update (even through RunOnUiThread), which could be unlocked by activity recreation.
Make sure your view isn't "flattened" by another existing one in your layout (for example having a view matching parent size in both dimensions)
Beside the fact that it won't display your layout, is your application running properly (no permanent freeze) ?
I have a layout that I want to show as a popup window (used as a custom options menu) while in a fragment of a viewpager. Therefore, when the "Options" button is clicked, I do the following:
public void onOptionsButtonClicked(int buttonHeight)
{
LayoutInflater optionsLayoutInflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
int popupWidth = ViewGroup.LayoutParams.MatchParent;
int popupHeight = ViewGroup.LayoutParams.WrapContent;
View layout = optionsLayoutInflater.Inflate(Resource.Layout.TrackOptions, null);
int popupYOffset = (85 + buttonHeight) * -1;
var popup = new PopupWindow(context);
popup.Focusable = true;
popup.Width = popupWidth;
popup.Height = popupHeight;
popup.ContentView = layout;
popup.SetBackgroundDrawable(new BitmapDrawable());
popup.OutsideTouchable = true;
popup.ShowAsDropDown(view, 0, popupYOffset);
}
And this works as I want, visually that is. Meaning, I click the button and I do see the layout popup as a popup window with all of my options. HOWEVER, none of the buttons work. I put a breakpoint in the class that should be associated the the layout and noticed that onCreateView never gets called, therefore, none of the buttons and associated click event handlers are ever wired up. So, I know why it is not working. However, I don't know how to fix it. I think it is because, while I inflate the view, I am never actually creating the fragment. I have done fragementmanager transactions to replace a fragment in other parts of my project and I know that would probably do it, however, this is a different case as I am trying to do a popup window.
Thanks!
Mike
Fragment is attach in activity so you can try it in onActivityCreated(Bundle) method
i start using xamarin to create ios software.
in mainstoryboard, i navigate PageMain view controller to PageTo view controller.
I want navigate from PageMain view controller to PageTo view controller. i use this code and did not auto navigate:
var storyBoard = UIStoryboard.FromName ("MainStoryboard", null);
storyBoard.InstantiateViewController ("PageTo");
tried this one too but also not auto navigate :
PageMainViewController *viewController = segue. PageToViewController;
viewController.delegate = self;
tried this one too but also not auto navigate :
UIViewController pageto = new PageTo ();
pageto.Transition (PageMain, PageTo);
i know it, it easier use button to create push seque to PageTo view controller, but i did not want it.
please help me.
Another thing that you can do is push to another view controller...
AssignCaseController assignCaseController = this.Storyboard.InstantiateViewController("AssignCaseController") as AssignCaseController;
if (assignCaseController != null)
{
assignCaseController.CaseID = GetCurrentCaseID();
this.NavigationController.PushViewController(assignCaseController, true);
}
I hope this helps, I just had to do the same.
// Clear your notifications and other things
// Show the controller
// I am assuming your are using storyboards based in your use of Handle. I am also assuming the identifier in your storyboard for this is set to "WebViewController and its a custom subclass of UIViewController named WebViewController.
UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null); // Assume the name of your file is "MainStoryboard.storyboard"
var webController = Storyboard.InstantiateViewController("WebViewController") as WebViewController;
// ... set any data in webController, etc.
// Make it the root controller for example (the first line adds a paramater to it)
webController.CaseID = int.Parse(notification.UserInfo["ID"].ToString());
this.Window.RootViewController = webController;
// Note* the "Window" should already be set and created because the app is running. No need to remake it.
I've a child view (SharedView) that's shared between two parent views so I add it to each parent view through a Region like this
<StackPanel>
<ContentControl cal:RegionManager.RegionName="SharedViewRegion" />
</StackPanel>
In the parent view's ViewModel I register the child view like this
regionManager.RegisterViewWithRegion("SharedViewRegion", typeof(SharedView));
When I run the application if I open only one of the parent views it works as expected but if I open the two parent views then I get the following exception
An exception occurred while creating a region with name
'SecondRegion'. The exception was: System.InvalidOperationException:
Specified element is already the logical child of another element.
Disconnect it first.
I've been googling and this is the closer solution I found to my problem InvalidOperationException occurs when the same view instance is added to multiple ContentControl regions
But I'm using the prism navigation feature so I'm instancing the parent view like this
regionManager.RequestNavigate("ModuleRegion", new Uri("ParentView1", UriKind.Relative));
Can someone help me to solve this?
Try doing the following:
Add a name to the ContentControl that is hosting the region
Now you have to remove the region's content before leaving the parent view so in the ViewModel add the following code to the OnNavigatedFrom method
public void OnNavigatedFrom(NavigationContext navigationContext)
{
ParentView.MyContentControl.Content = null;
}
Note: You can access the parent view importing it in your ViewModel.
Now you need to add the content to the region by hand because you removed it before leaving the region. Here's the code
public void OnNavigatedTo(NavigationContext navigationContext)
{
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
Note: In this method you must add some workaround because the first time you open this view you'll get a System.InvalidOperationException because PRISM will try to add the SharedView to MyContentControl.
Here's a possible workaround
bool isFirstTime = true;
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (isFirstTime)
{
isFirstTime = false;
return;
}
SharedView view = (SharedView)ServiceLocator.Current.GetInstance(typeof(SharedView));
ParentView.MyContentControl.Content = view;
}
You have to do the same work in all parent views that share the SharedView
I hope this help you
You should directly add view to region instead of view discovery for this case. Give unique names to your parent views (you can use a counter to make up a unique name).
var region = this.regionManager.Regions["SharedViewRegion"];
var viewName = string.Format("SharedView{0}", this.countParent+1);
//Only add if it doesn't exist
var view = region.GetView(viewName);
if (null==view)
{
//Use container to get new instance of the parent view.
view = this.container.Resolve<SharedView>();
region.Add(view, name);
//Increment the counter as you added a new parent view
this.countParent++;
}
//Now activate the view
region.Activate(view);