Updating Monotouch.Dialog RootElement - c#

I have been following the code from the monotouch.dialog task sample app (http://docs.xamarin.com/ios/Guides/User_Interface/MonoTouch.Dialog/Elements_API_Walkthrough).
Something I dont seem able to work out, is when the user clicks the + button a new row to the table is added. The user touches this and navigates to another screen where they can enter information. Now, when they navigate back to the root view, I want the list of rootElements to be updated so the entered name is used instead of the default name 'connection'
How would I go about updating the text for each of the RootElements based upon what has been entered?
I hope that all makes sense!
-- code below.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
RootElement _rootElement = new RootElement ("To Do List"){ new Section()};
DialogViewController _rootVC = new DialogViewController (_rootElement);
// This adds an 'add' button to the root view controller, and handles by delegate,the push to a screen where you can add a new vCenter connection profile.
UIBarButtonItem _addButton = new UIBarButtonItem (UIBarButtonSystemItem.Add);
_rootVC.NavigationItem.RightBarButtonItem = _addButton;
_addButton.Clicked += (sender, e) => {
var connectionProfile = new connectionProfile{};
// See, on the line below, I add a default RootElement with the text New Connection.
var connectionProfileElements = new RootElement ("New Connection") {
new Section () {
// When they enter the name on the next line of code, I want to use this to update the New Connection text on the root screen.
new EntryElement ("Name", "Enter Connection Name", connectionProfile._connectionName),
new EntryElement ("VC Address", "Enter VC Address", connectionProfile._address),
new EntryElement ("VC Port", "Enter VC Port", connectionProfile._port),
new EntryElement ("Username", "Enter Username", connectionProfile._userID),
new EntryElement ("Password", "Enter Password", connectionProfile._password,true)}
};
_rootElement [0].Add (connectionProfileElements);
};
UINavigationController _nav = new UINavigationController (_rootVC);
window.RootViewController = _nav;
window.MakeKeyAndVisible ();
return true;
}
}
And :
public class connectionProfile
{
public connectionProfile ()
{
}
public string _connectionName { get; set; }
public string _address { get; set; }
public string _port { get; set; }
public string _userID {get; set; }
public string _password { get; set; }
}

Did you try this.ReloadData(); like this ?
public NameOfYourDialogViewController() : base (UITableViewStyle.Grouped, null)
{
Root = new RootElement ("test") {
new Section ("First Section"){
new StringElement ("Hello", () => {
new UIAlertView ("Hola", "Thanks for tapping!", null, "Continue").Show ();
}),
new EntryElement ("Name", "Enter your name", String.Empty)
},
new Section ("Second Section"){
},
};
}
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Do your stuff
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
this.ReloadData();
}
You can find other topic about that here.
EDIT
You forgot to say that this is located in your AppDelegate, which is not made for what you are doing.
At first, create a DialogViewController: Project > Add new file > MonoTouch > DialogViewController.
And in the different method I mentioned earlier, put the ReloadData() method.
These methods (ViewDidLoad, WillAppear and so on) are override methods from UIView.
AppDelegate exists to get data before your is launched, store static data for your app, and so on. It's completly different usage.
And for your AppDelegate, you should have this for example:
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
_window = new UIWindow (UIScreen.MainScreen.Bounds);
_controller = new NameOfYourDialogViewController();
_navigationController = new UINavigationController (_controller);
UIImageView splash = new UIImageView(_window.Bounds);
splash.Image = UIImage.FromFile("Default.png");
_window.AddSubview(splash);
_window.AddSubview(_navigationController.View);
_window.BringSubviewToFront(splash);
_window.MakeKeyAndVisible ();
// This is used to create a fadding effect in your splashscreen
UIView.Animate(1,
delegate { splash.Alpha = 0f; },
delegate {
_window.RootViewController = _navigationController;
splash.RemoveFromSuperview();
});
return true;
}

Related

Chartjs.Blazor Why IS Time Line Chart not rendering?

