MONOTOUCH get row item selected - uiviewcontroller

I have a navigation controller to show a list o item on table, then I need to touch an item a show the details of this item.
Here it's my code of how a fill the table:
public void SearchHotel (){
Hotel hotel = new Hotel();
var distribution = new HotelDistribution[]{new HotelDistribution(){ Adults = 1, Children = 0, ChildrenAges = new int[0]} };
var items = hotel.SearchHotels(Convert.ToDateTime("2013-08-08"),Convert.ToDateTime("2013-09-09 "),"(MIA)", distribution,"","","",0);
data = new List<DtoHotelinformation>();
foreach (var item in items)
{
DtoHotelinformation DtoHotelinformation = new DtoHotelinformation();
DtoHotelinformation.code = item.Code.ToString();
DtoHotelinformation.price = item.Price.ToString();
DtoHotelinformation.title = item.Name.ToString().ToTitleCase();
DtoHotelinformation.subtitle = item.Address.ToString();
DtoHotelinformation.rating = item.Rating.ToString();
DtoHotelinformation.imageUlr = item.ImageUrl;
data.Add(DtoHotelinformation);
}
hud.Hide(true);
hud.RemoveFromSuperview();
HotelSearchTable.Source = new HotelTableSource(data.ToArray());
HotelSearchTable.ReloadData();
}
Because I'm using storyboard to show the details view controller I have this code on my table source:
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
if (RowTouched != null) {
RowTouched (this, EventArgs.Empty);
}
tableView.DeselectRow (indexPath, true); // normal iOS behaviour is to remove the blue highlight
}
Back in to my viewcontroller I call the RowTouched to show the details controller like this:
public override void ViewDidAppear (bool animated) {
base.ViewDidAppear (animated);
SearchHotel ();
var source = new HotelTableSource(data.ToArray());
var detail = Storyboard.InstantiateViewController("HotelDetailScreen") as iPhoneHotelDetailViewController;
source.RowTouched += (sender, e) => {
this.NavigationController.PushViewController(detail, true);
};
HotelSearchTable.Source = source;
}
But I need to pass the information of the item touched on the table to show the details. I don't really don't know what do I have to do?
NOTE: I can't use PrepareForSegue because I don't have a segue between controllers.
Thanks in advance

If you want you can get a hold of your UINavigationController
from within your Row Selected event, inside of your Table Data Source.
From there you can push your new ViewController.
[indexPath.Row] in "Row_Selected" should tell you which element in your array or list, that the user has selected.
UINavigationController navcontroller =(UINavigationController)UIApplication.SharedApplication.Windows[0].RootViewController;

Passing data to an event handler is exatly what the EventArgs parameter is for.
Create a class that inherits from EventArgs and has properties for the data you need:
public class HotelSelectedEventARgs : EventArgs
{
public DTOHotelInformation HotelInfo { get; set; }
}
Then, when you call your handler in RowSelected, instead of passing an empty EventArgs, create an instance of your custom class and assign the selected data to the HotelInfo property.

Related

How to handle form post from View Component (Razor Core 2)

