MonoTouch Dialog's UITableView. How to handle taps? - c#

MonotouchDialog makes it very easy to create UITableView Dialogs, but sometimes questions like that one popup:
MonoTouch Dialog. Buttons with the Elements API
Now, I have a similar problem but quite different:
List<User> users = GetUsers();
var root =
new RootElement ("LoginScreen"){
new Section ("Enter your credentials") {
foreach(var user in users)
new StyledStringElement (user.Name, ()=> {
// tap on an element (but which one exactly?)
}
),
}
navigation.PushViewController (new MainController (root), true);
Now, the second parameter of StyledStringElement's constructor has the type of NSAction delegate, and doesn't take any arguments, now I dunno how to determine exactly which element been tapped.
How to get that?

If it was Tapped then it has been selected. So you should be able to inherit from StyleStringElement and override its Selected method to accomplish the same goal.
e.g.
class UserElement : StyleStingElement {
public UserElement (User user) { ... }
public override Selected (...)
{
// do your processing on 'user'
base.Selected (dvc, tableView, indexPath);
}
}
For Touch.Unit I created a new *Element for every item I had, TestSuiteElement, TestCaseElement, TestResultElement... to be able to customize each of them and adapt (a bit) their behaviour but I did not use this Selected to replace Tapped. You might want to check but it would not fit with your code pattern to create elements.

"...a Flower by any other name?"
If you look closely NSAction's are just delegates. I prefer to pass Action / Func into those params the reference for which is contained within the...container controller.
So lets so you have a UINavigationController that pushes a DialogViewController. When your element is selected you provide the unique user that you've passed to the Element and go from there :-)
public class MyNavController : UINavigationController
{
Action<User> UserClickedAction;
public MyNavController()
{
UserClickedAction = HandleUserClicked;
}
public void HandleUserClicked(User user)
{
...
}
}

Related

Getting error in MVC Model regarding use of List(T)

I am learning to build the application using one of the online tutorials regarding MVC. It requires to create a user db.
I am getting the following error while building the application. I have just copy-pasted the code from the tutorial. I googled few things, but I am not getting it. Please help to resolve and explain.
using System;
using System.Collections.Generic;
using System.Collections;
using System.EnterpriseServices;
namespace AdvancedMVCApplication.Models
{
public class Users
{
public List UserList = new List();
//action to get user details
public UserModels GetUser(int id)
{
UserModels usrMdl = null;
foreach (UserModels um in UserList)
if (um.Id == id)
usrMdl = um;
return usrMdl;
}
//action to create new user
public void CreateUser(UserModels userModel)
{
UserList.Add(userModel);
}
//action to udpate existing user
public void UpdateUser(UserModels userModel)
{
foreach (UserModels usrlst in UserList)
{
if (usrlst.Id == userModel.Id)
{
usrlst.Address = userModel.Address;
usrlst.DOB = userModel.DOB;
usrlst.Email = userModel.Email;
usrlst.FirstName = userModel.FirstName;
usrlst.LastName = userModel.LastName;
usrlst.Salary = userModel.Salary;
break;
}
}
}
//action to delete exising user
public void DeleteUser(UserModels userModel)
{
foreach (UserModels usrlst in UserList)
{
if (usrlst.Id == userModel.Id)
{
UserList.Remove(usrlst);
break;
}
}
}
}
}
Error: CS0305: Using the generic type 'List' requires 1 type arguments\Models\Users.cs Line:11
You can view the example here: https://www.tutorialspoint.com/mvc_framework/mvc_framework_advanced_example.htm
I was going to say "maybe the code blocks on tutorialspoint hide the necessary <xxx> after the List because it gets interpreted as an HTML tag".. but then I saw the next code block had actual html tags in just fine
To expand on the point Klaus made, it is possible to write classes in C# that are completed by the compiler rather than you. You specify some placeholder for the type of object the class deals with and then the compiler can use it to create an actual class in the background for you
class TenThings<T>{
private T[] _things = new T[10];
private T GetFirst(){
return _things[0];
}
}
T isn't any type in your program, or in the framework, for the purposes of this class/as written here but if you then say somewhere else:
var tt = new TenThings<string>();
Then the compiler can know "anywhere T is mentioned, in this case it needs to be a string" so it can knock together a class for you that is an array of ten strings and has a GetFirst method that returns a string. On the very next line you can have a TenThings<int> and you'll get another different type of class out that deals with ints. You created a template for the compiler to use to write code for you, and the benefit you get is that your GetFirst really does return a string in one case and an int in another. You could have just made a class like this:
class TenThings{
private object[] _things = new object[10];
private object GetFirst(){
return _things[0];
}
}
But then you have to cast everything that comes out - old classes like ArrayList worked this way, and it wasn't a great experience
List is a generic class like this new "templates" way; you really need to have another type of class in angle brackets after its name, such as List<UserModel> and it becomes a part of the type at the same time as dictating to the compiler how to create the template. Per the comment it seems that tutorials point forgot to put the relevant <UserModels> after the List
There are a few other things I take exception to in that tutorial, but talking specifically about this property; creating the List as a public field for one, calling the class UserModels when it seems to represent a single item (unwarranted plural / collections of items are typically recommended to have a name that ends with "Collection" - plurals are used for properties that are collections), I.e. it should be public List<UserModel> UserModels { get; set; } = new List<UserModel>();. I'll leave picking on it for not being a read only collection typed as something generic like IEnumerable<T> for another time :)

Method, Delegate and Event Subscription Help in Autodesk Inventor Add-In

I'm creating an add-in for Autodesk Inventor. Basically, you define the buttons you want to add, and tell the app to add the button definitions. The problem I'm having is that when I define the 'OnExecute' method for the button definition, the button doesn't execute. I think the way I'm trying to organize the code is what's creating the problem.
I have a CustomButton class that has a delegate property that looks like this (the signature is void with an input of a NameValueMap Interface)
public class CustomButton
{
// … properties and methods that don't matter here
public ButtonDefinitionSink_OnExecuteEventHandler Execute { get; set; }
}
In the main Activate() method (what's called when Inventor starts) I create an instance of the following class to set all the button definitions and the methods that fire when they are clicked. That class looks like this:
public class CustomButtonDefinitions
{
public CustomButtonDefinitions(ref Application app)
{
_inventorApp = app;
InitializeButtonDefinitions();
}
public List<CustomButton> CustomButtons { get; set; } = new List<CustomButton>();
private void InitializeButtonDefinitions()
{
AddTestButton();
}
private void AddTestButton()
{
var testButton = new CustomButton
{
DisplayName = "test",
InternalName = "testCommand1",
Ribbon = "Assembly",
RibbonPanel = "Simplification",
IconSource = "./Assets/test.jpg",
Classification = CommandTypesEnum.kFileOperationsCmdType,
ShowText = true,
UseLargeIcon = true,
};
testButton.Execute = TestButton_Execute;
CustomButtons.Add(testButton);
}
private void TestButton_Execute(NameValueMap Context)
{
// This is where the logic of the button would go.
// For now, just something that gives me an indication it worked.
System.Windows.Forms.MessageBox.Show("Hello");
_inventorApp.ActiveDocument.Close();
}
}
Where I think the source of the error comes from is the next code (this is in the Activate():
CustomButtonDefinitions customButtonDefinitions = new CustomButtonDefinitions(ref _InventorApp);
foreach (var button in customButtonDefinitions.CustomButtons)
{
// this creates the button in Inventor
var buttonDef = button.CreateButtonDefinition(ref controlDefs);
// and this subscribes the button click event to my method
buttonDef.OnExecute += button.Execute;
}
There must be something un-subscribing my method from the button click event.
I'll be posting this in the Inventor forums as well, but wanted to check here too since I'm new to delegates and event handlers. I'm either not understanding something about delegates/events or it's something Inventor specific that I'll need some other help with.
Hopefully this is enough to give some context. Thanks in advance.
The problem was the fact that I wasn't creating the button definition in a high enough scope. I needed to create a variable above the scope of the Activate() method so the app could see it when needed.

A better way to set user permissions on elements using bindings

I have a WPF desktop app.
I am also using sql lite to store tables/values.
One of these tables is User Permission Roles.
So.. when a User logs in the code will get a list of roles they have permission to use.
I then go through each record to set whether the button (ie) is enabled for then or not.
so in my ViewModel...
var myUserRoles = DB.CallMyMethodToReturnPermissions(User.Id);
foreach (role in myUserRoles)
{
switch (role.Name)
{
case "CanDelete"
if (role.Enabled)
{
UserPermissions.CanDelete = true;
}
break;
case "CanAdd"
if (role.Enabled)
{
UserPermissions.CanAdd = true;
}
break;
....etc etc etc
}
}
and then in my View(s) something like this...
<Button name="CanDelete "IsEnabled="{Binding UserPermissions.CanDelete}" />
<Button name="CanAdd "IsEnabled="{Binding UserPermissions.CanAdd}" />
....etc etc etc
All this works well. But looking to the future I can see added functionality and therefore User Roles/Permissions being set.
Therefore, should I consider another paradigm or hard code changes when I need to? Or is there a way to drive this from my DB table?
I only ask this question because I am relatively new to WPF and bindings so was wondering whether there are some clever alternatives?
You could create you own implementation of ICommand.
Draft example:
public class PermissionRequiredCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public PermissionRequiredCommand(string role,
Action onExecute,
Action<bool> canExecute,
Func<string, bool> hasPermission)
{
// bla bla
}
public void Execute(object parameter)
{
onExecute();
}
public bool CanExecute()
{
return canExecute() & hasPermission(role);
}
}
public class ViewModel
{
public ICommand Delete {get;}
public ViewModel(Authenticator authenticator)
{
Delete = new RequiresPermissionCommand(Roles.Delete,
Delete,
CanDelete,
roleName => authenticator.HasPermission(roleName));
}
}
public static class Roles
{
public const string Delete = "CanDelete";
}
Are you ever going to change the permission during run time?
I don't know what UserPermissions is. but if it is not a field but a static class then you might run into trouble for this. Since WPF cant really determine whether the property has changed or not if its a static property. But the fact that your binding isn't x:static so I assume it is a property. Which is fine.
other than that the binding will work well with your current structure given that all ViewModels has access to the UserPermissions instance.
I would also recommend looking into RelayCommand and move the CanExecute and click action in the view model. This way you will be able to put more logic to determine whether to enable the button without a converter.

Advice on Views navigation using Caliburn.Micro MVVM WPF

I'm new on Caliburn Micro and want some advice on which path to take to devolop my app interface and navigation between views.
My idea is to have a MainWindow which will contain a menu of buttons, each one related with a specific view. Each view will be stored in a separated WPF UserControl. The mainWindow will also contain a TabControl bound to an ObservableCollection of tabs on viewmodel. Everytime a button on menu is clicked, I want to add a new tab with a ContentPresenter inside that will dynamically load a view and its corresponding viewmodel.
So my questions:
1) Should I use a Screen Collection here?
2) Should the UserControl implement Screen interface?
3) How do I tell MainWindow ViewModel which view to load on the new added tab maintaining viewmodels decoupled?
Thanks to everyone in advance.
UPDATE
After a lot of reading and some help of the community I managed to resolve this. This is the resultant AppViewModel:
class AppViewModel : Conductor<IScreen>.Collection.OneActive
{
public void OpenTab(Type TipoVista)
{
bool bFound = false;
Screen myScreen = (Screen)Activator.CreateInstance(TipoVista as Type);
myScreen.DisplayName = myScreen.ToString();
foreach(Screen miItem in Items)
{
if (miItem.ToString() == myScreen.ToString())
{
bFound = true;
ActivateItem(miItem);
}
}
if (!bFound) ActivateItem(myScreen);
}
public ObservableCollection<MenuItem> myMenu { get; set; }
public ObservableCollection<LinksItem> myDirectLinks { get; set; }
public ICommand OpenTabCommand
{
get
{
return new RelayCommand(param => this.OpenTab((Type) param), null);
}
}
public AppViewModel()
{
OpenTab(typeof(ClientsViewModel));
MenuModel menu = new MenuModel();
myMenu = menu.getMenu();
myDirectLinks = menu.getLinks();
}
public void CloseTab(Screen param)
{
DeactivateItem(param, true);
}
}
I have to keep the ICommand from OpenTabCommand because the name convention of Caliburn.micro doesn't seems to work inside DataTemplate. Hope it could help someone else. Thanks to all
I've done something very similar using Caliburn.Micro, and based it on the SimpleMDI example included with the examples, with a few tweaks to fit my needs.
Much like in the example, I had a main ShellViewModel:
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
}
with a corresponding ShellView containing a TabControl - <TabControl x:Name="Items">, binding it to the Items property of the the Conductor.
In this particular case, I also had a ContextMenu on my ShellView, bound (using the Caliburn.Micro conventions), to a series of commands which instantiated and Activated various other ViewModels (usually with a corresponding UserControl, using the ActivateItem method on the Conductor.
public class YourViewModel: Conductor<IScreen>.Collection.OneActive
{
// ...
public void OpenItemBrowser()
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
In that case, I didn't require the ViewModels to be created with any particular dependency, or from any other locations in the program.
At other times, when I've needed to trigger ViewModel from elsewhere in the application, I've used the Caliburn.Micro EventAggregator to publish custom events (e.g. OpenNewBrowser), which can be handled by classes implementing the corresponding interface (e.g. IHandle<OpenNewBrowser>), so your main ViewModel could have a simple Handle method responsible for opening the required View:
public class YourViewModel: Conductor<IScreen>.Collection.OneActive, IHandle<OpenNewBrowser>
{
// ...
public void Handle(OpenNewBrowser myEvent)
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
This section of the documentation will probably be useful, especially the Simple MDI section.
Additional code I mentioned in the comments:
I sometimes use a generic method along these lines ensure that if I have an existing instance of a screen of a particular type, switch to it, or create a new instance if not.
public void ActivateOrOpen<T>() where T : Screen
{
var currentItem = this.Items.FirstOrDefault(x => x.GetType() == typeof(T));
if (currentItem != null)
{
ActivateItem(currentItem);
}
else
{
ActivateItem(Activator.CreateInstance<T>());
}
}
Used like:
public void OpenBrowser()
{
this.ActivateOrOpen<BrowserViewModel>();
}

Custom detail pages in Windows 8 grid application

I have created a simple C# Windows 8 grid application.
If you're unfamiliar with this layout, there is a brief explanation of it here :
Link
What I would like to have is simple - some custom ItemDetailPages. I'd like to be able to click on some items on the GroupDetailPage and the GroupedItemsPage and navigate to a custom .xaml file, one where I can include more than one image.
I'm sure there is a simple way of doing that that I have missed out on, and I'm also sure that this information will be useful for a lot of people, so I will be offering a bounty on this question.
I have struggled with doing this so far :
I've created a CustomDataItem in the SampleDataSource.cs class :
/// <summary>
/// Generic item data model.
/// </summary>
public class CustomDataItem : SampleDataCommon
{
public CustomDataItem(String uniqueId, String title, String subtitle, String imagePath, String description, String content, SampleDataGroup group)
: base(uniqueId, title, subtitle, imagePath, description)
{
this._content = content;
this._group = group;
}
private string _content = string.Empty;
public string Content
{
get { return this._content; }
set { this.SetProperty(ref this._content, value); }
}
private SampleDataGroup _group;
public SampleDataGroup Group
{
get { return this._group; }
set { this.SetProperty(ref this._group, value); }
}
}
However, obviously, adding to the ObservableCollection
private ObservableCollection<SampleDataGroup> _allGroups = new ObservableCollection<SampleDataGroup>();
public ObservableCollection<SampleDataGroup> AllGroups
{
get { return this._allGroups; }
}
is impossible with a different data type. So what can I do in this case ?
Thanks a lot.
I have a simple grid application; how do I make it possible to have one of the elements in the group item page link to a custom item detail page ?
Ok, lets take the app that is generated when using the "Grid App" template from Visual Studio.
The data class for the elements on the group items page is the SampleDataItem class. What you can do is add some type of data field (bool, int, or other) that indicates how to handle the navigation. In this example, we are keeping it simple, so we add a bool to indicate whether the navigation is custom or not.
public class SampleDataItem : SampleDataCommon
{
// add flag as last param
public SampleDataItem(String uniqueId, String title, String subtitle,
String imagePath, String description, String content, SampleDataGroup group,
bool isCustomNav = false)
: base(uniqueId, title, subtitle, imagePath, description)
{
this._content = content;
this._group = group;
this.IsCustomNav = isCustomNav;
}
// to keep it simple this doesn't handle INotifyPropertyChange,
// as does the rest of the properties in this class.
public bool IsCustomNav { get; set; }
...
}
So when you are adding a new SampleDataItem object to be displayed, you just need to set the isCustomNav field in the constructor.
Now all we have to do is change the already existing click event handler in the grid on the grouped item page (GroupedItemsPage.xaml.cs):
void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
// Navigate to the appropriate destination page, configuring the new page
// by passing required information as a navigation parameter
var item = (SampleDataItem)e.ClickedItem;
var itemId = item.UniqueId;
if (item.IsCustomNav == false)
{
// default
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
}
else
{
// custom page
this.Frame.Navigate(typeof(ItemDetailPage2), itemId);
}
}
All we are doing above is getting the selected item and then testing the navigation flag that we added earlier. Based on this we navigate to either the original ItemDetailPage or a new one called ItemDetailPage2. As I mentioned before, the navigation flag doesn't have to be a bool. It can be an int or enum or some other type that tells us where to navigate.
Note that if you want similar behavior on the GroupDetailsPage, you just have to update the click event handler there the same way.
Hope that helps.
Yes you should be able to create a custom or different data type. If you create a Win8 app using the grid template, you see that the template does three things for you:
1) It creates three types, SampleDataCommon, which is the base, SampleDataItem, which implements SampleDataCommon and adds two new properties - content and group, and SampleDataGroup which also implements SampleDataCommon, adds a method, ItemsCollectionChanged, and adds two properties, Items and TopItems.
2) It creates a class called SampleDataSource, in which a collection of SampleDataGroup is created and named AllGroups: ObservableCollection AllGroups.
3) It binds Items and AllGroups of SampleDataSource to objects in XMAL pages.
In your case, you use the same data structure. In other words, you will create a group with items, etc.

Categories

Resources