monotouch - set current view - xamarin.ios

I have a UISegmented control, which act as a tabs.
When a tab is clicked I wanted to change the view to another page.
Essentially at the moment I have two views each have their own segment control which switches from one view to the other and vice versa.
If I use PresentModalViewController I continually adding the view to the stack each time the user changes the uiSegment which isn't good.
I want to set the current view to a new view.
but at the same time ensure that if I call dismiss I can do back a view
My applicantion is structure like so:
[Main Window] --> [VC shows table of items] -->
|--> [VC with Segment control View 1]
| (swap between)
|--> [VC with Segment control View 2]
so in both the views I would have for instance:
View1:
partial void Tabs_Changed (UISegmentedControl sender)
{
if( btnTabs.SelectedSegment == 1) {
View2 view2 = new View2(); //load view 2
this.PresentModalViewController(view2,false);
}
}
View 2:
partial void Tabs_Changed (UISegmentedControl sender)
{
if( btnTabs.SelectedSegment == 0) {
View1 view1 = new View1(); //load view 1
this.PresentModalViewController(view1,false);
}
}
But this is making the app unpredictable presumably cause I'm constantly stacking up views?
Please help.
Jason

Using the PresentModalViewController() method really is not the way to go. You are indeed stacking multiple views which is a bad thing to do.
What I would do is have one view, with the UISegmentedControl and another 2 subviews added to it (use the AddSubview(View) method to do that).
On your viewDidLoad method, create the veiws and hide the second one using:
view1 = new UIView(){BackgroundColor = UIColor.Green};
view2 = new UIView(){Hidden = true, BackgroundColor = UIColor.Red};
this.AddSubview(view1);
this.AddSubview(view2);
Now, in your method:
partial void Tabs_Changed (UISegmentedControl sender)
{
bool v1Visible = btnTabs.SelectedSegment == 0);
view1.Hidden = !v1Visible;
view2.Hidden = v1Visible;
}
I havent run the code, but it should work. Hope this helps.

This might help. switching between uiviewcontrollers without a UI navigator control
but doesnt need an UI to switch, call the App deletegate from your viewcontroller with
something like
var appDelegate = (AppDelegate) UIApplication.SharedApplication.Delegate;
appDelegate.switch();

Related

Prevent collapsingToolbar from expanding for certain fragments

