Sync Navigation Drawer menu on Back stack - c#

I followed the https://github.com/jamesmontemagno/Xam.NavDrawer example and Im able to successfully use the drawer layout with fragments(infact nested fragments with view pager). I have one issue, when I click the back button the navigation drawer menu item on the left side is not synced with the fragment that is shown.
This is how I navigate to other fragments
SupportFragmentManager.BeginTransaction().Replace(Resource.Id.content, fragment).AddToBackStack(fragment.Name).Commit();
I tried the OnAttachFragment method, but its not called on back stack. I also tried the SupportFragmentManager BackStackChanged method, but I could not get the current fragment that is in the view to update the navigation drawer menu title.

I had the same issue and couldn't find any solution as well. Although this question is kinda old, my solution may help someone. I'm not sure if it's the best solution but it works for me.
So first you need to add variable to store ID of previously checked item:
private int previousItemChecked;
set it initially to your default checked item:
if (savedInstanceState == null) {
selectItem(0);
previousItemChecked = 0;
}
then edit the fragment transaction so that the transaction title in backstack contains position of the previously checked item converted to string and after the transaction is done set the variable previousItemChecked to the currently checked item id:
fragmentManager.beginTransaction().replace(R.id.content_frame, selectedFragment).addToBackStack(""+previousItemChecked).commit();
previousItemChecked = mDrawerList.getCheckedItemPosition();
and finally in method onBackPressed you need to get the string previously assigned to fragment transaction, parse it to int and update the drawer according to the obtained id:
#Override
public void onBackPressed() {
if(fragmentManager.getBackStackEntryCount() > 0) {
String title = fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1).getName();
int pos = Integer.parseInt(title);
previousItemChecked = pos;
mDrawerList.setItemChecked(pos, true);
}
super.onBackPressed();
}
I took the code from my app created in Android Studio so it isn't for Xamarin but if you update the code it should work with it too. What's important here is the idea how it's done. I hope the answer is understandable and will help someone.

I had the same issue and I solved like this:
Inside selectItem we are passing the position Item;
So if position is 0 (or whatever is fragment we want it appears as first indipendently from it's position on the menu) we have to avoid to save the first transaction. So...
private void selectItem(position){
//...
if (position != 0)
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.AddToBackStack(fragment.Name)
.Commit();
}
else
{
SupportFragmentManager.BeginTransaction()
.Replace(Resource.Id.content_frame, fragment)
.Commit();
}
}

Managed to achieve this by injecting a DestinationChangedListener into the NavController like this:
NavController navController.addOnDestinationChangedListener(this);
and then:
#Override
public void onDestinationChanged(#NonNull NavController controller,
#NonNull NavDestination destination,
#Nullable Bundle arguments) {
NavigationView navigationView = findViewById(R.id.nav_view);
if(navigationView != null){
navigationView.setCheckedItem(destination.getId());
}
}

Related

Xamarin iOS: Downcasting segue.DestinationViewController Error

I set up a segue with a navigation controller in Xamarin.iOS. The first screen is just a ViewController that has a List of phoneNumbers, and adds to the list of phone numbers when a button is clicked. When a different button is clicked, I want to go to the CallHistory2 screen and display the list of phone numbers. However, I'm having trouble passing the List object to the second screen.
This is a method in the ViewController.cs class (the first screen)
public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// set the View Controller that’s powering the screen we’re
// transitioning to
var callHistoryContoller = segue.DestinationViewController as CallHistory2;
//set the Table View Controller’s list of phone numbers to the
// list of dialed phone numbers
if (callHistoryContoller != null) {
callHistoryContoller.PhoneNumbers = PhoneNumbers;
}
}
I get an error at this line
var callHistoryContoller = segue.DestinationViewController as CallHistory2;
Cannot convert type 'UIKit.UIViewController' to 'PortableAppTest.iOS.CallHistory2' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion PortableAppTest.iOS
When I change CallHistory2 to UITableViewController the error goes away, but then callHistoryController won't contain a reference to my CallHistory2 class (implements UITableViewController), but rather to a generic UITableViewController class.
How do I work around this issue? Any help is appreciated!
try following, It will work .
if ( segue.DestinationViewController.GetType() == typeof(CallHistory2))
{
CallHistory2 callHistoryContoller = (CallHistory2)segue.DestinationViewController
callHistoryContoller.PhoneNumbers = PhoneNumbers;
}
I hope this helps someone else as I ran into the same issue!
This is from Xamarins Multiscreen guide
https://developer.xamarin.com/guides/ios/getting_started/hello,_iOS_multiscreen/hello,_iOS_multiscreen_quickstart/
And the issue for me was that the app is called "PhoneWord" but the code for this Viewcontroller was using "Phoneword" as the namespace (notice the lower case W)
As soon as I set it correctly it worked as expected.

