Find EditText in a complex layout - xamarin.android

I have an Android UI layout that is rather complex (it has a lot added to it dynamically depending on business rules).
One part of the UI has a LinearLayout which contains another layout which contains an EditText. Sometimes there is another LinearLayout in the base layout with the edit text in - it looks like this
LinearLayout
|
+-EditText
|
+-LinearLayout
| |
| +-EditText
+-EditText
|
+-LinearLayout
| |
| +-LinearLayout
| | |
| | +-EditText
| +-EditText
And so on
Currently, I have a pile of loops in a method that check if it's LinearLayout, then checks if the next Child is an EditText or a LinearLayout. If it's another LinearLayout then it creates another loop, checks if there is an EditText and so on. It gets messy.
Is there a way in LINQ (or something similar) that I can iterate through an entire LinearLayout in order to pick out the EditText widgets?

I don't know of a way in LINQ as you've requested, and am not sure how you're looping in code, but a few years back I had a similar need, and created a class that will iterate every view, including deeply nested views.
The output string displays the full hierarchy, including children, grandchildren, etc. with appropriate indentations. Of course, you can modify the code to look for a specific view type and act on it, such as:
if (vChild is EditText) vChild.Enabled = false;
Hope this helps!
public class IterateView {
// Iterate a view and all children recursively and generate a visual hierarchy
#region Example Usage
// //OnCreate()...
// SetContentView(Resource.Layout.MyLayout);
// RelativeLayout view = FindViewById<RelativeLayout>(Resource.Id.MyRelativeLayoutView);
// IterateView iv = new IterateView();
// iv.Iterate(view);
// string s = iv.OutputString;
#endregion
private readonly StringBuilder sb = new StringBuilder();
private const string PADDING_STRING = " ";
private string sPadding = "";
public string OutputString { get; private set; }
public void Iterate(View view) {
if (view is ViewGroup)
IterateViewChildren(view);
else
sb.AppendLine(sPadding + view.GetType().Name);
OutputString = sb.ToString();
}
private void IterateViewChildren(View view) {
if (view is ViewGroup) {
sb.AppendLine(sPadding + view.GetType().Name + " (ViewGroup)");
sPadding += PADDING_STRING;
ViewGroup vGroup = (ViewGroup)view;
for (int i = 0; i < vGroup.ChildCount; i++) {
if (!(vGroup.GetChildAt(i) is ViewGroup))
sb.AppendLine(sPadding + vGroup.GetChildAt(i).GetType().Name);
View vChild = vGroup.GetChildAt(i);
IterateViewChildren(vChild);
}
// Remove padding after iterating children to get us back to where we need to be
sPadding = sPadding.Remove(sPadding.Length - PADDING_STRING.Length);
}
}
}

Related

Replacement for collapseItemsRecursively and expandItemsRecursively in Vaadin 8.1 TreeGrid