I'm trying to prevent a CollapsingToolbar from expanding for one of my fragments.
Currently, when I use the method setExpanded(), I am able to see the toolbar in its collapsed state.
appBarLayout.setExpanded(false, true);
However, the collapsingToolbar is still expandable, which is not what I want. I want the collapsingToolbar to not be expandable for this particular fragment.
In other words, I want the collapsingToolbar to behave and look like a regular toolbar (i.e. "not expandable") for this particular fragment only.
I am using Mike Penz's Material Drawer. The code below shows the relevant code, with notes indicating what I have tried.
private void buildDrawer(){
Drawer drawer = new DrawerBuilder()
.withActivity(this)
.withFullscreen(true)
.withTranslucentStatusBar(true)
.withToolbar(toolbar)
.withAccountHeader(accountHeader)
.addDrawerItems(
item1,
item2,
new DividerDrawerItem(),
item3,
item4
)
.withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() {
#Override
public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment f = new Fragment();
switch (position) {
//other cases not shown
case 2:
f = new LocateFragment();
appBarLayout.setExpanded(false, true);
//Adding the following three lines don't work - causes the toolbar to be unscrollable, but in its expanded form
//AppBarLayout.LayoutParams p = (AppBarLayout.LayoutParams)collapsingToolbarLayout.getLayoutParams();
//p.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP);
//collapsingToolbarLayout.setLayoutParams(p);
//toolbar.setCollapsible(false); doesn't work either
collapsingToolbarLayout.setTitle("Locate Events");
setInvisibleAddPhotoFab();
break;
//other cases not shown
}
fragmentTransaction.replace(R.id.frame_fragments, f);
fragmentTransaction.commit();
return false; //close drawer onclick
}
})
.build();
loadBackdrop();
}
This is what I want - I want the toolbar to be unexpandable:
Currently, however, it is still expandable, so the image below is NOT what I want:
UPDATE:
I was able to collapse the toolbar and prevent it from expanding like so (code below), but I've run into another issue - the title no longer appears on the collapsingToolbar when I set it like so: collapsingToolbarLayout.setTitle("string");
appBarLayout.setExpanded(false, true);
int px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)appBarLayout.getLayoutParams();
lp.height = px;
appBarLayout.setLayoutParams(lp);
I was able to resolve this problem using the following (as shown here by #DanielPersson):
collapsingToolbarLayout.setTitleEnabled(false);
toolbar.setTitle(title);

UWP page state manage

I want to learn how to manage the state of a page between navigation.
for example a navigate onto page1 and then i navigate to page2, but when i navigate back to page1, the UI elements must already be there with the same data as before and they must not be re-initialized or data must not be binded again by the compiler.
Also what I can do to manage state of whole application such that, I terminate the app and then when i launch it next time, the same state is already there as last time. can i apply it on whole application? or what if I only want to apply it on a few pages? any help would be appreciated thanks.
or example a navigate onto page1 and then i navigate to page2, but when i navigate back to page1, the UI elements must already be there with the same data as before and they must not be re-initialized or data must not be binded again by the compiler.
For this question, you may use UIElement.CacheMode property and Frame.CacheSize property. CacheSize property sets the number of pages in the navigation history that can be cached for the frame, and CacheMode property sets a value that indicates that rendered content should be cached as a composited bitmap when possible.
As we know, an UWP app default using a rootFrame for displaying several pages, we just use Navigation method to change the content in the frame. You can see this in the OnLaunched(LaunchActivatedEventArgs e) method of a blank UWP app. But how to implement cache function? For example, your app has two page and one root frame. You can define CacheSize property in your OnLaunched(LaunchActivatedEventArgs e) method for example:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
...
// Ensure the current window is active
rootFrame.CacheSize = 2;
Window.Current.Activate();
}
Then in your two pages's constructed functions enable CacheMode property for example:
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Enabled;
}
Also what I can do to manage state of whole application such that, I terminate the app and then when i launch it next time, the same state is already there as last time. can i apply it on whole application?
For this question, you will need to save the page state in the OnSuspending(object sender, SuspendingEventArgs e) method using Frame.GetNavigationState method, and you can save this state into the app's local settings. For example:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
Frame rootFrame = Window.Current.Content as Frame;
string navstate = rootFrame.GetNavigationState();
var localSettings = ApplicationData.Current.LocalSettings;
localSettings.Values["nav"] = navstate;
deferral.Complete();
}
And how to retrieve this informaton? You can override your OnLaunched(LaunchActivatedEventArgs e) method, and at first you will need to judge how is your app be closed last time, by user, or by system using ApplicationExecutionState enumeration, for example like this:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
//#if DEBUG
// if (System.Diagnostics.Debugger.IsAttached)
// {
// this.DebugSettings.EnableFrameRateCounter = true;
// }
//#endif
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (rootFrame.Content == null)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
//rootFrame.Navigate(typeof(MainPage), e.Arguments);
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated ||
e.PreviousExecutionState == ApplicationExecutionState.ClosedByUser)
{
object value;
var localSettings = ApplicationData.Current.LocalSettings;
if (localSettings.Values.TryGetValue("nav", out value))
{
rootFrame.SetNavigationState(value as string);
}
else
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
}
else
{
rootFrame.Navigate(typeof(MainPage), e.Arguments);
}
}
// Ensure the current window is active
rootFrame.CacheSize = 2;
Window.Current.Activate();
}
But be aware that when an app is closed, next time you launch this app, the UI elements will be re-initialized, this function can only navigate to the page when the last time you close your app, but the data in that page will be lost. But you can also save the data to the local settings and when you navigate to the page, set the value to those UI elements.

UICollectionview SelectItem programmatically not changing background