Trying to get a Line chart to render using ChartJS.Blazor but I'm just getting an unhandled exception which I can't seem to catch in any form. The documentation isn't great and I get no hints on what's going wrong. Has anyone got experience with this?
NOTE: I cannot see the console as this is a .NET Maui Blazor App. Nothing appears in my output window in VS2022 either.
Here is my code:
LengthTimeOverTime.Razor:
#page "/lengthtimeovertimechart"
#using Application.Services.UIData;
#using ChartJs.Blazor.Common.Axes;
#using ChartJs.Blazor.Common.Time;
#using ChartJs.Blazor.LineChart;
#using ChartJs.Blazor;
#using Application.DTOs;
#inject IUIDataService uiDataService;
<div class="w-50">
<h3>Length Time Over Time</h3>
<Chart Config="#_config" />
</div>
#code {
private LineConfig _config;
protected override void OnInitialized()
{
var data = uiDataService.GetLengthTimeOverTimeData().Select(record => new TimePoint(record.Time, record.LengthTime)).ToArray();
_config = new()
{
Options = new()
{
Title = new()
{
Text = "Length Time Over Time"
},
Responsive = true,
MaintainAspectRatio = true,
Scales = new()
{
YAxes = new List<CartesianAxis>()
{
new LinearCartesianAxis
{
ScaleLabel = new()
{
Display = true,
LabelString = "Length Time",
},
//Ticks = new()
//{
// Display = true
//}
}
},
XAxes = new List<CartesianAxis>()
{
new TimeAxis
{
ScaleLabel = new()
{
Display = true,
LabelString = "Time"
},
//Ticks = new()
//{
// Display = true
//},
//Time = new TimeOptions()
//{
// TooltipFormat = "hh:mm:ss"
//},
}
}
}
},
};
var lineDataSet = new LineDataset<TimePoint>(data)
{
BackgroundColor = "rgb(124,252,0)",
Label = "Any",
};
_config.Data.Datasets.Add(lineDataSet);
}
}
My DTO: LengthTimeOverTimeDTO.cs
namespace Application.DTOs
{
public class LengthTimeOverTimeDTO
{
public DateTime Time { get; set; }
public double LengthTime { get; set; }
}
}
I have another scatter graph which follows almost the exact same pattern which renders absolutely fine. I did wonder if it was because there are two graphs, and so I added Id's for both, but that didn't resolve it either.
Am I doing something stupid?

Implementing Xamarin Forms context actions