This weekend a lot of struggle with a View Component.
I try to add a dropdownlist that does an auto postback onchange. This dropdownlist is on a view component.
I have 2 problems:
I don't get the asp-page-handler after the post, does it work like I implemented it on the form-tag?
Post calls method public void OnPost on razor page containing view
component. I would think it would be better to have a method on the
View Component like OnChangeProject?
The code of my View (View Component):
<form asp-page-handler="ChangeProject" method="post">
#Html.AntiForgeryToken()
#Html.DropDownList("id", new SelectList(Model, "Id", "Id"), new { onchange = "this.form.submit()" })
</form>
Thanks in advance!!
I exprienced the same problem and the way i fixed it is already answered in your question.
The form call is made at the page where you got your View Component embedded. I don't think it would be even possible to call a handler in your View Component with asp-page-handler as this is Razor Pages tag helper.
The way i got it work is simply putting the page-handler method on the PageModel that is embedding the View Component. In your case you can simply implement this handler on your Razor Page:
public IActionResult OnPostChangeProject()
{
// ... do Something
}
I don't know though how it would work to trigger a controller method in your View Component. Possibly create a new Controller class and route to it with asp-controller and asp-action in your form tag.
You should remember that the Page handlers could be viewed as convenience methods.
All the ASP.Net Core framework does is looks at the Query string parameters and Form data and translates it into Page handler calls.
And even though the Handlers are not available in View Components or Partial Views you still can get access to all the required ingredients by injecting IHttpContextAccessor into the View.
It will provide you with HttpContext.Request which contains both the Query and the Form properties.
You can then create your own Handler mapper. Here is one, for example:
public class HandlerMapping
{
public string Name { get; set; }
public System.Delegate RunDelegate { get; set; }
public HandlerMapping(string name, Delegate runDelegate)
{
RunDelegate = runDelegate;
Name = name;
}
}
public class PartialHandlerMapper
{
IHttpContextAccessor _contextAccessor;
public PartialHandlerMapper(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
public void RouteHandler(List<HandlerMapping> handlerMappings, string PartialDescriminatorString = null)
{
var handlerName = _contextAccessor.HttpContext.Request.Query["handler"];
var handlerMapping = handlerMappings.FirstOrDefault(x => x.Name == handlerName);
if (handlerMapping != null)
{
IFormCollection form;
try
{
form = _contextAccessor.HttpContext.Request.Form;
}
catch
{
return;
}
if (!string.IsNullOrWhiteSpace(PartialDescriminatorString) && form[nameof(PartialDescriminatorString)] != PartialDescriminatorString)
return;
List<Object> handlerArgs = new List<object>();
var prmtrs = handlerMapping.RunDelegate.Method.GetParameters();
foreach (var p in prmtrs)
{
object nv = null;
var formValue = form[p.Name];
if (!StringValues.IsNullOrEmpty(formValue))
{
try
{
nv = TypeDescriptor.GetConverter(p.ParameterType).ConvertFromString(formValue);
}
catch (FormatException)
{
//throw new FormatException($"Could not cast form value '{formValue}' to parameter {p.Name} (type {p.ParameterType}) of handler {handlerName}. Make sure you use correct type parameter. ");
nv = Activator.CreateInstance(p.ParameterType);
}
catch (ArgumentException)
{
nv = Activator.CreateInstance(p.ParameterType);
}
}
else
nv = Activator.CreateInstance(p.ParameterType);
handlerArgs.Add(nv);
}
handlerMapping.RunDelegate.DynamicInvoke(handlerArgs.ToArray());
}
}
}
And inject it into the service container:
services.AddScoped<PartialHandlerMapper>();
And here is a shopping cart partial view code section example:
#inject ShoppingManager shoppingManager
#inject PartialHandlerMapper partialHandlerMappping
#{
string ToggleCartItemTrialUseHandler = nameof(ToggleCartItemTrialUseHandler);
string DeleteCartItemHandler = nameof(DeleteCartItemHandler);
List<HandlerMapping> handlerMappings = new List<HandlerMapping> {
new HandlerMapping (ToggleCartItemTrialUseHandler, (Guid? PicID, bool? CurrentValue) => {
if (PicID == null || CurrentValue == null)
return;
shoppingManager.UpdateTrial((Guid)PicID, !(bool)CurrentValue);
}),
new HandlerMapping (DeleteCartItemHandler, (Guid? PicID) => {
if (PicID == null)
return;
shoppingManager.RemoveProductFromCart((Guid)PicID);
})
};
partialHandlerMappping.RouteHandler(handlerMappings);
var cart = shoppingManager.GetSessionCart();
}
Form element example from the same view:
<td align="center" valign="middle">
<form asp-page-handler="#DeleteCartItemHandler">
<input name=PicID type="hidden" value="#i.PicID" />
<button>
Delete
</button>
</form>
</td>
Where #i is an Item in the shopping cart
It's possible to create a combo (Controller/ViewComponent) by decorating the controller with a ViewComponent(Name="myviewcomponent").
Then create the invokeasync as usual, but because the controller doesn't inherit from a ViewComponent, the return result would be one of the ViewComponent result (ViewViewComponentResult, et).
The form in the viewcomponent can then have a button with asp-controller/action tag helpers targetting the controller/action.