Vaadin 8.1 introduced the TreeGrid component. It does not have the collapseItemsRecursively and expandItemsRecursively methods anymore (as available in the now legacy Tree component). Do i miss something or do you need to develop your own implementation? If so, what is a recommended way of doing this?
As I'm sure you've noticed, the TreeGrid is a rather new component, currently being developed and available starting with v8.1.alphaX (current stable version is v8.0.6). As such, it probably has only some basic functionalities for the time being, with the rest to follow sometime in the future, although there are no guarantee. For example this similar feature request for the older TreeTable component has been in open state since 2011.
Either way, even if they're probably not the optimum solutions, there are a couple of work-arounds that you can use to achieve this behavior. I'm shamelessly using as a base sample, a slightly modified version of the code currently available in the vaadin-sampler for TreeGrid.
public class RecursiveExpansionTreeGrid extends VerticalLayout {
private Random random = new Random();
public RecursiveExpansionTreeGrid() {
// common setup with some dummy data
TreeGrid<Project> treeGrid = new TreeGrid<>();
treeGrid.setItems(generateProjectsForYears(2010, 2016), Project::getSubProjects);
treeGrid.addColumn(Project::getName).setCaption("Project Name").setId("name-column");
treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done");
treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified");
addComponent(treeGrid);
}
// generate some dummy data to display in the tree grid
private List<Project> generateProjectsForYears(int startYear, int endYear) {
List<Project> projects = new ArrayList<>();
for (int year = startYear; year <= endYear; year++) {
Project yearProject = new Project("Year " + year);
for (int i = 1; i < 2 + random.nextInt(5); i++) {
Project customerProject = new Project("Customer Project " + i);
customerProject.setSubProjects(Arrays.asList(
new LeafProject("Implementation", random.nextInt(100), year),
new LeafProject("Planning", random.nextInt(10), year),
new LeafProject("Prototyping", random.nextInt(20), year)));
yearProject.addSubProject(customerProject);
}
projects.add(yearProject);
}
return projects;
}
// POJO for easy binding
public class Project {
private List<Project> subProjects = new ArrayList<>();
private String name;
public Project(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Project> getSubProjects() {
return subProjects;
}
public void setSubProjects(List<Project> subProjects) {
this.subProjects = subProjects;
}
public void addSubProject(Project subProject) {
subProjects.add(subProject);
}
public int getHoursDone() {
return getSubProjects().stream().map(project -> project.getHoursDone()).reduce(0, Integer::sum);
}
public Date getLastModified() {
return getSubProjects().stream().map(project -> project.getLastModified()).max(Date::compareTo).orElse(null);
}
}
// Second POJO for easy binding
public class LeafProject extends Project {
private int hoursDone;
private Date lastModified;
public LeafProject(String name, int hoursDone, int year) {
super(name);
this.hoursDone = hoursDone;
lastModified = new Date(year - 1900, random.nextInt(12), random.nextInt(10));
}
#Override
public int getHoursDone() {
return hoursDone;
}
#Override
public Date getLastModified() {
return lastModified;
}
}
}
Next, recursively expanding or collapsing the nodes depends a bit on your scenario, but basically it breaks down to the same thing: making sure each node from the root to the deepest leaf is expanded/collapsed.The simplest way of doing it is to flatten your hierarchy into a list of nodes, and call the appropriate method, expand(List<T> items) or expand(T ... items) (the second delegates to the first and is probably a convenience method eg expand(myItem)).
For simplicity, I've added a flatten method in our Project implementation. If you can't do that for some reason, then create a recursive method that creates a list starting with the selected node and includes all the children, of the children, of the children.... well, you get the idea.
public Stream<Project> flatten() {
return Stream.concat(Stream.of(this), getSubProjects().stream().flatMap(Project::flatten));
}
Possible scenarios:
Automatically expand the entire hierarchy when expanding the root - add listeners, and expand/collapse the whole flattened hierarchy:
treeGrid.addCollapseListener(event -> {
if (event.isUserOriginated()) {
// event is triggered by all collapse calls, so only do it the first time, when the user clicks in the UI
// and ignore the programmatic calls
treeGrid.collapse(event.getCollapsedItem().flatten().collect(Collectors.toList()));
}
});
treeGrid.addExpandListener(event -> {
if (event.isUserOriginated()) {
// event is triggered by all expand calls, so only do it the first time, when the user clicks in the UI
// and ignore the programmatic calls
treeGrid.expand(event.getExpandedItem().flatten().collect(Collectors.toList()));
}
});
Expanding the hierarchy or part of it with a custom action, such as a context menu
GridContextMenu<Project> contextMenu = new GridContextMenu<>(treeGrid);
contextMenu.addGridBodyContextMenuListener(contextEvent -> {
contextMenu.removeItems();
if (contextEvent.getItem() != null) {
Project project = (Project) contextEvent.getItem();
// update selection
treeGrid.select(project);
// show option for expanding
contextMenu.addItem("Expand all", VaadinIcons.PLUS, event -> treeGrid.expand((project).flatten().collect(Collectors.toList())));
// show option for collapsing
contextMenu.addItem("Collapse all", VaadinIcons.MINUS, event -> treeGrid.collapse((project).flatten().collect(Collectors.toList())));
}
});
In the end, you should be getting this effect:
From the docs for treegrid, you can use the methods, collapse and expand, by passing a list or array of the treegrid's data items to expand or collapse:
treeGrid.expand(someTreeGridItem1, someTreeGridItem2);
treeGrid.collapse(someTreeGridItem1);
Also worthy of note, is a section showing the ability to prevent certain items from ever being collapsed

Web Api Routing Multiple DTOs Same POCO

I am in doubt about how to implement the Web API Routing when you have multiple DTOs for the same POCO.
Let us imagine the following scenario:
you have a POCO Class with. Let's say 100 properties
you need to display lists of that class in different platforms/UIs
i.e.
List 1 Prop A1 | Prop A2 | Prop A3 | Prop A4
List 2 Prop A21 | Prop A22 | Prop A23 | Prop A24 | Prop A25 | Prop A26 | Prop A27 | Prop A28 |
I will call it Company, since there are many examples using this class name
Is it correct to implement a Web API + DTOs strategy like this?
Classes are:
Company (the POCO class)
CompanyDetailDTO
CompanyLightListDTO
CompanyMediumListDTO
Example:
public class CompaniesController : ApiController
{
private MultiTierWebApiContext db = new MultiTierWebApiContext();
private static readonly Expression<Func<Company, CompanyDetailDTO>> AsCompanyDetailDTO =
x => new CompanyDetailDTO
{
Name = x.Name,
Email = x.Email,
isCustomer = x.isCustomer,
isSupplier = x.isSupplier,
Id = x.Id
};
private static readonly Expression<Func<Company, CompanyMediumListDTO>> AsCompanyMediumListDTO =
x => new CompanyMediumListDTO
{
Name = x.Name,
Email = x.Email,
Id = x.Id
};
private static readonly Expression<Func<Company, CompanyLightListDTO>> AsCompanyLightListDTO =
x => new CompanyLightListDTO
{
Name = x.Name,
Id = x.Id
};
// GET: api/Companies/LightList
[Route("api/Companies/LightList")] **It works, but is this a correct way to do it?**
[ResponseType(typeof(CompanyLightListDTO))]
public IQueryable<CompanyLightListDTO>GetCompaniesLightList()
{
return db.Companies.Select(AsCompanyLightListDTO); // default
}
// GET: api/Companies/MediumList
[Route("api/Companies/MediumList")]
[ResponseType(typeof(CompanyMediumListDTO))]
public IQueryable<CompanyMediumListDTO> GetCompaniesMediumList()
{
return db.Companies.Select(AsCompanyMediumListDTO); // default
}
// remaining code removed for simplicity
}
Thanks in advance for further help.
I would say you are on the right track with only providing the relevant information related to the particular DTO. Don't provide more data than necessary.
If you look at the following walk-through you will see how they follow a similar pattern to what you have in your example.
Create a REST API with Attribute Routing in ASP.NET Web API 2
Quoting for reference:
Instead, I want this request to return a subset of the fields. Also, I
want it to return the author's name, rather than the author ID. To
accomplish this, we'll modify the controller methods to return a data
transfer object (DTO) instead of the EF model. A DTO is an object that
is designed only to carry data.
// Typed lambda expression for Select() method.
private static readonly Expression<Func<Book, BookDto>> AsBookDto =
x => new BookDto
{
Title = x.Title,
Author = x.Author.Name,
Genre = x.Genre
};
// GET api/Books
public IQueryable<BookDto> GetBooks()
{
return db.Books.Include(b => b.Author).Select(AsBookDto);
}
My advice would be to separate the transformation/projection functionality out of the controller (SoC) into their own service(s) (SRP) and inject them (DI) into the ApiController. It will keep your Controllers light.

How to get List & Hierarchy of Linked and sub-linked Revit files

We're trying to get the list and hierarchy of all linked external files. Right now we tried the following code:
FilteredElementCollector collectorI = new FilteredElementCollector(DocChild);
IList<Element> elemsI = collectorI.OfCategory(BuiltInCategory.OST_RvtLinks).OfClass(typeof(RevitLinkInstance)).ToElements();
foreach (Element eI in elemsI)
{
if (eI is RevitLinkInstance)
{
RevitLinkInstance InstanceType = eI as RevitLinkInstance;
RevitLinkType type = DocChild.GetElement(InstanceType.GetTypeId()) as RevitLinkType;
TaskDialog.Show("Debug", "IsNestedLink=" + type.IsNestedLink.ToString() + " IsLinked=" + DocChild.IsLinked.ToString());
if (!type.IsNestedLink)
{
TaskDialog.Show("Debug", "Children=" + InstanceType.GetLinkDocument().PathName.ToString());
}
}
}
We succeed to get the list of all linked files but there's no hierarchy. We don't know which file is a children of which parent.
This is the Link structure we're trying to get:
enter image description here
You need to play with GetParentId and GetChilds methods to read the hierarchy. Here is a code:
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
// get active document
Document mainDoc = commandData.Application.ActiveUIDocument.Document;
// prepare to show the results...
TreeNode mainNode = new TreeNode();
mainNode.Text = mainDoc.PathName;
// start by the root links (no parent node)
FilteredElementCollector coll = new FilteredElementCollector(mainDoc);
coll.OfClass(typeof(RevitLinkInstance));
foreach (RevitLinkInstance inst in coll)
{
RevitLinkType type = mainDoc.GetElement(inst.GetTypeId()) as RevitLinkType;
if (type.GetParentId() == ElementId.InvalidElementId)
{
TreeNode parentNode = new TreeNode(inst.Name);
mainNode.Nodes.Add(parentNode);
GetChilds(mainDoc, type.GetChildIds(), parentNode);
}
}
// show the results in a form
System.Windows.Forms.Form resultForm = new System.Windows.Forms.Form();
TreeView treeView = new TreeView();
treeView.Size = resultForm.Size;
treeView.Anchor |= AnchorStyles.Bottom | AnchorStyles.Top;
treeView.Nodes.Add(mainNode);
resultForm.Controls.Add(treeView);
resultForm.ShowDialog();
return Result.Succeeded;
}
private void GetChilds(Document mainDoc, ICollection<ElementId> ids,
TreeNode parentNode)
{
foreach (ElementId id in ids)
{
// get the child information
RevitLinkType type = mainDoc.GetElement(id) as RevitLinkType;
TreeNode subNode = new TreeNode(type.Name);
parentNode.Nodes.Add(subNode);
// then go to the next level
GetChilds(mainDoc, type.GetChildIds(), subNode);
}
}
And the result should look like:
Original source blog post.
Thank you for the great answer. It helped foxing a big part of my problem but I just have a remaining issue which is: how to get the full path name of all instances. In some cases the same file is re-used two time in the Revit Link Hierarchy.
Regards

