I need to set the selected item of my property grid. I'm getting an eventargs, which stores a string (this string tells me what property in my propertygrid the user wants to select).
The problem is i cannot find a collection of grid items, i can select one from. And also i dont know how to properly create a new GridItem object and set the SelectedGridItem property.
GridItem gridItem = ???;
detailsPropertyGrid.SelectedGridItem = gridItem;
thank you for your help.
Edit:
Its almost working now tahnk you VERY much.
GridItem gi = this.detailsPropertyGrid.EnumerateAllItems().First((item) =>
item.PropertyDescriptor != null &&
item.PropertyDescriptor.Name == colName);
this.detailsPropertyGrid.SelectedGridItem = gi;
this.detailsPropertyGrid.Select();
The only problem is: Now its selecting the Property Name field. Can I set the focus to the input field of the property?
Here are a couple of PropertyGrid extensions that can enumerate all items in a grid. This is how you can use it to select one item:
// get GridItem for a property named "Test"
GridItem gi = propertyGrid1.EnumerateAllItems().First((item) =>
item.PropertyDescriptor != null &&
item.PropertyDescriptor.Name == "Test");
// select it
propertyGrid1.Focus();
gi.Select();
// enter edit mode
SendKeys.SendWait("{F4}");
...
public static class PropertyGridExtensions
{
public static IEnumerable<GridItem> EnumerateAllItems(this PropertyGrid grid)
{
if (grid == null)
yield break;
// get to root item
GridItem start = grid.SelectedGridItem;
while (start.Parent != null)
{
start = start.Parent;
}
foreach (GridItem item in start.EnumerateAllItems())
{
yield return item;
}
}
public static IEnumerable<GridItem> EnumerateAllItems(this GridItem item)
{
if (item == null)
yield break;
yield return item;
foreach (GridItem child in item.GridItems)
{
foreach (GridItem gc in child.EnumerateAllItems())
{
yield return gc;
}
}
}
}
Related
SubVwr.Tables[0].tbl is a table of several items, some of which require specific Trace. Those items which require trace have serial numbers assiciated with them. To handle this, I created a new window for each, where I'd like the user to input serial numbers. Some of the Data inside of the new table (dialog) should be pulled from the previous table (Subvwr). What would be the syntax here if I wanted to complete an assignment for a specific value within each row of dialog?
private void requestSerials()
{
// needs to check for a difference. If QtyRcvd has changed, then we need to request "QtyRcvd" number of serials
//for each row in our received order..
foreach (DataRow row in SubVwr.Tables[0].Tbl.Rows)
{
// check to see if it neesd to be traced..
if (row["TraceReq"].ToString() == "Y")
{
//if yes, create a serialInputDialogWindow
SerialInputDialogWindow dialog = new SerialInputDialogWindow()
{
Topmost = true
};
//Iterate through the dialogWindow before we display it to fill it with some necessary data.
var itemsSource = dialog.serialtable.ItemsSource as IEnumerable;
if (itemsSource != null)
{
foreach (var item in itemsSource)
{
var serialrow = item as System.Data.DataRowView;
serialrow["PN"] = "test"; //doesn't work
serialrow.Row["PN"] = "test"; //doesn't work
}
}
dialog.ShowDialog();
}
}
}
An example of similar code that works. It populates that specific column for each row as specified. I'd like to take Data from Subvwr and populate Dialog in a similar way
foreach(DataRow row in SubVwr.Tables[0].Tbl.Rows)
{
row["DateRcvd"] = DateTime.Today;
row["QtyRcvd"] = row["QtyPer"];
}
The items in the ItemsSource are of some type. If for example dialog.serialtable.ItemsSource returns a DataView, you can cast the items to DataRowViews and then access any columns using the indexer:
foreach (var item in itemsSource)
{
var serialrow = item as System.Data.DataRowView;
if (serialrow != null)
{
var name = serialrow["ColumnName"].ToString();
}
}
If it is an IEnumerable<T> and T is a YourClass, you cast it to this type:
foreach (var item in itemsSource)
{
var serialrow = item as YourType;
if (serialrow != null)
{
var name = serialRow.Property1;
}
}
My aim is to loop through all fields in each row in a GridView. The fields are of type CheckBox, TextBox and DropDownList. If one of them are found unchecked/empty/selectedIndex=0, I'll add it to emptyControls list.
foreach (GridViewRow gvRow in gvProxyEntry.Rows)
{
List<object> emptyControls = new List<object>();
foreach (TableCell cell in gvRow.Cells)
{
Type controlType = cell.Controls[0].GetType();
if (controlType == typeof(CheckBox))
{
CheckBox chkBox = (CheckBox)cell.Controls[0];
if (chkBox.Checked == false)
{
emptyControls.Add(chkBox);
}
}
...
}
...
}
My problem is the if-else checking keeps failing to detect checkboxes (based on code snippet above).
I have a guess why this fails. Doing a debug, I found controlType variable is always of type System.Web.UI.LiteralControl.
How do I correctly get the correct type of all the fields without using field's ID? The reason I don't want to use the field's ID is to prevent code change in the future if new fields are being added to the row.
If a control is nested inside other control, you won't be able to find it easily. I believe you know the ID of the control at design time.
If so, you can use the following FindControlRecursive helper method.
Helper Method
public static Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
return root;
return root.Controls.Cast<Control>()
.Select(c => FindControlRecursive(c, id))
.FirstOrDefault(c => c != null);
}
Usage
foreach (TableCell cell in gvRow.Cells)
{
var checkBox = FindControlRecursive(cell, "CheckBox1") as CheckBox;
if(checkBox != null)
{
// Do something
}
}
Using Linq you can get all the checkboxes using one line
var unCheckedCheckBoxes = gvProxyEntry.Rows.OfType<TableRow>()
.SelectMany(row => row.Cells.OfType<TableCell>()
.SelectMany(cell => cell.Controls.OfType<CheckBox>())).Where(c=>!c.Checked).ToList();
same way you can get other control types by changing type and the where condition
Solution 1
Apparently, I was targeting cell.Control[0], which is the wrong control type in cell.Controls. So I added another foreach loop to loop through cell.Controls to get the correct control type that I wanted.
foreach (GridViewRow gvRow in gvProxyEntry.Rows)
{
List<object> emptyControls = new List<object>();
foreach (TableCell cell in gvRow.Cells)
{
foreach(Control c in cell.Controls)//newly added foreach loop
{
Type controlType = c.GetType();
if (controlType == typeof(CheckBox))
{
CheckBox chkBox = (CheckBox)c;
if (chkBox.Checked == false)
{
emptyControls.Add(chkBox);
}
}
...
}
...
}
...
}
Solution 2
For LinQ users
I have the following property for getting and setting a Session variable...
protected List<Item> SearchResults
{
get
{
if (Session["SearchResults"] == null)
Session["SearchResults"] = new List<string>();
return ((List<string>) Session["SearchResults"]).Select(s => new Item(s)).ToList();
}
set { Session["SearchResults"] = value.Select(p => p.Serialize()).ToList(); }
}
As you can see, I am storing the items as serialized strings.
When the user checks a few checkboxes in a GridView and then clicks submit, I would like to modify the value of the Selected property of the Items that he has selected. Here is my code for doing that and it is working great...
private void RecordSelections()
{
var searchResults = SearchResults; //localize session list
foreach (var checkBox in AssignGrid.Rows.Cast<GridViewRow>().Select(r => (CheckBox)r.FindControl("chkSelect")))
{
var result = searchResults.FirstOrDefault(r => r.Key == long.Parse(checkBox.Attributes["ItemKey"].ToString()));
if (result != null) result.Selected = checkBox.Checked;
SearchResults = searchResults; //store session list
}
}
My question then is this. I have found it necessary to localize the session list (see commented line) and then to store that session list (also commented), but what is it precisely that is happening when I access this Session variable through the getter? I would like to do something like the following to get an Item out of the list and modify it's value. I am not able to answer exactly why it is that this doesn't work. Would it work if I were not serializing the object (the reason for serializing the item is another post altogether)?
private void RecordSelections()
{
foreach (var checkBox in AssignGrid.Rows.Cast<GridViewRow>().Select(r => (CheckBox)r.FindControl("chkSelect")))
{
var result = SearchResults.FirstOrDefault(r => r.Key == long.Parse(checkBox.Attributes["ItemKey"].ToString()));
if (result != null) result.Selected = checkBox.Checked;
}
}
Thanks in advance for your help.
How can we access the items added to a ListView?
The thing I have to do is: add an item to the list view. I want to check if the item to add to the listview is already present in the ListView.
I'm using C# and Visual Studio 2005.
The ListView class provides a few different methods to determine if an item exists:
Using Contains on the Items collection
Using one of the FindItemWithText methods
They can be used in the following manner:
// assuming you had a pre-existing item
ListViewItem item = ListView1.FindItemWithText("test");
if (!ListView1.Items.Contains(item))
{
// doesn't exist, add it
}
// or you could find it by the item's text value
ListViewItem item = ListView1.FindItemWithText("test");
if (item != null)
{
// it exists
}
else
{
// doesn't exist
}
// you can also use the overloaded method to match sub items
ListViewItem item = ListView1.FindItemWithText("world", true, 0);
Just add your items and make sure you assign a name. Then
just use the ContainsKey method of the Items collection to
determine if it's there, like this.
for (int i = 0; i < 20; i++)
{
ListViewItem item = new ListViewItem("Item" + i.ToString("00"));
item.Name = "Item"+ i.ToString("00");
listView1.Items.Add(item);
}
MessageBox.Show(listView1.Items.ContainsKey("Item00").ToString()); // True
MessageBox.Show(listView1.Items.ContainsKey("Item20").ToString()); // False
You could do something like this:
ListViewItem itemToAdd;
bool exists = false;
foreach (ListViewItem item in yourListView.Items)
{
if(item == itemToAdd)
exists=true;
}
if(!exists)
yourListView.Items.Add(itemToAdd);
The following will help to locate a ListViewItem within the ListView control once you've added it:
string key = <some generated value that defines the key per item>;
if (!theListViewControl.Items.ContainsKey(key))
{
item = theListViewControl.Items.Add(key, "initial text", -1);
}
// now we get the list item based on the key, since we already
// added it if it does not exist
item = theListViewControl.Items[key];
...
Note
The key used to add the item to the ListView items collection can be any unique value that can identify the ListViewItem within the collection of items. For example, it could be a hashcode value or some property on an object attached to the ListViewItem.
A small correction in Robban's answer
ListViewItem itemToAdd;
bool exists = false;
foreach (ListViewItem item in yourListView.Items)
{
if(item == itemToAdd)
{
exists=true;
break; // Break the loop if the item found.
}
}
if(!exists)
{
yourListView.Items.Add(itemToAdd);
}
else
{
MessageBox.Show("This item already exists");
}
In case of multicolumn ListView, you can use following code to prevent duplicate entry according to any column:
Let us suppose there is a class Judge like this
public class Judge
{
public string judgename;
public bool judgement;
public string sequence;
public bool author;
public int id;
}
And i want to add unique object of this class in a ListView. In this class id is unique field, so I can check unique record in ListView with the help of this field.
Judge judge = new Judge
{
judgename = comboName.Text,
judgement = checkjudgement.Checked,
sequence = txtsequence.Text,
author = checkauthor.Checked,
id = Convert.ToInt32(comboName.SelectedValue)
};
ListViewItem lvi = new ListViewItem(judge.judgename);
lvi.SubItems.Add(judge.judgement ? "Yes" : "No");
lvi.SubItems.Add(string.IsNullOrEmpty(judge.sequence) ? "" : txtsequence.Text);
lvi.SubItems.Add(judge.author ? "Yes" : "No");
lvi.SubItems.Add((judge.id).ToString());
if (listView1.Items.Count != 0)
{
ListViewItem item = listView1.FindItemWithText(comboName.SelectedValue.ToString(), true, 0);
if (item != null)
{
// it exists
}
else
{
// doesn't exist
}
}
I need to enumerate over a collection of controls - regardless of their nesting level - that match a given predicate.
Originally the problem occurred, when I needed to set all textbox's in a grids row to ReadOnly, if a column in that row indicated that the record should not be editable.
Later I realized, that I already solved a problem in the past very much like this one, only with a different criteria (find a single control recursively by its ID).
After trying a few alternatives I came up with a general solution that works. But since I will use this method very often, I wanted to gather possible improvements.
This method will return all child controls matching a predicate:
public static IEnumerable<T> FindChildControls<T>(this Control parentControl,
Predicate<Control> predicate) where T : Control
{
foreach (Control item in parentControl.Controls) {
if (predicate(item))
yield return (T)item;
foreach (T child in item.FindChildControls<T>(predicate)) {
yield return child;
}
}
}
Using this method I can do the following:
var allTxt = Page.FindChildControls<TextBox>(c => c is TextBox);
var submit = Page.FindChildControls<Button>(c => c.ID == "btnSubmit").First();
You can use a queue to get rid of recursion if you want.
public static IEnumerable<T> FindChildControls<T>(Control parentControl,
Predicate<Control> predicate) where T : Control
{
Queue<Control> q = new Queue<Control>();
foreach (Control item in parentControl.Controls)
{
q.Enqueue(item);
}
while (q.Count > 0)
{
Control item = q.Dequeue();
if (predicate(item))
yield return (T)item;
foreach (Control child in item.Controls)
{
q.Enqueue(child);
}
}
}