Saving an MVC View to PDF

As the title suggests, I am looking for a way to export a .NET MVC View to a PDF.
My program works like this:
Page 1
Takes in information
Page 2
Takes this information and heavily styles it with CSS etc
So basically I need to save page 2 after it has been processed and used the information from Page 1's model.
Thanks in advance!
To render a non-static page to a pdf, you need to render the page to a string, using a ViewModel, and then convert to a pdf:
Firstly, create a method RenderViewToString in a static class, that can be referenced in a Controller:
public static class StringUtilities
{
public static string RenderViewToString(ControllerContext context, string viewPath, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
{
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
}
else
{
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
}
if (viewEngineResult == null)
{
throw new FileNotFoundException("View cannot be found.");
}
// get the view and attach the model to view data
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result.Trim();
}
}
Then, in your Controller:
var viewModel = new YourViewModelName
{
// Assign ViewModel values
}
// Render the View to a string using the Method defined above
var viewToString = StringUtilities.RenderViewToString(ControllerContext, "~/Views/PathToView/ViewToRender.cshtml", viewModel, true);
You then have the view, generated by a ViewModel, as a string that can be converted to a pdf, using one of the libraries out there.
Hope it helps, or at least sets you on the way.

JavaFX : TableView inside Dialog has duplicate items