I have a UICollectionView with images. The user can select (multiselect) the images. When the user taps a single image, everything works fine. The SelectedBackgroundView is visible and on tap again, the normal image is visible.
But my problem is, I have a option for the user "Select all". In that i want to select all items programmatically. With following code:
for (int i = 0; i < CollectionView.NumberOfItemsInSection(0); i++)
{
var ip = NSIndexPath.FromItemSection(i, 0);
CollectionView.SelectItem(ip, false, UICollectionViewScrollPosition.None);
}
The following method returns the correct number for the selected items:
var number = CollectionView.GetIndexPathsForSelectedItems().Length;
But the UI is not changing to the SelectedBackgroundView.
Can anyone help me? Thanks.
Calling SelectItem does not cause the display to be updated; it just changes the Selected property of the UICollectionViewCell therefore updating the selected index set in the collection view.
What I do is override the Selected property of my UICollectionViewCell implementation and adjust the UI at that point:
public class MyCell : UICollectionViewCell
{
// ...
public override bool Selected
{
get { return base.Selected; }
set
{
base.Selected = value;
// change the state of the selected background
imageBackground.Image = LoadAnImage(value ? "BackgroundOn" : "BackgroundOff");
}
}
}
This way ensures that the UI is updated at all possible points when the selected state of the cell changes, either by user interaction or programmatically calling SelectItem or DeselectItem on the collection view.
I do not personally use the SelectedBackgroundView property on a cell (I do my own layering, most of the time), but you may have to manually bring that view to the front yourself in a similar Selected property override.

Monotouch Dialog - Reload data list after modifying row detail not updating list when returning

When an item is clicked, a details view controller is opened that makes some webservice calls to save back the data if the user clicks save (this works fine). The issue I have is after saving the data when the user clicks back to go back to the list, the list item (ie if the name was updated) is not updated. So, perhaps the user changes the name and so the list prior should be updated, I'd like to update the value without having to make another webservice call to repopulate.
I am calling this.ReloadData() when the view appears, but this never seems to update the list names. I've checked workouts which is a list of objects, and it has been modified by the detail controller, however the reload data doesn't seem to do anything.
How do I refresh the data in the case that another controller has modified the list of objects.
public override void ViewWillAppear (bool animated)
{
InvokeOnMainThread (delegate {
this.ReloadData();
}); // call reload on main thread
base.ViewWillAppear (animated);
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
workouts = manager.GetWorkouts(Helpers.User.Auth);
var sections = new Section();
var root = new RootElement("Workouts"){sections};
foreach (var wo in workouts) {
var tempwo = wo;
var wodetail = new RootElement(wo.Name, delegate {
var W = new WorkoutViewModel(tempwo);
var p = new WorkoutDetailController(W, tempwo);
return p;
});
sections.Add (wodetail);
}
Root = root;
}
It seems that when you assign the value of an element from a custom object like so:
AccountDetailsSection = new Section ("Details"){
new StringElement ("Code:", _Account.Code),
new StringElement ("Name:", _Account.Name) };
You get the desired result but the element loses the pointer back to the property of the object. So if you change the value of the property later for example:
_Account.Code = "xxx";
Reloading the data doesn't do anything. You should manually update the value like so:
(AccountDetailsSection.Elements [0] as StringElement).Value = "xxx";
this.Root.Reload ( AccountDetailsSection, UITableViewRowAnimation.Fade);
Hope this Helps.

SharePoint show Ribbon with multiple webparts on page