Always get a System.NotSupportedException when quitting a view after subscribing to OnPreDrawListener

I'm in the process of developing an app to display booking information via a client's API in Xamarin.Forms. They wanted a section to display several blocks of information, and that information is often styled with HTML tags (we have no control over how this data is returned). As a quick way to throw a view together to display this information, I created a view which consists of a series of individual webviews to display this information inside a scrollview.
To make this page look less horrendous, I have made a custom renderer for the webview that, when content has been loaded, will resize the view to encapsulate all of the content without scrolling.
Now the actual problem:
To achieve this, inside the custom renderer, in the OnAttachedToWindow() callback, get the webview's ViewTreeObserver and call the AddOnPreDrawListener() method to make use of the OnPreDraw() callback;
The resize is performed in that callback, if the ContentHeight is greater than 0 then it resized the page and returns true.
Then in the OnDetachedFromWindow() callback, I check that the VTO is alive, and unsubscribe from the OnPreDraw listener.
Simple, Right?
The page works exactly as expected when navigating to it, however when trying to navigate away, It ALWAYS throws this error:
System.NotSupportedException: Unable to activate instance of type ExtendedWebViewRenderer from native handle 0xbe9f6eac (key_handle 0xf883b13).
The error does not occur if I remove all the ViewTreeObserver event subscription stuff, but if I do that, then the page will not resize accordingly.
Could someone please point me in the right direction? I have included the brief source code for this. I would appreciate any help on this:
Source
public class ExtendedWebViewRenderer : WebViewRenderer, ViewTreeObserver.IOnPreDrawListener
{
ExtendedWebView _xwebView = null;
WebView _webView;
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged (e);
_xwebView = e.NewElement as ExtendedWebView;
_webView = Control;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
// Here assign a ViewTreeObserver to monitor when the view's children need to be resized.
var _vto = _webView.ViewTreeObserver;
_vto.AddOnPreDrawListener(this);
}
protected override void OnDetachedFromWindow()
{
var _vto = _webView.ViewTreeObserver;
if (_vto.IsAlive)
{
_vto.RemoveOnPreDrawListener(this);
}
this.Control.ClearCache(true);
this.Control.ClearAnimation();
this.Control.StopLoading();
base.OnDetachedFromWindow();
}
public bool OnPreDraw()
{
if (_webView != null)
{
// We get the view's content height and apply that to the parent view.
int contentHeight = _webView.ContentHeight;
if (contentHeight != 0 && contentHeight != _xwebView.HeightRequest)
{
int desiredHeight = contentHeight + _webView.PaddingBottom + _webView.PaddingTop;
_xwebView.HeightRequest = desiredHeight;
}
}
return true;
}
}

Why is this String remaining null?