I have an issue with my TableView and its items. I have created a small Dialog window to display warnings about my app, and inside the Dialog I have a TableView which displays the name of the warning and some information about it upon clicking on a button.
I have created a WarningUtil class (Singleton pattern) just to open / close the Dialog. The relevant code follows.
The constructor of the WarningUtil class (called once only) :
private WarningUtil(RootCtrl rootCtrl) {
this.rootCtrl = rootCtrl;
warnings = new HashMap<>();
setupWarningCallbacks(); // not relevant
setupTable();
setupColumns(); // not relevant
setupDialog();
}
The function managing the construction of the Dialog :
private void setupTable() {
// create the content pane
content = new AnchorPane(); // class variable - reference needed for further uses
content.setPrefSize(480, 240);
// create the root nodes of the view (table + 2 columns)
warningTable = new TableView<>(); // class variable - reference needed for further uses
warnDescriptionCol = new PTableColumn<>(); // class variable - reference needed for further uses
warnDetailsCol = new PTableColumn<>(); // class variable - reference needed for further uses
// settings anchors to keep the ration between dialog <-> table
AnchorPane.setBottomAnchor(warningTable, 15.0);
AnchorPane.setTopAnchor(warningTable, 15.0);
AnchorPane.setLeftAnchor(warningTable, 15.0);
AnchorPane.setRightAnchor(warningTable, 15.0);
// setting up the columns
warnDescriptionCol.setText(i18n("label.desc"));
warnDetailsCol.setText(i18n("label.details"));
warnDescriptionCol.setPercentageWidth(0.7);
warnDetailsCol.setPercentageWidth(0.3);
warnDescriptionCol.setResizable(false);
warnDetailsCol.setResizable(false);
// adding nodes to containers
warningTable.getColumns().addAll(warnDescriptionCol, warnDetailsCol);
content.getChildren().add(warningTable);
}
The function used to create the Dialog and set the content :
private void setupDialog() {
// creation and saving of the dialog in a variable reused later
warningDialog = DialogFactory.getInstance(rootCtrl.getPrimaryStage()).createWarningDialog();
warningDialog.getDialogPane().setContent(content);
warningDialog.getDialogPane().getScene().getWindow().sizeToScene();
}
// The DialogFactory function creating the dialog
public Dialog createWarningDialog(){
CustomDialog dialog = new CustomDialog(rootStage);
dialog.setTitle(i18n("warning.description"));
ButtonType cancelBt = new ButtonType(i18n("button.close"), ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().add(cancelBt);
return dialog.setupLayout();
}
The Main class is in charge of loading the warnings (stored in a .json file and deserialized upon starting the app). For now, the file only contains one entry.
When I click on my Warning button, the following function is called :
public void showWarnings() {
warningTable.getItems().clear(); // BP
warningTable.setItems(FXCollections.observableArrayList(warnings.values()));
warningDialog.showAndWait();
}
What happens is the following : When I have only one entry in my .json file, the first time I click on the button, only one warning is shown. If I click a second time, a second entry appears (the same) which should not be possible because of the following reasons :
Logic constraint : warnings.values() comes from an HashMap where the key is the type of the warning (WarningType class) > Not possible to have two identical keys
Debugging : When I set a breakpoint at "//BP", I clearly see that the warningTable has one item, and after clear the number of items is zero
Debugging : Still with the same breakpoint, I also check that warnings.values() has only one item, which is the case
After five clicks on the button, the Dialog clearly shows something is bugging.
More surprisingly, when I add a second warning (different from the first one, another type), the problem does not occur : No duplicates, warnings are correctly displayed and no matter how many times I open the window.
My question is : Could that be that the way I am creating this warning dialog leads to uncommon errors ? If so, why isn't it the case with two warnings ?
EDIT Include of the cellFactories / cellValueFactories
private void setupColumns() {
warnDescriptionCol.setCellFactory(new Callback<TableColumn<CustomWarning, String>, TableCell<CustomWarning, String>>() {
#Override
public TableCell<CustomWarning, String> call(TableColumn<CustomWarning, String> param) {
TableCell<CustomWarning, String> cell = new TableCell<CustomWarning, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
Label label = new Label(item);
setGraphic(label);
}
}
};
return cell;
}
});
warnDetailsCol.setCellFactory(new Callback<TableColumn<CustomWarning, CustomWarning>, TableCell<CustomWarning, CustomWarning>>() {
#Override
public TableCell<CustomWarning, CustomWarning> call(TableColumn<CustomWarning, CustomWarning> param) {
TableCell<CustomWarning, CustomWarning> cell = new TableCell<CustomWarning, CustomWarning>() {
#Override
protected void updateItem(CustomWarning item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
Button button = new Button(i18n("button.view"));
button.getStyleClass().add("save");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
showWarning(item);
}
});
setGraphic(button);
}
}
};
return cell;
}
});
warnDescriptionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, String>, ObservableValue<String>>() {
TableViewObjectWrapper<CustomWarning, String> wrapper = new TableViewObjectWrapper<CustomWarning, String>() {
#Override
public String getData() {
return getModel().getTitle();
}
};
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<CustomWarning, String> param) {
return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
}
});
warnDetailsCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, CustomWarning>, ObservableValue<CustomWarning>>() {
TableViewObjectWrapper<CustomWarning, CustomWarning> wrapper = new TableViewObjectWrapper<CustomWarning, CustomWarning>() {
#Override
public CustomWarning getData() {
return getModel();
}
};
#Override
public ObservableValue<CustomWarning> call(TableColumn.CellDataFeatures<CustomWarning, CustomWarning> param) {
return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
}
});
}
You have to clear your cells in the cell factory if the cell is empty, as explained in the documentation:
It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points:
We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly.
Since the cells are reused, you have to clear the graphic if it has become empty, not just set it if it's not.

How do I refresh/update a LongListSelector after removing a MenuItem (on Windows Phone 8)?