I've created a sharepoint page that has an xslt webpart and a 2nd webpart that is unrelated to the question
When we add this second webpart the ribbon bar is hidden and you have to click the webpart to get the ribbon bar shown again.
Clicking the webpart isn’t something we can ask from our users so I’m trying to get the ribbon bar visible at all times with the context of our xslt listview webpart.
When searching for this problem I found out that when you search for this hidden ribbon behavior with reflector in the SharePoint source code it seems this is behavior that is designed by Microsoft as the example below shows:
public override bool Visible {
get {
if (!this.SingleWebPartPresentOnPage)
return false;
else
return base.Visible;
}
}
Someone with same problem but no solution: http://www.glynblogs.com/2011/02/list-view-selector-missing-with-multiple-web-parts-in-sharepoint-2010.html
Is it possible to force the ribbon bar to visible with server side code or can I call the javascript code that is being used when I click the webpart to show the ribbon bar?
I think it should be possible with javascript because if you click the xslt webpart the ribbon is visible but i can't reproduce the code thats being executed.
you can use JavaScript to reselect the XSLTListViewWebPart, that the ribbon appears again.
$(document).ready(function() {
var target = document.getElementById("MSOZoneCell_WebPartWPQ2");
if(target != null) {
var fakeEvent = new Array();
fakeEvent["target"] = target;
fakeEvent["srcElement"] = target;
WpClick(fakeEvent);
}
});
Below Javascript worked for me!!
<script>
setTimeout(function() {
var elem = document.getElementById("MSOZoneCell_WebPartWPQ4");
if(elem != null) {
var dummyevent = new Array();
dummyevent["target"] = elem;
dummyevent["srcElement"] = elem;
WpClick(dummyevent);
}
}, 100);
</script>
In the above script the MSOZoneCell_WebPartWPQ4 being my list view web part
A great solution is to grab the contextualInfo for the main webpart on your view page.
public class MyView : WebPart, IWebPartPageComponentProvider
{
protected override void CreateChildControls(){.............}
public WebPartContextualInfo WebPartContextualInfo
{
get
{
// get default current view webart (WebPartWPQ2)
ListViewWebPart listView = this.WebPartManager.WebParts
.OfType<ListViewWebPart>().FirstOrDefault();
// use reflection to get non-public member containing contextualinfo
var t = listView.GetType();
WebPartContextualInfo oViewInfo = (WebPartContextualInfo)t.InvokeMember("Microsoft.SharePoint.WebControls.IWebPartPageComponentProvider.WebPartContextualInfo", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.GetProperty, null, listView, new object[] { });
return oViewInfo;
}
}
protected override void OnPreRender(EventArgs e)
{
SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
// Ensure ribbon exists.
if (ribbon != null)
{
// Load dependencies if not already on the page.
ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Ribbon.js", false, true);
}
base.OnPreRender(e);
}
}
Below a version using SharePoint's Script On Demand instead of 100ms timeout or jquery. I think thats more solid, cuz it is exactly executed after the ribbon is initialized.
SP.SOD.executeOrDelayUntilScriptLoaded(function () {
//using setTimeout to ensure it will be executed after the code of sp.ribbon.js has done its initialization
setTimeout(function () {
//try to focus the default webpart so the ribbon will show
var elem = document.getElementById("MSOZoneCell_WebPartWPQ2");
if (elem != null) {
var dummyevent = new Array();
dummyevent["target"] = elem;
dummyevent["srcElement"] = elem;
WpClick(dummyevent);
}
}, 0);
}, "sp.ribbon.js");
Similar to Thorstens solution, I use jQuery to fire the WpClick function on the mouseenter event. This approach also handles the issue where the Full Toolbar freaks out when a user first enters a page and tries to use one of the menus. You can trap the event bubble for any number of web parts on the page if desired. For example:
$("body").on("mouseenter","#MSOZoneCell_WebPartWPQ2,#MSOzoneCell_WebPartWPQ3, . . . etc.",function() {
WpClick(event);
});
Where "body" could be any parent element you want that contains the web parts to auto select when hovering.
When only one web part is of concern, or for optimal performance on large pages you could also set the event directly on the zone.
$("#MSOZoneCell_WebPartWPQ2").attr("onmouseenter","WpClick(event)");
or if jQuery is not available
var el = document.getElementById("MSOZoneCell_WebPartWPQ2");
if(el != null) {
el.setAttribute('onmouseenter','WpClick(event);');
}
Optionally, you can still force the Ribbon to appear after the page loads and before a user hovers by triggering the event manually. Just include the appropriate code after attaching the event above. e.g. using jQuery
$("#MSOZoneCell_WebPartWPQ2").mouseenter();

Resources