How do I avoid .Parent.Parent.Parent. etc. when referencing control hierarchies? - c#

I'm trying to fix this ugly code.
RadGrid gv = (RadGrid) (((Control) e.CommandSource).Parent.Parent.Parent.Parent.Parent);
I often need to find the first grid that is the parent of the parent of... etc of a object that just raised an event.
The above tends to break when the layout changes and the number of .Parents increase or decreases.
I don't necessarily have a control Id, so I can't use FindControl().
Is there a better way to find the 1st parent grid?

Control parent = Parent;
while (!(parent is RadGrid))
{
parent = parent.Parent;
}

If you really have to find the grid, then you might something like this:
Control ct = (Control)e.CommandSource;
while (!(ct is RadGrid)) ct = ct.Parent;
RadGrid gv = (RadGrid)ct;
But maybe you can explain why you need a reference to the grid? Maybe there is another/better solution for your problem.

I'm not familiar with the API that you are using, but can you do something like:
Control root = ((Control)e.CommandSource);
while(root.Parent != null)
{
// must start with the parent
root = root.Parent;
if (root is RadGrid)
{
// stop at the first grid parent
break;
}
}
// might throw exception if there was no parent that was a RadGrid
RadGrid gv = (RadGrid)root;

If you have control of the Parent's code, you could use simple recursion to do it, I'd think. Something like:
public Control GetAncestor(Control c)
{
Control parent;
if (parent = c.Parent) != null)
return GetAncestor(parent);
else
return c;
}
I make no claims as to how well that will work, but it should get the idea across. Navigate up the parent chain as far as it goes until there is no parent, then return that object back up the recursion chain. It's brute force, but it will find the first parent no matter how high it is.

Related

Is there any way to find previous sibling in Coded UI

I have a Control which is first children of the Parent but nothing is unique in Parent and in the Control. I can find unique properties for Scond children so I need to find the previous Sibling of Second children
This is possible by using the SearchConfiguration property on the control.
What you do is you search for the label and next you instantiate a WinEdit control and pass it in the label search control. Next you set on the WinEdit instance the SearchConfiguration.Add(SearchConfiguration.NextSibling)
this will now search for the next sibling of the text label.
in a code example this looks like follows:
var app = ApplicationUnderTest.Launch(#"yourapplication.exe");
var mainWindow = new WinWindow(app);
mainWindow.WindowTitles.Add("Form1");
WinText textLabel = new WinText(mainWindow);
textLabel.SearchProperties.Add(WinControl.PropertyNames.Name, "Some Text Label");
WinEdit siblingEditControl = new WinEdit(textLabel);
siblingEditControl.SearchConfigurations.Add(SearchConfiguration.NextSibling);
siblingEditControl.Text = "setting the text";
There are no direct methods for getting siblings. One way of finding the siblings of a control is to find its parent and then find all the children of that parent. Next search through the children to find the current control and then take the previous one. The method might be based on the following. It uses the Name fields of the control for the comparison, this may be incorrect for the general case and I suggest other values be tried.
public UITestControl GetPreviousSibling(UITestControl uitc)
{
UITestControlCollection siblings = uitc.GetParent().GetChildren();
// Note that 'sibings[0]' has no predecessor
for (int ii=1; ii<siblings.Count)
{
if (uitc.Name == siblings[ii].Name)
{
return siblings[ii - 1];
}
}
return null;
}
The definition of "sibling" is not clear. This MSDN blog gives some details of siblings.

Problem looping through web controls

I've got a web page where I am dynamically creating controls during Page_Load event (this is done so because I do not know how many controls I will need until session is active and certain variables are accessible)
I need to be able to loop through these controls to find Checkbox when a button click is processed. Looping through the Form.Controls does not appear to be sufficient. I would think that Request.Form might work but it does not appear to be accessible in my C# block?
What should code for Request.Form look like? OR
Has anyone done this before with dynamically created controls?
Any insight is appreciated.
Simplified Example from MSDN:
var myControl = FindControl("NameOfControl");
if(myControl != null)
{
//do something
}
else
{
//control not found
}
Hope this helps! ;)
Your controls will be accessible trough the Controls collection of their immediate parent. Unless you add them like Page.Form.Controls.Add (myControl);, you won't find it in Page.Form.Conttrols. If you add them to a place holder, you must find them in thePlaceHolder.Controls.
LinkButton myDynamicLinkButton = new myDynamicLinkButton ();
myDynamicLinkButton.ID = "lnkButton";
myPlaceHolder.Controls.Add (myDynamicLinkButton);
//........
LinkButton otherReferenceToMyLinkButton = myPlaceHolder.FindControl ("lnkButton");
As #David said in his comment, you should probably think about using a Repeater instead. It would probably simplify your case a lot.
Since the controls might be nested in other controls, you need to search recursively. You can use this method to find the control:
public Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
And you can implement it this way:
CheckBox check = FindControlRecursive(Page.Form, "CheckBox1");
You should have access to Request["xyz"] anywhere in your aspx.cs code. You can either find control as described above and read it's value or do so directly from Request using the Control.UniqueID property. For example if it's a checkbox that's within the repeater then the UniqueID would look like dtgData$ctl02$txtAmount
Thanks for the insight guys. I kind of took the discussion and ran with it and found my solution that worked best for me.
foreach(String chk in Request.Form)
{
if (chk.Contains("chkRemove"))
{
int idxFormat = chk.LastIndexOf("chkRemove");
objectname = chk.Substring(idxFormat);
}
}
Turned out really all I needed was the name. The string contained a number at the end which was needed to determine a position of datatable items. Thanks for the advice!