I am trying to implement context actions on my list in Xamarin Forms but can't get it to work.
I am not using XAML, but instead creating my layout in code.
I am trying to follow the steps in https://developer.xamarin.com/guides/xamarin-forms/user-interface/listview/interactivity/#Context_Actions and I want to push a new page when "Edit" is clicked.
I cleaned up my code and removed my feeble attempts to make things work.
So this is my custom list cell:
public class PickerListCell : TextCell
{
public PickerListCell ()
{
var moreAction = new MenuItem { Text = App.Translate ("Edit") };
moreAction.SetBinding (MenuItem.CommandParameterProperty, new Binding ("."));
moreAction.Clicked += async (sender, e) => {
var mi = ((MenuItem)sender);
var option = (PickerListPage.OptionListItem)mi.CommandParameter;
var recId = new Guid (option.Value);
// This is where I want to call a method declared in my page to be able to push a page to the Navigation stack
};
ContextActions.Add (moreAction);
}
}
And here is my model:
public class OptionListItem
{
public string Caption { get; set; }
public string Value { get; set; }
}
And this is the page:
public class PickerPage : ContentPage
{
ListView listView { get; set; }
public PickerPage (OptionListItem [] items)
{
listView = new ListView () ;
Content = new StackLayout {
Children = { listView }
};
var cell = new DataTemplate (typeof (PickerListCell));
cell.SetBinding (PickerListCell.TextProperty, "Caption");
cell.SetBinding (PickerListCell.CommandParameterProperty, "Value");
listView.ItemTemplate = cell;
listView.ItemsSource = items;
}
// This is the method I want to activate when the context action is called
void OnEditAction (object sender, EventArgs e)
{
var cell = (sender as Xamarin.Forms.MenuItem).BindingContext as PickerListCell;
await Navigation.PushAsync (new RecordEditPage (recId), true);
}
}
As you can see by my comments in the code, I have indicated where I believe things are missing.
Please assist guys!
Thanks!
Probably is too late for you, but can help others. The way i've found to do this is passing the instance of page on creation the ViewCell.
public class PickerListCell : TextCell
{
public PickerListCell (PickerPage myPage)
{
var moreAction = new MenuItem { Text = App.Translate ("Edit") };
moreAction.SetBinding (MenuItem.CommandParameterProperty, new Binding ("."));
moreAction.Clicked += async (sender, e) => {
var mi = ((MenuItem)sender);
var option = (PickerListPage.OptionListItem)mi.CommandParameter;
var recId = new Guid (option.Value);
myPage.OnEditAction();
};
ContextActions.Add (moreAction);
}
}
So, in your page:
public class PickerPage : ContentPage
{
ListView listView { get; set; }
public PickerPage (OptionListItem [] items)
{
listView = new ListView () ;
Content = new StackLayout {
Children = { listView }
};
var cell = new DataTemplate(() => {return new PickerListCell(this); });
cell.SetBinding (PickerListCell.TextProperty, "Caption");
cell.SetBinding (PickerListCell.CommandParameterProperty, "Value");
listView.ItemTemplate = cell;
listView.ItemsSource = items;
}
void OnEditAction (object sender, EventArgs e)
{
var cell = (sender as Xamarin.Forms.MenuItem).BindingContext as PickerListCell;
await Navigation.PushAsync (new RecordEditPage (recId), true);
}
}
Ok, so with the help of some posts, specifically this one https://forums.xamarin.com/discussion/27881/best-practive-mvvm-navigation-when-command-is-not-available, I came to the following solution, though I'm not perfectly satisfied with the way it looks.
My custom cell now announces when a command is being executed using MessagingCenter:
public class PickerListCell : TextCell
{
public PickerListCell ()
{
var moreAction = new MenuItem { Text = App.Translate ("Edit") };
moreAction.SetBinding (MenuItem.CommandParameterProperty, new Binding ("."));
moreAction.Clicked += async (sender, e) => {
var mi = ((MenuItem)sender);
var option = (PickerListPage.OptionListItem)mi.CommandParameter;
var recId = new Guid (option.Value);
// HERE I send a request to open a new page. This looks a
// bit crappy with a magic string. It will be replaced with a constant or enum
MessagingCenter.Send<OptionListItem, Guid> (this, "PushPage", recId);
};
ContextActions.Add (moreAction);
}
}
And in my PickerPage constructor I added this subscription to the Messaging service:
MessagingCenter.Subscribe<OptionListItem, Guid> (this, "PushPage", (sender, recId) => {
Navigation.PushAsync (new RecordEditPage (recId), true);
});
All this works just find, but I'm not sure if this is the way it was intended to. I feel like the binding should be able to solve this without the Messaging Service, but I can't find out how to bind to a method on the page, only to a model, and I don't want to pollute my model with methods that have dependencies on XF.

How can I send all of my information from my DB into a pin and send it to another page?