The controls on my WebPart are not being created because the condition:
if (this.dpsvisWebPart != null && this.dpsvisWebPart.CustomTitleProp != null)
...fails due to CustomTitleProp being null. Yet it is declared in the WebPart's constructor and should not be null.
Here is the code:
namespace DirectPaymentSectionsWebPart.DPSVisualWebPart
{
[ToolboxItemAttribute(false)]
public class DPSVisualWebPart : WebPart
{
// Text
[WebBrowsable(true),
Category("Financial Affairs Forms"),
Personalizable(PersonalizationScope.Shared),
WebDisplayName("WebPart Title Text")]
public string CustomTitleProp { get; set; }
// Checkboxes
[WebBrowsable(true),
Category("Financial Affairs Forms"),
Personalizable(PersonalizationScope.Shared),
WebDisplayName("Generate Section1")]
public bool CheckboxGenSection1 { get; set; }
. . .
// Visual Studio might automatically update this path when you change the Visual Web Part project item.
private const string _ascxPath = #"~/_CONTROLTEMPLATES/DirectPaymentSectionsWebPart/DPSVisualWebPart/DPSVisualWebPartUserControl.ascx";
. . .
protected override void CreateChildControls()
{
//base.CreateChildControls(); // added this 5/28/2015 <= commented out, because it is being called over and over again
DPSVisualWebPartUserControl control = Page.LoadControl(_ascxPath) as DPSVisualWebPartUserControl;
//DPSVisualWebPartUserControl control = Page.LoadControl(siteUrl) as DPSVisualWebPartUserControl;
if (control != null)
{
control.dpsvisWebPart = this;
}
Controls.Add(control);
}
} // class DPSVisualWebPart
So why might it be that CustomTitleProp is null at the previously noted juncture? Here that code is with more context:
public partial class DPSVisualWebPartUserControl : UserControl
{
public DPSVisualWebPart dpsvisWebPart { get; set; }
System.Web.UI.ScriptManager scriptMgr;
. . .
protected void Page_Load(object sender, EventArgs e)
{
base.OnPreRender(e);
if (System.Web.UI.ScriptManager.GetCurrent(this.Page) == null)
{
scriptMgr = new System.Web.UI.ScriptManager();
scriptMgr.EnablePartialRendering = true;
this.Controls.AddAt(0, scriptMgr);
}
if (this.dpsvisWebPart != null && this.dpsvisWebPart.CustomTitleProp != null)
{
lbl_Title.Text = String.Format("<h1>{0}</h1>", this.dpsvisWebPart.CustomTitleProp.ToString());
. . .
UPDATE
Under the heading of "Adventures in Folder Diffing" comes this:
The legacy project contains a file named FileListAbsolute.txt with this content:
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.pdb
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\Microsoft.Sharepoint.Sandbox.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\ResolveAssemblyReference.cache
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.pdb
The new project contains a file of the same name with this content:
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.pdb
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\bin\Debug\Microsoft.Sharepoint.Sandbox.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\ResolveAssemblyReference.cache
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentSectionsWebPart\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.pdb
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\obj\Debug\ResolveAssemblyReference.cache
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.dll
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\bin\Debug\DirectPaymentSectionsWebPart.pdb
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\bin\Debug\Microsoft.Sharepoint.Sandbox.dll
C:\Projects\DirectPaymentWebForm\DirectPaymentSectionsWebPart\obj\Debug\DirectPaymentSectionsWebPart.pdb
So I wonder: should I remove the first six entries from the new project's file, which reference the legacy project? Or will doing so cause Pearl Harbor III to erupt?
UPDATE 2
I tried this:
...and it turns out that there already is a ScriptManager by this point (Page_Load()) event, and that it already has EnablePartialRendering set, and so this code is moot. The question still is, though, why in Hec Ramsay is CustomTitle null here:
if (this.dpsvisWebPart != null && this.dpsvisWebPart.CustomTitleProp != null)
...which comes right after the (about-to-be-removed-because-its-moot) code in the Page_Load() event?
UPDATE 3
Alright (or is it "all right"?) - which is, at any rate, a funny thing to say when obviously not everything is all right, but then again, it's Friday, so it's okay...anyway:
I wondered: "maybe 'CustomTitleProp' is null because the Web Editor (after all, that's where CustomTitleProp lives) is not being created/rendered. And sure enough, when I went to "Edit Web Part" the Web Part Editor wouldn't even display, and I again got blasted with the red-hot, reeking-breath Sharepoint mother-in-law thus:
"Web Part Error: A Web Part or Web Form Control on this Page cannot be displayed or imported. The type DirectPaymentSectionsWebPart.DPSVisualWebPart.DPSVisualWebPart, DirectPaymentSectionsWebPart, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bbf47e850237632c could not be found or it is not registered as safe."
Alas and anon, it is back to the coding board again! For forsooth, why is my WebPartEditor not displaying?
(sorry, I've been reading "A Connecticut Yankee in King Arthur's Court" of late).
I think you have to just register your dll as safe in Web.config.
Once I entered a value in the Web Part Editor for this, the problem went away. But the question still remains, how to prevent this prior to adding a value via the Web Part Editor? I see no way to give this a default value; I assume even a space (" ") would do the trick...
Could I do something like:
[WebBrowsable(true),
Category("Financial Affairs Forms"),
Personalizable(PersonalizationScope.Shared),
WebDisplayName("WebPart Title Text")]
public string CustomTitleProp { call a custom method to return a space or something; set; }
?

ObjectListView editing doesn't work

I'm trying to create a simple listbox with ObjectListView (WinForm, C#). The goal is to have a single value (a double) and a check box.
I want to be able to edit the double value by Single Click, so here are the relevant lines of code from my MyWindow.Designer.cs file (i've left out the default values for efficiency):
this.olvDepths = new BrightIdeasSoftware.ObjectListView();
this.olvColumn1 = ((BrightIdeasSoftware.OLVColumn)(new BrightIdeasSoftware.OLVColumn()));
...
this.olvDepths.CellEditActivation = BrightIdeasSoftware.ObjectListView.CellEditActivateMode.SingleClick;
this.olvDepths.CheckBoxes = true;
this.olvDepths.CheckedAspectName = "IsDefault";
this.olvDepths.FullRowSelect = true;
//
// olvColumn1
//
this.olvColumn1.AspectName = "Depth";
this.olvColumn1.Text = "";
this.olvColumn1.IsEditable = true;
I then create a list of my class (ShieldingEntry) and use the olvDepths.SetObjects() with the list. My ShieldingEntry class looks like this:
public class ShieldingEntry
{
public double Depth { get; set; }
public bool IsDefault { get; set; }
}
However, when I click the field, it doesn't go into edit mode. I've also tried the DoubleClick, SingleClickAlways, and F2Only modes and they don't work either.
The Checkbox works fine.
************** I have additional information *********************
I've pulled and build the ObjectListView source, so I could step through it.
I put a breakpoint in the OLV StartCellEdit method and it gets called and appears to setup and select the control appropriately. It just never appears...
As I noted in the comments on the answer below, I've got this control on a tabbed dialog, and if I switch to another tab, then back, the control works fine.
What am I missing?
I've used ObjectListView before, and here is what I had to do:
Handle the CellEditStarting event. This event is raised when the cell goes into edit mode. Since OLV doesn't really have built-in editors, you have to make your own. Then handle the CellEditFinishing event to validate the data before putting it back into your model.
So first, handling the CellEditStarting event:
private void objlv_CellEditStarting(object sender, CellEditEventArgs e)
{
//e.Column.AspectName gives the model column name of the editing column
if (e.Column.AspectName == "DoubleValue")
{
NumericUpDown nud = new NumericUpDown();
nud.MinValue = 0.0;
nud.MaxValue = 1000.0;
nud.Value = (double)e.Value;
e.Control = nud;
}
}
This creates your editing control. If you want to make sure the size is right, you can set the size of the control (in this case a NumericUpDown) to the cell bounds using e.CellBounds from the event object.
This will show the editor when you click in the cell. Then you can handle the editor finished event to validate the data:
private void objlv_CellEditFinishing(object sender, CellEditEventArgs e)
{
if (e.Column.AspectName == "DoubleValue")
{
//Here you can verify data, if the data is wrong, call
if ((double)e.NewValue > 10000.0)
e.Cancel = true;
}
}
I don't think handling it is required, but its good practice to validate data from the user.
The editing control in the CellEditStarting event can be any control, even a user defined one. I've used a lot of user defined controls (like textboxes with browse buttons) in the cell editor.
[Edit]
I uploaded an example here dropbox link that seems to work. Might not be in the exact view as needed, but seems to do the job.
For anyone else with this problem. I had it specifically when trying to edit a 'null' value in a decimal? on the OLV on a tab page. Solution for me was to set UseCustomSelectionColors to 'False'. I didn't look elsewhere to see if it was reported as a bug. Seems like a bug.

silverlight, ListBox navigation to new page with object possible?

Hi all i have a listbox MainListBox where i add items to dynamically.
Now i want to navigate to DetialsPage.xaml.cs when i choose an item in the listbox.
where i can then display my info about the selected item.
private void SetListBox()
{
foreach (ToDoItem todo in itemList)
{
MainListBox.Items.Add(todo.ToDoName);
}
}
MainListBox_SelectionChanged ("Generated by visual studio 2010 silverlight for windows 7 phone)
// Handle selection changed on ListBox
private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// If selected index is -1 (no selection) do nothing
if (MainListBox.SelectedIndex == -1)
return;
// Navigate to the new page
NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative));
// Reset selected index to -1 (no selection)
MainListBox.SelectedIndex = -1;
}
in DetailsPage.xaml.cs is the next method. ("Generated by visual studio 2010 silverlight for windows 7 phone)
I'm aware that the below method does not do what i try.
// When page is navigated to set data context to selected item in list
protected override void OnNavigatedTo(NavigationEventArgs e)
{
string selectedIndex = "";
if (NavigationContext.QueryString.TryGetValue("selectedItem", out selectedIndex))
{
int index = int.Parse(selectedIndex);
DataContext = App.ViewModel.Items[index];
}
}
I would like to access the selectedIndex and call my methods of my object that is in the MainListbox
so Basicly:
Mainlistbox => select item => send that item to details page => details page access the item and call methods on the item (object)
I'm sure this is a basic question tough it seems hard to find any specifics on it. i would like to add that this is my first windows phone 7 app.
There are many ways you can pass an object from page to page:
serialize and deserialize like Dennis said, but this, although feasable, is not practical, unless you want to save the object in isolated storage and retrieve it later.
Place an object in the App.cs class, which is accessible to all pages. Set your object in the master page, retrieve it from the Details page.
Code to put in App.cs: MyObject selectedObject;
Code to put in MasterPage.cs: application.selectedObject = MainListBox.selectedItem;
Code to put in DetailsPage.cs: MyObject selectedObject = application.seletedObject;
You can set the Object in the DataContext of your LayoutRoot, but i don't have the code for that on top of my head.
The answer here is simple - you cannot directly pass an object to another page. You can serialize it to JSON or XML and then deserialize it on the target page, but the serialized item will still have to be passed as a parameter.
Instead of sending the selectedindex as a query string parameter you could send the ID for the object or similar, something that uniquely can identify the object.
Then in the details page you could fetch the correct object from the same datasource that the main list box get its data from (in your case "itemList" which could come from e.g. IsolatedStorage).
If itemList is instantiated and kept only within the main page then you won't be able to fetch the item by ID from the details page. So in that case you'd need to move the itemList to some static or app level storage.
HTH

Categories

Resources