How to find a control or page a control is embedded In

I've written a web user control which I want to be able to drop into the markup for either aspx pages or other web user controls.
I need my user control to be able to easily and efficiently work out if its inside another user control or an aspx page. My initial idea is to do it recursively with checks on the Parent property - continue looking up the nesting hierarchy until I find either a web form or a user control - but I'm not sure this the best way of going about this.
Can you suggest an easier way? Thanks.
Recursively check the type of your Parent until Parent.GetType() is either typeof(UserControl) or type(Page)
private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
var parent = c.Parent;
if (parent == null) return false;
if (typesToCheck.Contains(parent.GetType())) return true;
return IsAncestorTypeOf(parent, typesToCheck);
}
Or the same without recursion
private bool IsAncestorTypeOf(Control c, params Type[] typesToCheck)
{
var parent = c.Parent;
while (true)
{
if (parent == null) return false;
if (typesToCheck.Contains(parent.GetType())) return true;
parent = parent.Parent;
}
}
Call it like
var isAncestorPageOrUserControl = IsAncestorTypeOf(this, typeof(Page), typeof(UserControl));
or
var isAncestorPage = IsAncestorTypeOf(this, typeof(Page));
var isAncestorUserControl = IsAncestorTypeOf(this, typeof(UserControl));
Generally, components should be unaware of their arbitrary containers, although the containers must know their components (unless it's a strong dependency situation like list items are always in a list type and you can make a strong two way relationship). However it sounds like you are reaching out into the general surroundings. You might find many cases to code for doing this and accidentally miss others.
By making the user control aware of its surroundings and the larger world you may be introducing dependencies that make your control less reusable and harder to maintain.
If something the control needs is outside of itself, you might move toward composition by forcing the developer to provide a reference to the needed thing on a property of your user control. This is the way, for example, that validation controls in ASP.NET do it, to reference an external control to validate by id.
Of course what I specified is practical only some of the time. Is there a specific reason or edge case why you need to make your user control look around itself, or can you get away with providing instructions to the developer about where the control should be used?
This should work:
C#
bool inPage = (this.NamingContainer == this.Page);
VB.NET
Dim inPage as Boolean = Me.NamingContainer is Me.Page
Edit: it seems to be not as simple as i hoped. If the usercontrol resists in a control like a GridViewRow, the NamingControl of it would be the Row and not the Page.
This takes it into account:
C#
public static bool isControlInPageOruserControl(Control uc)
{
bool inPage = uc.NamingContainer is Page;
if (inPage) {
return true;
} else if (uc.NamingContainer is UserControl) {
return false;
} else {
return isControlInPageOruserControl(uc.NamingContainer);
}
}
VB.NET:
Public Shared Function isControlInPageOruserControl(ByVal uc As Control) As Boolean
Dim inPage As Boolean = TypeOf uc.NamingContainer Is Page
If inPage Then
Return True
ElseIf TypeOf uc.NamingContainer Is UserControl Then
Return False
Else
Return isControlInPageOruserControl(uc.NamingContainer)
End If
End Function

Trouble with FindControl and dynamicly created controls

Example code:
var div = new HtmlGenericControl("div");
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
Assert.IsNotNull(lit);
This code fails the assert, because lit is null. Debugging shows that div.Controls definitely contains a literal with ID of "litSomeLit." My questions are "Why?" and "Is there any way to get a control of a specific ID without doing a recursive search of div.Controls[] by hand one element at a time?"
The reason I'm doing things this way is that my actual application is not so straightforward- a method I'm writing is given a complex control with several subcontrols in a number of possible configurations. I need to access a specific control several layers down (eg, the control with ID "txtSpecificControl" might be at StartingControl.Controls[0].Controls[2].Controls[1].Controls[3]). Normally I could just do FindControl("txtSpecificControl"), but that does not seem to work when the controls were just dynamically created (as in the above example code).
Near as I can tell, there is no way to do what I'm trying to accomplish without adding the control to the page. If I had to guess, I'd say that FindControl uses the UniqueID property of the control, which generally contains the IDs of all the controls above the current one (eg OuterControlID$LowerControlId$TargetControlID). That would only get generated when the control actually gets added to the page.
Anyway, here's an implementation of recursive depth-first-search FindControl that'll work when the control is not attached to the page yet:
public static Control FindControl(Control parent, string id)
{
foreach (Control control in parent.Controls)
{
if (control.ID == id)
{
return control;
}
var childResult = FindControl(control, id);
if (childResult != null)
{
return childResult;
}
}
return null;
}
Change your code to
var div = new HtmlGenericControl("div");
Page.Controls.Add(div);
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
As far as i know FindControl only works when the control is in the visual tree of the page.
When you confirmed that the control was in the Controls collection, did you do that by inspecting the collection directly? FindControl() may not work in this context.
When you debug the test, is the var lit null? If so, you may have to access the member by item index instead of using the FindControl() method.

Drop Item into Specific Index in ListView in WPF C#

I have a list of files in a ListView in WPF. Users can drag files onto the list view, and right now they are just appended to the end of the list. Is it possible to insert the file into the ListView right where the user dropped it?
WPF isn't really designed to be used that way. While you can brute force add ListViewItem's directly to the ListView, the way it's really supposed to work is that you have a collection of some kind (ObservableCollection<FileInfo> would work well) and bind the ListView's ItemsSource property to that collection.
Then the answer is simple. Instead of the Add method, you use the Insert method of the collection which takes an index.
As for finding which ListViewItem the mouse event occurred over, you could use the VisualTreeHelper.HitTest method.
From my point of view it is little tricky when I used the templated item. I have fight with it little bit. I am sharing my usecase which works with DraggableListBox. But I suppose the same solution works with ListBox control.
As the first I created the dependency object extension which is able to provide me ListItem element:
public static class WpfDomHelper
{
public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
}
Then I implemented Drop logic which inserts(adds) item according specific Drop Y position of destination ListBoxItems:
private void Grid_Drop(object sender, DragEventArgs e)
{
int dropIndex = -1; // default position directong to add() call
// checking drop destination position
Point pt = e.GetPosition((UIElement)sender);
HitTestResult result = VisualTreeHelper.HitTest(this, pt);
if (result != null && result.VisualHit != null)
{
// checking the object behin the drop position (Item type depend)
var theOne = result.VisualHit.FindParent<Microsoft.TeamFoundation.Controls.WPF.DraggableListBoxItem>();
// identifiing the position according bound view model (context of item)
if (theOne != null)
{
//identifing the position of drop within the item
var itemCenterPosY = theOne.ActualHeight / 2;
var dropPosInItemPos = e.GetPosition(theOne);
// geting the index
var itemIndex = tasksListBox.Items.IndexOf(theOne.Content);
// decission if insert before or below
if (dropPosInItemPos.Y > itemCenterPosY)
{ // when drag is gropped in second half the item is inserted bellow
itemIndex = itemIndex + 1;
}
dropIndex = itemIndex;
}
}
.... here create the item .....
if (dropIndex < 0)
ViewModel.Items.Add(item);
else
ViewModel.Items.Insert(dropIndex, item);
e.Handled = true;
}
So this solution works with my template DraggableListBoxView, I suppose the same solution must work with standard ListBoxView. Good Luck
You can do this. It takes a bit of work, but it can be done. There are a couple demos out there, here is one on CodeProject. This particular one is by the wpf master known as Josh Smith. It's probably not exactly what you are looking for, but it should be pretty darn close.

Categories