I'm building a Mac OS app in C# using Xamarin.Mac. I'd like to populate a dropdown (NSPopUpButton) with items that store more information than just their title and index. Specifically I'd like to have each item also hold its own database ID, so when the user chooses that item from the dropdown I can easily fetch the relevant record from the database.
I see that NSPopUpButton uses instances of NSMenuItem, and it's easy enough to subclass NSMenuItem, but there doesn't seem to be a way to insert an instance directly. The AddItem() method only accepts a string (the title of the item) and has no overloads. Is there a way to use custom items, or am I stuck with just title and index to differentiate?
Thanks!
Add your subclass'd NSMenuItem via the NSPopUpButton.Menu.AddItem method.
Example NSMenuItem:
public class MyNSMenuItem : NSMenuItem
{
public string SomeCustomProperty { get; set; }
~~~~
// add all the .ctors w/ base calls....
}
Example usage:
var popUpButton = new NSPopUpButton()
{
Frame = new CGRect(100, 100, 100, 100),
Title = "A pop button",
};
var menuItem = new MyNSMenuItem()
{
Title = "StackOverflow",
SomeCustomProperty = "SomeInstance",
};
menuItem.Activated += (sender, e) =>
{
Console.WriteLine((sender as MyNSMenuItem).SomeCustomProperty);
};
popUpButton.Menu.AddItem(menuItem);
this.View.AddSubview(popUpButton);
Related
I would like to ask about bindings. What is the best approach to bind some actions in listview items in ios and android using xamarin in mvvm world. As I understand, we have few approaches.
1.
For every list item we have some Model, and to this model we have to add some Commands.
For example:
public class ItemModel
{
public string MyName { get; set; }
public ICommand RemoveCommand { get; set; }
}
Where in ViewModel we have SomeInitMethod
public class ViewModel
{
public ObservableCollection<ItemModel> Items {get;set;}
public async Task SomeInitMethod
{
Items = new ObservableCollection(await _myApiService.FetchItemsAsync());
foreach(var item in Items)
{
item.Command = new RelayCommand(RemoveItem);
}
}
public void RemoveItem(ItemModel item)
{
Items.Remove(item);
}
}
But I see a drawback in SomeInitMethod where we should set RemoveCommand. What if we should to set 2 or even more commands than we duplicate code in ListItemView(somehow we need to bind all these commands)?
Next approach is somehow handle events of remove/toggle buttons and others in Listview and then delegate this commands directly to ViewModel.
Example:
ContactsListView.ItemRemoveClicked += (ItemModel model) => ViewModel.RemoveItem
Advantages is: we no longer need to handle commands in ViewModel
Drawback is: we need every time to write custom ListView and support event handling in code-behind.
The last approach is to send ViewModel to ListItem to set Commands.
Example
somewhere we have method CreateListViewItem on the view, let's say on iOS.
private void InitTableView() {
TableView.RegisterNibForCellReuse(ItemViewCell.Nib, ItemViewCell.Key);
var source = new ObservableTableViewSource <ItemModel>
{
DataSource = ViewModel.Items,
BindCellDelegate = (cell, viewModel, index) =>
{
if (cell is ItemModel memberCell)
{
memberCell.BindViewModel(viewModel);
memberCell.RemoveItem = (item) => ViewModel.RemoveItem;
}
}
};
TableView.Source = source;
}
Advantages: we no longer need to have Commands in Model, and we don't need to setup this Commands in ViewModel.
Possibly, drawback is that we somehow need to have ViewModel reference.
In WPF or UWP you have DataContext, you can binding directly to ViewModel.
Which approach you use, maybe I miss something, and it would be perfect if you provide some examples or thoughts.
Thanks.
I've got a drop-down selection box that has 2,000 items in it. I tried to bind it like this:
#(Html.Kendo().MultiSelect().Name("kendo-dropdown-manager")
.DataValueField("Value")
.DataTextField("Text")
.Placeholder("Select Entity")
.BindTo(Model.ManagersList)
In my Razor Page, I implement the 'Get' asynchronously:
public class IndexModel : PageModel
{
public IndexModel()
{
this.ManagersList = new List<SelectListItem>();
}
public List<SelectListItem> ManagersList { get; private set; }
public Task OnGetAsync()
{
ViewsController viewsController = new ViewsController();
List<ManagerViewModel> managers = await viewsController.GetManagersAsync();
this.ManagersList.AddRange(
from m in managers
select new SelectListItem
{
Value = m.Id.ToString(),
Text = m.Name
});
}
}
When the page appears, the drop down is empty. If I change 'OnGetAsync' to a completely synchronous version, the drop down is populated correctly. My page has several similar controls and I don't want to load them up synchronously. What's the proper way to start an operation and have the Kendo controls catch the results when they're returned?
I have a model "optionModel" with "data" member as a list of "options". Option has title and status members:
class optionModel
{
...
public List<option> data {get; private set;}
}
class option
{
...
public String title{get;set}
public bool status {get;set}
}
My view for the model is a grid, which is populating like this:
optionModel _m = new optionModel();
Grid g = new Grid();
foreach (option op in _m.data)
{
..
TextSwitch tc = new TextSwitch(); // view class
tc.BindingContext = op;
tc.SetBinding(TextSwitch.IsToggledProperty, "status");
g.Children.Add(tc, 0, iRow);
..
}
Everything works as expected. "status" property of the "option" is updated, I am happy.
My question is: In addition to "option" I also want "optionModel" to be notified when any of the "option" change their status. One immediate solution is
to construct the "option" to have pointer to the optionModel and notify optionModel it within "status" set method of "option".
But perhaps there is a way to do it via Xamarin binding mechanism without introducing optionModel as a member of "option"?
Thanks!
I'm new to Xamarin and C#, so apologies in advance for any mistakes I make.
In my app, I have a list of plants. When a plant is selected, I have a detail view of info about the plant. In the detail view, I have a button that adds or removes the plant from a shopping list.
To implement this, I have a class named MyPlant, with a field called InCart, and a method ToggleInCart that the button calls.
(note that I didn't paste in some code to simplify this question as much as possible)
public class MyPlant : INotifyPropertyChanged
{
string name;
bool inCart;
...
public bool InCart
{
set
{
if (inCart != value)
{
inCart = value;
OnPropertyChanged("InCart");
}
}
get { return inCart; }
}
public ICommand ToggleCartStatus
{
get
{
if (_toggleCartStatus == null)
{
_toggleCartStatus = new Command(() => InCart = !InCart);
}
return _toggleCartStatus;
}
I have another class called PlantList, which has a method PlantsInCart that uses LINQ to return an ObservableCollection of MyPlant where InCart is true.
public class PlantList : INotifyPropertyChanged
{
public ObservableCollection PlantsInCart
{
private set { }
get
{
ObservableCollection list = new ObservableCollection(myPlants.Where(i => i.InCart));
return list;
}
}
In my XAML, I have a ListView bound to PlantsInCart.
Everything works as I want EXCEPT when I remove the selected plant, the list doesn't update to show the plant is missing even though the data underneath it is correctly updated. If I refresh the list by going to a different page and coming back, then the list shows the right plants.
I suspect this doesn't work because the change in the InCart field isn't bubbling up high enough to that the ListView hears that it is supposed to update.
Can anybody advise me on the proper way to implement this kind of feature? In other words, how should you implement a scenario where you have a list that should update when a property of an item in the list changes?
Can someone please give me a simple example on how to add three rows to a ListField so that the list shows something like this?
Item 1
Item 2
Item 3
I just want to show a list in which the user can select one of the items and the program would do something depending on the item selected.
I've search all over the internet but it seems impossible to find a simple example on how to do this (most examples I found are incomplete) and the blackberry documentation is terrible.
Thanks!
You probably want to look at using an ObjectListField. Handling the select action is done throught the containing Screen object, I've done this below using a MenuItem, I'm not really sure how to set a default select listener, you may have to detect key and trackwheel events.
Some example code for you: (not tested!)
MainScreen screen = new MainScreen();
screen.setTitle("my test");
final ObjectListField list = new ObjectLIstField();
String[] items = new String[] { "Item 1", "Item 2", "Item 3" };
list.set(items);
screen.addMenuItem(new MenuItem("Select", 100, 1) {
public void run() {
int selectedIndex = list.getSelectedIndex();
String item = (String)list.get(selectedIndex);
// Do someting with item
});
screen.add(list);
You can override the navigationClick method like this:
ObjectListField list = new ObjectListField()
{
protected boolean navigationClick(int status, int time)
{
// Your implementation here.
}
};
final class SimpleListScreen extends MainScreen
{
public SimpleListScreen()
{
super(Manager.NO_VERTICAL_SCROLL);
setTitle("Simple List Demo");
add(new LabelField("My list", LabelField.FIELD_HCENTER));
add(new SeparatorField());
Manager mainManager = getMainManager();
SimpleList listField = new SimpleList(mainManager);
listField.add("Item 1");
listField.add("Item 2");
listField.add("Item 3");
}
}
//add listener so that when an item is chosen,it will do something
private ListField fList = new ListField(){
protected boolean navigationClick(int status, int time) {
System.out.println("omt Click");
return true;
};
};
You can detect the click on each list item by overriding
protected boolean navigationClick(int status,int time)
Then you just need to work out what to do in response to the click. The way I did this was by using an anonymous class, set for each list item.