I'm a beginner in MonoTouch and MonoTouch.Dialog.
I am trying to use MT Dialog but I cannot understand how to get data in and out.
Let's say I have Event class:
class Event {
bool type {get;set;}
string name {get;set;}
}
And I want to edit it using this dialog definition:
return new RootElement ("Event Form") {
// string element
new Section ("Information"){
new EntryElement ("Name", "Name of event", ""),
new RootElement ("Type", new RadioGroup (0)){
new Section (){
new RadioElement ("Concert"),
new RadioElement ("Movie"),
new RadioElement ("Exhibition"),
new RadioElement ("Sport")
}
}
},
How can I pass data to and from this form? (using low-level API not Reflection which supports binding)
Very easy, assign the intermediate values to variables:
Section s;
SomeElement e;
return new RootElement ("Foo") {
(s = new Section ("...") {
(e = new StringElement (...))
})
};
You can do something like this:
//custom class to get the Tapped event to work in a RadioElement
class OptionsRadioElement: RadioElement
{
public OptionsRadioElement(string caption, NSAction tapped): base(caption)
{
Tapped += tapped;
}
}
//Custom Controller
public class MyController: DialogViewController
{
private readonly RadioGroup optionsGroup;
private readonly EntryElement nameField;
public MyController(): base(null)
{
//Note the inline assignements to the fields
Root = new RootElement ("Event Form") {
new Section ("Information"){
nameField = new EntryElement ("Name", "Name of event", ""),
new RootElement ("Type", optionsGroup = new RadioGroup (0)){
new Section (){
new OptionsRadioElement("Concert", OptionSelected),
new OptionsRadioElement("Movie", OptionSelected),
new OptionsRadioElement("Exhibition", OptionSelected),
new OptionsRadioElement("Sport", OptionSelected)
}
}
};
}
private void OptionSelected()
{
Console.WriteLine("Selected {0}", optionsGroup.Selected);
}
public void SetData(MyData data)
{
switch(data.Option)
{
case "Concert:
optionsGroup.Selected = 0;
break;
case "Movie":
optionsGroup.Selected = 1;
break;
//And so on....
default:
optionsGroup.Selected = 0;
break;
}
nameField.Value = data.Name;
ReloadData();
}
}
Related
I have a ViewModel called LocationsViewModel, in which I have a ObservableCollection<LocationViewModel>. Additionally I have a LocationsView, which is an MvxCollectionViewController, in which I create a binding set and bind a MvxCollectionViewSource to the ObservableCollection.
In the LocationCell, which is a MvxCollectionViewCell, I want to display a MonoTouch.Dialog which is bound to various properties in the currently
selected LocationViewModel. The easiest way seems to be to create a nested MvxDialogViewController in the MvxCollectionViewCell, however to bind the
Elements in the MvxDialogViewController, I obviously need to create a Binding Target. My question is really can I pass a binding target from the MvxCollectionViewCell to the MvxDialogViewController?
Let me also try to explain it briefly with some code to improve the understanding.
LocationsViewModel.cs
public class LocationsViewModel : MvxViewModel
{
...
public ObservableCollection<LocationViewModel> Locations
{
get { return _locationDataService.Locations.Locations; }
}
...
}
LocationViewModel.cs
public class LocationViewModel : MvxNotifyPropertyChanged
{
...
//Tons of public properties like:
public string Name
{
get { return LinkedDataModel.Name; }
set
{
LinkedDataModel.Name = value;
RaisePropertyChanged(() => Name);
}
}
public double CurrentNoiseLevel
{
get { return LinkedDataModel.CurrentNoiseLevel; }
set
{
LinkedDataModel.CurrentNoiseLevel = value;
RaisePropertyChanged(() => CurrentNoiseLevel);
}
}
...
}
LocationsView.cs
[Register("LocationView")]
public class LocationsView
: MvxCollectionViewController
{
static readonly NSString LocationCellId = new NSString("LocationCell");
private readonly bool _isInitialized;
public LocationsView()
: base(new UICollectionViewFlowLayout()
{
MinimumInteritemSpacing = 0f,
ScrollDirection = UICollectionViewScrollDirection.Horizontal,
MinimumLineSpacing = 0f
})
{
_isInitialized = true;
ViewDidLoad();
}
public new LocationsViewModel ViewModel
{
get { return (LocationsViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
public sealed override void ViewDidLoad()
{
if (!_isInitialized)
return;
base.ViewDidLoad();
CollectionView.RegisterClassForCell(typeof(LocationCell), LocationCellId);
var source = new MvxCollectionViewSource(CollectionView, LocationCellId);
CollectionView.Source = source;
CollectionView.PagingEnabled = true;
var set = this.CreateBindingSet<LocationsView, LocationsViewModel>();
set.Bind(source).To(vm => vm.Locations);
set.Apply();
CollectionView.ReloadData();
}
}
LocationCell.cs
public class LocationCell : MvxCollectionViewCell
{
[Export("initWithFrame:")]
public LocationCell(RectangleF frame)
: base(string.Empty, frame)
{
InitView();
}
public LocationCell(IntPtr handle)
: base(string.Empty, handle)
{
InitView();
}
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
}
public class LocationCellDialog
: MvxDialogViewController
{
public LocationCellDialog()
: base(UITableViewStyle.Grouped, null, true)
{ }
public override void ViewDidLoad()
{
base.ViewDidLoad();
//How do I get the target here?
var target = ??;
Root = new RootElement
{
new Section
{
new StringElement().Bind(target, t => t.Name),
new StringElement().Bind(target, t => t.CurrentNoiseLevel)
}.Bind(target, t => t.Name),
};
}
}
}
So the question is can I simply pass along a binding target from the parent LocationCell to nested LocationCellDialog or is that a no go?
Each bindable view in MvvmCross has its own DataContext
For a top level View this DataContext is the ViewModel
For a Cell within a List, Table or Collection then the DataContext is set to the object in the list which the Cell is currently showing.
If you want to data-bind any property within a Cell to a property path on the DataContext then you can do so using the Fluent binding syntax.
For example, to bind the Text value of a child UILabel called myLabel to a child property Name on a Person in the list you could use:
this.CreateBinding(myLabel).For(label => label.Text).To<Person>(p => p.Name).Apply();
Or if you wanted to bind the Text to the Person itself you could use:
this.CreateBinding(myLabel).For(label => label.Text).Apply();
In your LocationCell I think you are saying you want to bind the DataContext of the nested LocationCellDialog to the DataContext of the containing cell.
To do this you should be able to use:
private void InitView()
{
var cell = new LocationCellDialog();
ContentView.Add(cell.View);
this.CreateBinding(cell).For(cell => cell.DataContext).Apply();
}
I want to ask about finding a location using the action bar. I've made the program as shown below :
I've made a class to find location. but at the time I called into the EditText search, class does not work.
// An AsyncTask class for accessing the GeoCoding Web Service
private class GeocoderTask extends AsyncTask<String, Void, List<Address>>{
#Override
protected List<Address> doInBackground(String... locationName) {
// Creating an instance of Geocoder class
Geocoder geocoder = new Geocoder(getBaseContext());
List<Address> addresses = null;
try {
// Getting a maximum of 3 Address that matches the input text
addresses = geocoder.getFromLocationName(locationName[0], 3);
} catch (IOException e) {
e.printStackTrace();
}
return addresses;
}
}
protected void onPostExecute(List<Address> addresses) {
if(addresses==null || addresses.size()==0){
Toast.makeText(getBaseContext(), "No Location found", Toast.LENGTH_SHORT).show();
}
// Clears all the existing markers on the map
map.clear();
// Adding Markers on Google Map for each matching address
for(int i=0;i<addresses.size();i++){
Address address = (Address) addresses.get(i);
// Creating an instance of GeoPoint, to display in Google Map
latLng = new LatLng(address.getLatitude(), address.getLongitude());
String addressText = String.format("%s, %s",
address.getMaxAddressLineIndex() > 0 ? address.getAddressLine(0) : "",
address.getCountryName());
markerOptions = new MarkerOptions();
markerOptions.position(latLng);
markerOptions.title(addressText);
map.addMarker(markerOptions);
// Locate the first location
if(i==0)
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng,10));
}
}
and I had to call the class to the class action bar menu. like this:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.cost_direction, menu);
/** Get the action view of the menu item whose id is search */
View v = (View) menu.findItem(R.id.search).getActionView();
/** Get the edit text from the action view */
EditText txtSearch = ( EditText ) v.findViewById(R.id.txt_search);
/** Setting an action listener */
txtSearch.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Getting user input location
String location = v.getText().toString();
if(location!=null && !location.equals("")){
new GeocoderTask().execute(location);
}
Toast.makeText(getBaseContext(), "Search : " + v.getText(), Toast.LENGTH_SHORT).show();
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
please help for those who know about the problems I was having. :)
String strPlace = etSearch.getText().toString();
Geocoder gc = new Geocoder(getBaseContext(), Locale.getDefault());
List<Address> adrs = null;
try{
adrs = gc.getFromLocationName(strPlace,5);
}catch(IOException e){
}finally{
if (adrs != null){
if(adrs.size() > 0)
{
LatLng loc = new LatLng(adrs.get(0).getLatitude(), adrs.get(0).getLongitude());
map.moveCamera(CameraUpdateFactory.newLatLngZoom(loc, 15));
// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.zoomTo(13), 2000, null);
}
I have a RadioGroup I created:
Root = new RootElement ("Club 1") {
new Section ("Club Members"){
new StringElement ("P1", "Kyle"),
new StringElement ("P2", "Matt"),
new RootElement("Members", new RadioGroup(0))
{
CreateRoot()
}
}
The RootElement above need to the display the value selected from the RadioGroup.
RootElement CreateRoot ()
{
StringElement se = new StringElement (String.Empty);
MyRadioElement.OnSelected += delegate(object sender, EventArgs e) {
se.Caption = (sender as MyRadioElement).Caption;
var root = se.GetImmediateRootElement ();
root.Reload (se, UITableViewRowAnimation.Fade);
};
return new RootElement (String.Empty, new RadioGroup (0)) {
new Section ("Select Member") {
new MyRadioElement ("No Member Selected"),
new MyRadioElement ("Member 1"),
new MyRadioElement ("Member 2"),
new MyRadioElement ("Member 3")
}
};
}
I have an outside class:
class MyRadioElement : RadioElement {
public MyRadioElement (string s) : base (s) {}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
base.Selected (dvc, tableView, path);
var selected = OnSelected;
if (selected != null)
selected (this, EventArgs.Empty);
}
static public event EventHandler<EventArgs> OnSelected;
}
How do I get the selected value to display back on the parent root element?
I was not deactivating the controller.
class MyRadioElement : RadioElement {
public MyRadioElement (string s) : base (s) {}
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
base.Selected (dvc, tableView, path);
var selected = OnSelected;
if (selected != null)
selected (this, EventArgs.Empty);
dvc.DeactivateController(true);
}
static public event EventHandler<EventArgs> OnSelected;
}
i want to create a alert dialog with radiobuttons for single selection or alert dialog with Checkboxes for Multiselection in blackberry.it is possible in android.but i want in blackberry.i searched in google.but i didn't got any solution.please give any suggestions r usefull links for this problem.
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.component.CheckboxField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.container.DialogFieldManager;
public class CheckboxInputDialog extends Dialog{
private CheckboxField checkboxEditField;
public CheckboxInputDialog(String choices[],int values[], String label){
super(label, choices,values,Dialog.OK, Bitmap.getPredefinedBitmap(Bitmap.INFORMATION), Dialog.GLOBAL_STATUS);
checkboxEditField = new CheckboxField("Lablel",false);
net.rim.device.api.ui.Manager delegate = getDelegate();
if( delegate instanceof DialogFieldManager){
DialogFieldManager dfm = (DialogFieldManager)delegate;
net.rim.device.api.ui.Manager manager =dfm.getCustomManager();
if( manager != null ){
manager.insert(checkboxEditField, 0);
}
}
}
}
Now Call this dialog at following way...
String choices[] = { "OK", "CANCEL" };
int values[] = { Dialog.OK, Dialog.CANCEL };
CheckboxInputDialog d = new CheckboxInputDialog(choices,values,"Dialog Label");
d.show();
Output will Be:
Get Event of OK and Cancel Button.
String choices[] = { "OK", "CANCEL" };
int values[] = { Dialog.OK, Dialog.CANCEL };
final CheckboxInputDialog d = new CheckboxInputDialog(choices, values,"Dialog Label");
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
int iResponse = d.doModal();
if (iResponse == 0) {
System.out.println("Press Ok");
}else{
System.out.println("Press Cancel");
}
}
});
Hope Help full..
Create popupScreen and in this screen you can add radiobuttons and Checkboxes.
public class Custom_Popup extends PopupScreen {
public Custom_Popup() {
// TODO Auto-generated constructor stub
super( new VerticalFieldManager(Manager.VERTICAL_SCROLL),
Field.NON_FOCUSABLE | Field.USE_ALL_WIDTH );
}
}
On your event, push this screen.
UiApplication.getUiApplication().pushScreen(new MyPopup());
public class MyPopup extends PopupScreen{
public MyPopup() {
super(new VerticalFieldManager(), Field.FOCUSABLE);
add();//add checkbox , radio buttons here.
}
I'm trying to put together a detail view that is editable, similar to the iPhone default contacts app.
I have a TableView of contacts and I activate an editable detail view when a cell is selected:
public override void Selected (DialogViewController dvc, UITableView tableView, NSIndexPath path)
{
var editingSection = new Section ("Entity") {
new StringElement ("First name", "enter", _entity.FirstName),
new StringElement ("Last name", "enter", _entity.LastName)
};
var root = new RootElement("Entity Entry") {
editingSection
};
var entityEdit = new EntityEdit (root, true);
ConfigEdit (entityEdit);
dvc.ActivateController(entityEdit);
}
void ConfigEdit (DialogViewController dvc)
{
dvc.NavigationItem.RightBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Edit, delegate {
dvc.TableView.SetEditing (true, true);
ConfigDone (dvc);
});
}
void ConfigDone (DialogViewController dvc)
{
dvc.NavigationItem.RightBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Done, delegate {
dvc.TableView.SetEditing (false, true);
ConfigEdit (dvc);
});
}
The behavior that I want to change is in the ConfigEdit method. When I first show the view, the elements in the editing section should be StringElements. When I switch to edit mode, the elements should change to entry Elements because I want the ability to delete the row, or edit the text inside the element.
Is this possible? Is there a better approach to showing read-only elements until edit mode has been set?
You have a couple of options:
a. You can just change the RootElement with an updated RootElement, or update the individual cells with new cells of the proper type of element to get the desired effect. You could do this by parametrizing your creation:
RootElement CreateRoot (bool editable)
{
return new RootElement (...) {
CreateEditableElement ("foo", bar, editable)
}
}
Element CreateEditableElement (string caption, string content, bool editable)
{
return editable ? EntryElement (caption, content) : StringELement (caption, content)
}
Then you need to call ReloadData after the user has tapped the "Edit" button.
The other thing that you could do is to create a new Element that can switch states based on this information. My blog has a tutorial on how to create new elements:
http://tirania.org/monomac/archive/2011/Jan-18.html
Based on Miguel's answer, here is what I did:
public partial class AppDelegate
{
public void DemoAdvancedEditing ()
{
var root = new RootElement ("Todo list") {
new Section() {
new StringElement ("1", "Todo item 1"),
new StringElement ("2","Todo item 2"),
new StringElement ("3","Todo item 3"),
new StringElement ("4","Todo item 4"),
new StringElement ("5","Todo item 5")
}
};
var dvc = new AdvancedEditingDialog (root, true);
AdvancedConfigEdit (dvc);
navigation.PushViewController (dvc, true);
}
void AdvancedConfigEdit (DialogViewController dvc)
{
dvc.NavigationItem.RightBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Edit, delegate {
// Activate editing
// Switch the root to editable elements
dvc.Root = CreateEditableRoot(dvc.Root, true);
dvc.ReloadData();
// Activate row editing & deleting
dvc.TableView.SetEditing (true, true);
AdvancedConfigDone(dvc);
});
}
void AdvancedConfigDone (DialogViewController dvc)
{
dvc.NavigationItem.RightBarButtonItem = new UIBarButtonItem (UIBarButtonSystemItem.Done, delegate {
// Deactivate editing
dvc.ReloadData();
// Switch updated entry elements to StringElements
dvc.Root = CreateEditableRoot(dvc.Root, false);
dvc.TableView.SetEditing (false, true);
AdvancedConfigEdit (dvc);
});
}
RootElement CreateEditableRoot (RootElement root, bool editable)
{
var rootElement = new RootElement("Todo list") {
new Section()
};
foreach (var element in root[0].Elements) {
if(element is StringElement) {
rootElement[0].Add(CreateEditableElement (element.Caption, (element as StringElement).Value, editable));
} else {
rootElement[0].Add(CreateEditableElement (element.Caption, (element as EntryElement).Value, editable));
}
}
return rootElement;
}
Element CreateEditableElement (string caption, string content, bool editable)
{
if (editable) {
return new EntryElement(caption, "todo", content);
} else {
return new StringElement(caption, content);
}
}
}