I have multiple data that I receive from my database and I want all of it to be sent in with my pin, but I am not sure how I should do it. Right now I send pin.Name + pin.Address, but I want everything from that particular pin info to get to the next page. So in my case, for example, phone number, URL, etc., but as I am now sending my pin as the sender, I do not know how to send all of the info.
This is the code I am working with:
private async Task<List<Pin>> LoadData()
{
var pins = new List<Pin> ();
var getItems = await phpApi.getInfo ();
foreach (var currentItem in getItems["results"]) {
longString = currentItem ["long"].ToString ();
latString = currentItem ["lat"].ToString ();
nameString = currentItem ["name"].ToString ();
adressString = currentItem ["adress"].ToString ();
phoneString = currentItem ["phone"].ToString ();
urlString = currentItem ["URL"].ToString ();
var pin = new Pin ();
pin.Position = new Position (latString, longString);
pin.Label = nameString;
pin.Address = adressString;
pin.Clicked += onButtonClicked1;
pins.Add (pin);
}
}
void onButtonClicked1 (object s, EventArgs a)
{
Pin pin = (Pin)s;
Navigation.PushModalAsync (new DetailPage ( pin.Label, pin.Address)); // How do I include all of the strings here to the next page?
}
Inside of the constructor of your custom DetailPage, you should be able to receive the Pin object.
EX:
public class MyDetailPage : ContentPage
{
public MyDetailPage(Pin pin)
{
//Do whatever you need to do to your Pin here.
}
}
However, I would probably say it would be easier with a custom object.
i.e.
public class MyCustomPinHolder
{
private string Phone { get; set; }
private string Name { get; set; }
...
private Pin Pin { get; set;}
}
Some notes:
You may want to consider using a method of saving the item to a database/local cache.
You can then pull the respective item from your database/cache given a unique id.

UISearchController and MvvmCross

I want add search logic for my application (IOS8). I have simple MvxTableViewController and display my data by UITableViewSource. Here is:
...controller:
MvxViewFor(typeof(MainViewModel))]
partial class MainController : MvxTableViewController
{
public MainController(IntPtr handle) : base(handle) { }
public override void ViewDidLoad()
{
base.ViewDidLoad();
// make background trasnsparent page
this.View.BackgroundColor = UIColor.Clear;
this.TableView.BackgroundColor = UIColor.Clear;
this.NavigationController.NavigationBar.BarStyle = UIBarStyle.Black;
this.SetBackground ();
(this.DataContext as MainViewModel).PropertyChanged += this.ViewModelPropertyChanged;
}
private void SetBackground()
{
// set blured bg image
}
private void ViewModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var viewModel = this.ViewModel as MainViewModel;
if (e.PropertyName == "Title")
{
this.Title = viewModel.Title;
}
else if (e.PropertyName == "Topics")
{
var tableSource = new TopicTableViewSource(viewModel.Topics);
tableSource.TappedCommand = viewModel.NavigateToChildrenPageCommand;
this.TableView.Source = tableSource;
this.TableView.ReloadData();
}
}
I read about search in IOS and choosed UISearchController for IOS8 app. But I don't understand, how I can add this controller to my view :(
I found sample from Xamarin (TableSearch) - but they don't use UITableViewSource and I don't understand what I should do with this.
I tried add controller:
this.searchController = new UISearchController (this.searchTableController)
{
WeakDelegate = this,
DimsBackgroundDuringPresentation = false,
WeakSearchResultsUpdater = this,
};
this.searchController.SearchBar.SizeToFit ();
this.TableView.TableHeaderView = searchController.SearchBar;
this.TableView.WeakDelegate = this;
this.searchController.SearchBar.WeakDelegate = this;
what should I do in this.searchTableController? Do I need move my display logic there?
Yes. The "searchTableController" should be responsible for the presentation of search results.
Here is the test project (native, not xmarin) which help you understand.
The searchController manages a "searchBar" and "searchResultPresenter". His not need add to a view-hierarchy of the carrier controller. When user starts typing a text in the "searchBar" the "SearchController" automatically shows your SearchResultPresenter for you.
Steps:
1) Instantiate search controller with the SearchResultsPresenterController.
2) When user inputs text in the search-bar you should invoke a your own service for the search. Below a sample of code..
#pragma mark - UISearchResultsUpdating
- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchString = searchController.searchBar.text;
if (searchString.length > 1)
{
// TODO - call your service for the search by string
// this may be async or sync
// When a data was found - set it to presenter
[self.searchResultPresenter dataFound:<found data>];
}
}
3) In the search presenter need to reload a table in the method "dataFound:"
- (void)dataFound:(NSArray *)searchResults
{
_searchResults = searchResults;
[self.tableView reloadData];
}
Here are some advice on how to use the UISearchController with Xamarin.iOS.
Create a new class for the results table view subclassing UITableViewSource. This is gonna be the view responsible of displaying the results. You need to make the items list of that table view public.
public List<string> SearchedItems { get; set; }
In your main UIViewController, create your UISearchController and pass your result table view as an argument. I added some extra setup.
public UISearchController SearchController { get; set; }
public override void ViewDidLoad ()
{
SearchController = new UISearchController (resultsTableController) {
WeakDelegate = this,
DimsBackgroundDuringPresentation = false,
WeakSearchResultsUpdater = this,
};
SearchController.SearchBar.SizeToFit ();
SearchController.SearchBar.WeakDelegate = this;
SearchController.HidesNavigationBarDuringPresentation = false;
DefinesPresentationContext = true;
}
The best way to add the search bar to your UI in term of user experience, in my opinion, is to add it as a NavigationItem to a NavigationBarController.
NavigationItem.TitleView = SearchController.SearchBar;
Add methods to perform the search in the main UIViewController:
[Export ("updateSearchResultsForSearchController:")]
public virtual void UpdateSearchResultsForSearchController (UISearchController searchController)
{
var tableController = (UITableViewController)searchController.SearchResultsController;
var resultsSource = (ResultsTableSource)tableController.TableView.Source;
resultsSource.SearchedItems = PerformSearch (searchController.SearchBar.Text);
tableController.TableView.ReloadData ();
}
static List<string> PerformSearch (string searchString)
{
// Return a list of elements that correspond to the search or
// parse an existing list.
}
I really hope this will help you, good luck.