SpecFlow assist - create instance from table

I'm trying out the specFlow assist and not sure how would one create class property from table.
Imagine I have this class:
public class Tracking
{
public string Category { get; set; }
}
public class ODARequest
{
public string Title { get; set; }
public string Name { get; set; }
public Tracking Tracking { get; set; }
}
My Given scenario is next:
Scenario: Successfully create an account
Given I have entered the following data into the ODA form:
| Field | Value |
| Title | Mr |
| Name | Andy |
| Tracking Category | MDA |
public void GivenIHaveEnteredTheFollowingDataIntoTheODAForm(Table table)
{
var request = table.CreateInstance<ODARequest>();
}
The Tracking property will not be populated. Anyone know how to describe Tracking.Category in the table for this situation?
I haven't been able to find a way of getting specflow to map "CreateInstance" to non-standard data types properties.
However, in this case you could at least use a StepArgumentTransformation as follows:
[Given(#"I have entered the following data into the ODA form:")]
public void GivenIHaveEnteredTheFollowingDataIntoTheODAForm(ODARequest request)
{
Assert.IsNotNull(request.Tracking);
}
[StepArgumentTransformation(#".*")]
public ODARequest StringToTracking(Table input)
{
return new ODARequest() {
Title = input.Rows.Single(row => row["Title"])["value"],
Name = input.Rows.Single(row => row["Name"])["value"],
Tracking = new Tracking()
{ Category = input.Rows.Single(row => row["Field"] == "Tracking Category")["Value"] }
};
}
With a little work you could tidy the stepargumenttransformation up to accept each parameter as optional (rather than having "single()" throw if it is omitted).
I feel like there really ought to be a better way to do this, more like your original suggested code.
Hope this is helpful.
I've been hitting this problem and been thinking how to do it with a single step.
Scenario: Successfully create an account
Given I have entered the following data into the ODA form:
|Title | Name | Category|
| Mr | Andy | MDA |
public void GivenIHaveEnteredTheFollowingDataIntoTheODAForm(Table table)
{
var request = table.CreateInstance<ODARequest>();
request.Tracking = table.CreateInstance<Tracking>();
}
How it works:
You may call "CreateInstance" for each complex property you have so specflow will create you an instance. So you can have a single table with properties from different types.
By doing so you won't need a different step and the need of sharing data between the steps.
The drawback is that you may end up with a huge table if your class has a lot of properties with different types.
Note: As #Alex M commented it there is a risk when the classes are having a property with same name. What will happen actually is that both instances will get the same value due to the property name match on both classes.
Another possibility without modifying the specflow assist itself is to separate the subclass properties into separate Given.
Can be as per below example:
Scenario: Successfully create an account
Given I have entered the following data into the ODA form:
| Field | Value |
| Title | Mr |
| Name | Andy |
And the tracking info as:
| Tracking Category | MDA |
then my step definitions would be as:
[Given(#"I have entered the following data into the ODA form:")]
public void GivenIHaveEnteredTheFollowingDataIntoTheODAForm(Table table)
{
var request = table.CreateInstance<ODARequest>();
ScenarioContext.Current.Set(request, "request");
}
[Given(#"the tracking info as:")]
public void GivenTheTrackingInfoAs(Table table)
{
var request = ScenarioContext.Current.Get<ODARequest>("request");
request.TrackingFields = table.CreateInstance<Tracking>();
}
Otherwise the possibility is to contribute to specflow assist development.

How do you switch View Engines on the fly within an ASP.Net MVC Controller action?

I want to write a custom view engine that returns custom text (like coma delimited) does anyone know how I'd change the view engine on the fly to handle this?
I'd create a custom ActionResult. I use Json() function to return a JsonResult when I need JSON as response. I use this code to fill a ExtJS tree using JSON data.
public JsonResult Folders(string node)
{
var relativePath = (node == "root") ? "" : node;
var path = Path.Combine(BASE_PATH, relativePath);
var folder = new DirectoryInfo(path);
var subFolders = folder.GetDirectories();
var folders = new List<ExtJsTreeNode>();
foreach (var subFolder in subFolders)
{
folders.Add(new ExtJsTreeNode(subFolder.Name, subFolder.FullName.Replace(BASE_PATH, ""), "folder"));
}
return Json(folders);
}
private class ExtJsTreeNode
{
public string text { get; set; }
public string id { get; set; }
public string cls { get; set; }
public ExtJsTreeNode(string text, string id, string cls)
{
this.text = text;
this.id = id;
this.cls = cls;
}
}
A sample of a custom ActionResult here.
Your controller shouldn't know or care about this, other than which View to send the data to. The View can render in any format imaginable. I've got views that emit RSS (XML), etc. In the controller, either send it to the default view or explicitly identify the target view.
If I understood your question correctly, you want to use different views based on the parameters passed to the controller. If so, you can use this statement in the controller action:
return View("ViewName");
Otherwise, please clarify your question.

Resources