I am trying to delete a MenuItem from a LongListSelector in my Windows Phone 8 app. The MenuItems play various sounds when clicked and I want the user to be able to delete them.
There are two panels on the app. The second panel records a new sound and puts the recording on the LongListSelector as a new MenuItem.
Problem: After I do the delete the display looks exactly the same and the sound still plays! However, if I record a new sound (switching to the new recording panel) then the deletion works with the deleted MenuItem gone.
How do I force the update/refresh of the LongListSelector to unload/delete the MenuItem and associated sound data resident on the GUI?
The following code is called from from the MenuItem's click event. The LongListSelector is named 'CustomSounds':
private void DeleteSoundClick(object sender, RoutedEventArgs e)
{
var menuItem = sender as MenuItem;
if (menuItem == null) return;
var soundData = menuItem.DataContext as SoundData;
if (soundData == null) return;
if (soundData.FilePath.Contains(CustomSounds.Name))
{
CustomSounds.ItemsSource.Remove(soundData);
}
this.LayoutRoot.UpdateLayout();
}
Inverse your thing. Set item source with new list.
private void DeleteSoundClick(object sender, RoutedEventArgs e)
{
var menuItem = sender as MenuItem;
if (menuItem == null) return;
var soundData = menuItem.DataContext as SoundData;
if (soundData == null) return;
if (soundData.FilePath.Contains(CustomSounds.Name))
{
MyNewList.remove(soundData);
CustomSounds.ItemsSource = myNewList;
}
this.LayoutRoot.UpdateLayout();
}
Create local variable and set your itemsource in constructor.
Other thing :
create an updated list :
private ObservableCollection<Sound> _myNewList;
public ObservableCollection<Sound> MynewList{
get
{
return _myNewList;
}
set
{
_myNewList= value;
RaisePropertyChanged(MynewList);
}
Bind this in your listbox :
<listbox itemSource="{Binding MyNewList" SelectedItem="{Binding SelectedSound,mode=twoway}>
Create selectedSound:
private Sound _selectedSound;
public Sound SelectedSound{
get
{
return _selectedSound;
}
set
{
_selectedSound= value;
RaisePropertyChanged(SelectedSound);
}
Delete item :
private void DeleteSoundClick(object sender, RoutedEventArgs e)
{
if(SelectedSound != null){
Mynewlist.remove(SelectedSound);
}
}
:D
ObservableCollection is nothing more than a collection with notification that when something is changed in the collection, it lets the UI know. It's just a ItemSource, so your LongListSelector is populated by the OC...
I had the same issue, however, I used simple List.
Solution was easy - just created new object of the List
var newList = new List<TheModel>();
newList.AddRange(originalList);
TheLongListSelector.ItemsSource = newList;
TheLongListSelector.UpdateLayout();
originalList was list with removed item.
It's not the most efficient way, but it works. I think that's no problem for small data.

Find out what fields are being updated

I'm using LINQ To SQL to update a user address.
I'm trying to track what fields were updated.
The GetChangeSet() method just tells me I'm updating an entity, but doesn't tell me what fields.
What else do I need?
var item = context.Dc.Ecs_TblUserAddresses.Single(a => a.ID == updatedAddress.AddressId);
//ChangeSet tracking
item.Address1 = updatedAddress.AddressLine1;
item.Address2 = updatedAddress.AddressLine2;
item.Address3 = updatedAddress.AddressLine3;
item.City = updatedAddress.City;
item.StateID = updatedAddress.StateId;
item.Zip = updatedAddress.Zip;
item.Zip4 = updatedAddress.Zip4;
item.LastChangeUserID = request.UserMakingRequest;
item.LastChangeDateTime = DateTime.UtcNow;
ChangeSet set = context.Dc.GetChangeSet();
foreach (var update in set.Updates)
{
if (update is EberlDataContext.EberlsDC.Entities.Ecs_TblUserAddress)
{
}
}
Use ITable.GetModifiedMembers. It returns an array of ModifiedMemberInfo objects, one for each modified property on the entity. ModifiedMemberInfo contains a CurrentValue and OriginalValue, showing you exactly what has changed. It's a very handy LINQ to SQL feature.
Example:
ModifiedMemberInfo[] modifiedMembers = context.YourTable.GetModifiedMembers(yourEntityObject);
foreach (ModifiedMemberInfo mmi in modifiedMembers)
{
Console.WriteLine(string.Format("{0} --> {1}", mmi.OriginalValue, mmi.CurrentValue));
}
You can detect Updates by observing notifications of changes. Notifications are provided through the PropertyChanging or PropertyChanged events in property setters.
E.g. you can extend your generated Ecs_TblUserAddresses class like this:
public partial class Ecs_TblUserAddresses
{
partial void OnCreated()
{
this.PropertyChanged += new PropertyChangedEventHandler(User_PropertyChanged);
}
protected void User_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string propertyName = e.PropertyName;
// do what you want
}
}
Alternatively, if you want to track a special property changing, you could use one of those OnPropertyNameChanging partial methods, e.g. (for City in your example):
partial void OnCityChanging(string value)
{
// value parameter holds a new value
}