Custom title of MonoTouch.Dialog RootElement

I am trying to build a menu structure with Monotouch.Dialog. The structure consists of multiple nested RootElements.
When you create a RootElement you set the caption in the constructor. This caption is used for the text of the table cell as well as the title in the view that follows when you click on it.
I would like to set the title of the view to a different text than the name of the element.
Let my try to illustrate what I mean with a simplified example.
The structure:
- Item 1
- Item 1.1
- Item 1.2
- Item 2
- Item 2.1
The code which creates this structure:
[Register ("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
UIWindow _window;
UINavigationController _nav;
DialogViewController _rootVC;
RootElement _rootElement;
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
_window = new UIWindow (UIScreen.MainScreen.Bounds);
_rootElement = new RootElement("Main");
_rootVC = new DialogViewController(_rootElement);
_nav = new UINavigationController(_rootVC);
Section section = new Section();
_rootElement.Add (section);
RootElement item1 = new RootElement("Item 1");
RootElement item2 = new RootElement("Item 2");
section.Add(item1);
section.Add(item2);
item1.Add (
new Section()
{
new StringElement("Item 1.1"),
new StringElement("Item 1.2")
}
);
item2.Add (new Section() {new StringElement("Item 2.1")});
_window.RootViewController = _nav;
_window.MakeKeyAndVisible ();
return true;
}
}
When Item 1 is clicked, a screen is displayed which has the title "Item 1". I would like to change the title "Item 1" to "Type 1 items". But the text of the element which was clicked should remain to be "Item 1".
What is the best way of handling this?
It would be nice if I could do something like this:
RootElement item1 = new RootElement("Item 1", "Type 1 items");
I have tried getting the DialogViewController (see this post) and setting the Title of it. But I failed in getting this to work properly.
You can create a subclass of RootElement, that overrides the return value from GetCell and changes the text to render when it is part of a table view:
class MyRootElement : RootElement {
string ShortName;
public MyRootElement (string caption, string shortName)
: base (caption)
{
ShortName = shortName;
}
public override UITableViewCell GetCell (UITableView tv)
{
var cell = base.GetCell (tv);
cell.TextLabel.Text = ShortName;
return cell;
}
}

Categories

Resources