C# can't update ComboBox when Item List is filled - c#

I have a function which clears all Comboboxes on the form and then try to update the text.
The new text is only shown if no Items are in the Item list.
Does anyone have an idea whats wrong?
I deleted all functions from the project so that only the necessary part is available.
https://www.dropbox.com/s/ibst7enrteyk9jb/Digitales_Auftragsformular.zip?dl=0
The project is attached. Simply start, go to tab "Werkzeuganfrage" there are two Comboboxes in red. One without Items = working, one with Items which is not working.

public Oberflaeche()
{
InitializeComponent();
List<ComboBox> myComboBoxes = GetControlsByType<ComboBox>(this, typeof(ComboBox));
foreach (ComboBox txt in myComboBoxes)
{
//txt.Text = "";
txt.SelectedIndex = -1;
}
Werkzeuganfrage_Combobox_rhino1_1.Text = "Rhino1_1";
Werkzeuganfrage_Combobox_rhino1_2.Text = "Rhino1_2";
comboBox1.Text = "Rhino1_1";
comboBox2.Text = "Rhino1_2";
}
public List<T> GetControlsByType<T>(Control container, Type type)
{
List<T> result = new List<T>();
foreach (Control c in container.Controls)
{
if (c.GetType() == type)
{
result.Add((T)Convert.ChangeType(c, typeof(T)));
}
if (c.HasChildren)
{
result.AddRange(GetControlsByType<T>(c, type));
}
}
return result;
}

Related

Retrieving drop down list items from list of controls

I am attempting to retrieve a ListItemCollection from a List of controls. This List contains many controls of different types - DropDownList, Label, TextBox.
I would like to retrieve all ListItem from all the DropDownList controls contained in the original List of controls.
My thought process so far has been to extract all of the DropDownList controls into a new list, and iterate though this to pull out each ListItem - however, every DropDownList control is coming up with 0 items
ControlCollection cList = pnlContent.Controls;
List<DropDownList> ddlList = new List<DropDownList>();
foreach (Control c in cList)
{
if (c.GetType() == new DropDownList().GetType())
{
ddlList.Add((DropDownList)c);
}
}
ListItemCollection itemCollection = new ListItemCollection();
foreach (DropDownList ddl in ddlList)
{
foreach(ListItem li in ddl.Items)
{
itemCollection.Add(li);
}
}
I'm sure this is the wrong (and massively inefficient) way of doing this. Any help would be appreciated.
This will do:
public IEnumerable<ListItem> GetListItems(ControlCollection controls)
{
return controls.OfType<DropDownList>().SelectMany(c => c.Items.OfType<ListItem>());
}
I currently do not have an installation where I can test this, but as a line of thought I would use Linq to do this.
Here is an example that you should be able to use;
var type = new DropDownList().GetType();
var listOfControl = from c in pnlContent.Controls
where c.GetType() == type
select ((DropDownList)c).Items;
Try this:
var ddlList = cList.OfType<DropDownList>();
ListItemCollection itemCollection = new ListItemCollection();
// option 1
var temp = ddlList.Select(ddl => ddl.Items.Cast<ListItem>()).SelectMany(li => li).ToArray();
itemCollection.AddRange(temp);
// or option 2
var temp = ddlList.Select(ddl => ddl.Items.Cast<ListItem>()).SelectMany(li => li);
foreach (var listItem in temp)
{
itemCollection.Add(listItem);
}
comboBox1.Items.Add(button1);
comboBox1.Items.Add(button2);
comboBox1.Items.Add(dateTimePicker1);
comboBox1.Items.Add(checkBox1);
comboBox2.Items.Add(button3);
comboBox2.Items.Add(button4);
comboBox2.Items.Add(dateTimePicker2);
comboBox2.Items.Add(checkBox2);
comboBox3.Items.Add(button5);
comboBox3.Items.Add(dateTimePicker3);
comboBox3.Items.Add(checkBox3);
comboBox3.Items.Add(checkBox4);
List<ComboBox> ddlList = new List<ComboBox>();
foreach (Control c in panel1.Controls)
{
if (c is ComboBox)
{
ddlList.Add((ComboBox)c);
}
}
List<Control> itemCollection = new List<Control>();
foreach (ComboBox ddl in ddlList)
{
foreach (var li in ddl.Items)
{
itemCollection.Add((Control)li);
}
}

How I can realize parallel search in ObservableCollection?

I have an ObservableCollection, where Item has 2 properties(for example: Name and Id) and collection contains of 12k elements. So, i have a textbox, i want to search elements, which names contains my textbox value and add these elems in new collection.
in real-proj:
Silverlight, TreeView(its ItemSource is my collection) which dynamically changing. And TreeView changing in UI.
My problem is just in slowly rendering results of search. I thing if it'll be parallel - it saves me.
for example, some code im using:
private ObservableCollection<ICDDocumentItemViewModel> LinearSearch(string searchText)
{
var filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
if (searchText.Length > 3)
{
foreach (var itemViewModel in _linearCollection)
{
if (!itemViewModel.Model.Name.ToLower().Contains(searchText.ToLower())) continue;
if (itemViewModel.Children.Count != 0)
{
itemViewModel.IsExpanded = true;
}
filteredCollection.Add(itemViewModel);
}
}
if(searchText.Length <= 3)
{
return new ObservableCollection<ICDDocumentItemViewModel>(ICDItemsViewModelsMain);
}
return filteredCollection;
}
there is no need to have parallel processing in place normally, this code should help you here.
private ObservableCollection<ICDDocumentItemViewModel> GetFiltered(string filter)
{
ObservableCollection<ICDDocumentItemViewModel> filteredCollection;
if (filter.Length > 3)
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>(_linearCollection.Where(x => x.Name.ToLower().Contains(filter)));
filteredCollection.ToList().ForEach(DetectChildren);
}
else
{
filteredCollection = new ObservableCollection<ICDDocumentItemViewModel>();
}
return filteredCollection;
}
private void DetectChildren(ICDDocumentItemViewModel item)
{
item.IsExpanded = item.Children.Any();
}

How to find toolstripmenuItem with name

I have set visible property of my menuStrip1 items to false as
foreach (ToolStripMenuItem itm in menuStrip1.Items)
{
itm.Visible = false;
}
Now I know the Names of toolStripMenuItem and dropDownItem of the menustrip1. How to can I activate the required toolStripMenuItem and dropDownItem.
I have
string mnItm = "SalesToolStripMenuItem";
string ddItm = "invoiceToolStripMenuItem";
Now I want to set visible true to these two(toolStripMenuItem and dropDownItem) items. How can I do that? I know those names only.
Simply use those names to get the actual item via MenuStrip.Items indexer:
ToolStripMenuItem menuItem = menuStrip1.Items[mnItm] as ToolStripMenuItem;
ToolStripDropDownMenu ddItem = menuStrip1.Items[ddItm] as ToolStripDropDownMenu;
You can use
menuStrip1.Items[mnItm].Visible = true;
menuStrip1.Items[ddItm].Visible = true;
or if you want to set Visible to multiple toolstrip items:
string [] visibleItems = new [] {"SalesToolStripMenuItem", "invoiceToolStripMenuItem"};
foreach (ToolStripMenuItem item in menuStrip1.Items)
{
if (visibleItems.Contains(item.Name))
{
item.Visible = false;
}
}
Hope it helps
You're looking for ToolStripItemCollection.Find method.
var items = menustrip.Items.Find("SalesToolStripMenuItem", true);
foreach(var item in items)
{
item.Visible = false;
}
second parameter says whether or not to search the childrens.
You should try something like this:
string strControlVal ="somecontrol"; //"SalesToolStripMenuItem" or "invoiceToolStripMenuItem" in your case
foreach (ToolStripMenuItem item in menuStrip1.Items)
{
if (strControlVal == item.Name)
{
item.Visible = false;
}
}
Initialize strControlVal string on your discretion where you need it.
If i get your question you are trying to disable other than the above two mentioned toolstrip items. Since you know the name of the menu items a slight change in code can get you along
foreach (ToolStripMenuItem itm in menuStrip1.Items)
{
if(itm.Text !="SalesToolStripMenuItem" || itm.Text !="invoiceToolStripMenuItem")
{
itm.Visible = false;
}
}
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string MenuItemName = sender.ToString()
}

Search in ListView c#

I've wrote a method to search thru ListView for a given string and mark the ones that are found with Color. It works fine however with lots of information on screen and scrollable ListView it's sometimes hard to find what user is looking for.
Normally I do create special searches by modifying method and SQL query WHERE clause but it's always a pain and requires more work/code for each ListView/Data.
I would like to have some generalized search that would work for all kind of searches in ListView just like one I have now but with ability to hide (rows) what's not needed and show only necessary rows. Of course if one changes it's mind it has to bring back the old rows back.
I guess the biggest problem for me is how to store all the columns and data without over complicating knowing that it can be 3 to 20+ columns and multiple rows.
public static void wyszukajNazweListView(ListView varListView, string varWyszukaj) {
if (varWyszukaj != "") {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
string textToAdd2 = drv.Text;
if (textToAdd2.Length >= 1) {
if (textToAdd2.ToLower().Contains(varWyszukaj.ToLower())) {
drv.BackColor = Color.DarkOrange;
} else {
drv.BackColor = Color.White;
}
}
}
bool varColor = false;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
if (drv.BackColor == Color.DarkOrange) {
varColor = true;
break;
}
}
if (varListView.SmallImageList != null) {
if (varColor) {
comp.ImageIndex = 2;
} else {
comp.ImageIndex = -1;
}
}
}
} else {
foreach (ListViewItem comp in varListView.Items) {
comp.UseItemStyleForSubItems = false;
comp.BackColor = Color.White;
foreach (ListViewItem.ListViewSubItem drv in comp.SubItems) {
drv.BackColor = Color.White;
comp.ImageIndex = -1;
}
}
}
}
I'd probably store it as a DataTable object. DataTable type allows setting its rows as hidden (e.g. Visible = false) and you can bind your ListView directly to it.
EDIT: noticed the WinForms tag. Even simpler: no need to mock about with ViewState/Session.

selecting combobox item using ui automation

how do I select ComboBox's SelectedIndex = -1?
I wrote a code to automate testing:
AutomationElement aeBuildMachine = null;
int count = 0;
do
{
Console.WriteLine("\nLooking for Build Machine Combo Box");
aeBuildMachine = aeTabitemmain.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.ClassNameProperty, "ListBoxItem"));
if (aeBuildMachine == null)
throw new Exception("No Build Machine Combo Box");
else
Console.WriteLine("Found Build Machine Combo Box");
++count;
}
while (aeBuildMachine == null && count < 50);
Console.WriteLine("Selecting Build machine from combobox...");
SelectionItemPattern spBuildmachine = (SelectionItemPattern)aeBuildMachine.GetCurrentPattern(SelectionItemPattern.Pattern);
How do I use this SelectionItemPattern?
This is about 100 times more complicated than it needs to be, but I finally got it working. The big issue with the WPF ComboBox is that as far as Automation goes, it doesn't appear to have any ListItems until the ComboBox has been expanded.
The following code uses the ExpandCollapse pattern to momentarily drop down the list and then collapse it, then it can use FindFirst on the ComboBox to get the ListItem to be selected, and then use the SelectionItem pattern to select it.
In the case of the original question, a selection of -1 means no items selected. There is no method for this, but you could simply use FindAll to get a collection of ListItems, get the SelectionItem pattern for each one in turn and call its RemoveFromSelection method.
public static void SetSelectedComboBoxItem(AutomationElement comboBox, string item)
{
AutomationPattern automationPatternFromElement = GetSpecifiedPattern(comboBox, "ExpandCollapsePatternIdentifiers.Pattern");
ExpandCollapsePattern expandCollapsePattern = comboBox.GetCurrentPattern(automationPatternFromElement) as ExpandCollapsePattern;
expandCollapsePattern.Expand();
expandCollapsePattern.Collapse();
AutomationElement listItem = comboBox.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, item));
automationPatternFromElement = GetSpecifiedPattern(listItem, "SelectionItemPatternIdentifiers.Pattern");
SelectionItemPattern selectionItemPattern = listItem.GetCurrentPattern(automationPatternFromElement) as SelectionItemPattern;
selectionItemPattern.Select();
}
private static AutomationPattern GetSpecifiedPattern(AutomationElement element, string patternName)
{
AutomationPattern[] supportedPattern = element.GetSupportedPatterns();
foreach (AutomationPattern pattern in supportedPattern)
{
if (pattern.ProgrammaticName == patternName)
return pattern;
}
return null;
}
I thought I would share this as a simple way to select any item from a ComboBox or other item container:
protected AutomationElement GetItem(AutomationElement element, string item)
{
AutomationElement elementList;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.TreeScope = TreeScope.Element | TreeScope.Children;
elementList = element.GetUpdatedCache(cacheRequest);
foreach (AutomationElement child in elementList.CachedChildren)
if (child.Cached.Name == item)
return child;
return null;
}
element is the ComboBox or item container, item is the string name or literal value of the item in the container. Once you have the item you can do the following to select it:
protected void Select(AutomationElement element)
{
SelectionItemPattern select = (SelectionItemPattern)element.GetCurrentPattern(SelectionItemPattern.Pattern);
select.Select();
}
Hope this helps others. I derived this pattern from the MSDN documentation on Automation, found here:
MSDN - Automation and Cached Children
This is what worked for me.
/// <summary>
/// Extension method to select item from comboxbox
/// </summary>
/// <param name="comboBox">Combobox Element</param>
/// <param name="item">Item to select</param>
/// <returns></returns>
public static bool SelectComboboxItem(this AutomationElement comboBox, string item)
{
if (comboBox == null) return false;
// Get the list box within the combobox
AutomationElement listBox = comboBox.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List));
if (listBox == null) return false;
// Search for item within the listbox
AutomationElement listItem = listBox.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, item));
if (listItem == null) return false;
// Check if listbox item has SelectionItemPattern
object objPattern;
if (true == listItem.TryGetCurrentPattern(SelectionItemPatternIdentifiers.Pattern, out objPattern))
{
SelectionItemPattern selectionItemPattern = objPattern as SelectionItemPattern;
selectionItemPattern.Select(); // Invoke Selection
return true;
}
return false;
}
Usage
AutomationElement paymentCombobox = element.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.NameProperty, "cbPayment")
);
paymentCombobox.SelectComboboxItem("Cash");
resource https://msdn.microsoft.com/en-us/library/ms752305(v=vs.110).aspx
http://msdn.microsoft.com/de-de/library/system.windows.automation.selectionitempattern_members(v=VS.85).aspx
That is the answer to your question as I understood it.
But.. is it really your question?
Anyhow, you can add or remove SelectableItems from a selection which - I suppose - belong to its parent, same goes for some other things like checking whether they are selected.
I think this might be the easiest way for a simple generic ComobBox value setter this one is fine provided the list items in the ComboBox doesn't have duplicates.
private void SetCombobValueByUIA( AutomationElement ctrl, string newValue )
{
ExpandCollapsePattern exPat = ctrl.GetCurrentPattern(ExpandCollapsePattern.Pattern)
as ExpandCollapsePattern;
if( exPat== null )
{
throw new ApplicationException( "Bad Control type..." );
}
exPat.Expand();
AutomationElement itemToSelect = ctrl.FindFirst(TreeScope.Descendants, new
PropertyCondition(AutomationElement.NameProperty,newValue));
SelectionItemPattern sPat = itemToSelect.GetCurrentPattern(
SelectionItemPattern.Pattern) as SelectionItemPattern ;
sPat. Select();
}
There isn't a big change but only a few to notice,
Don't need to use collapse pattern, calling expand will do the trick for you.
Using treescope.subtree worked for me instead of children.
The code sample would be like this,
public static void SelectValueInComboBox(string comboBox, string value)
{
var comboBoxElement = HelperMethods.FindElementFromAutomationID(comboBox);
if (comboBoxElement == null)
throw new Exception("Combo Box not found");
ExpandCollapsePattern expandPattern = (ExpandCollapsePattern)comboBoxElement.GetCurrentPattern(ExpandCollapsePattern.Pattern);
AutomationElement comboboxItem = comboBoxElement.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, value));
SelectionItemPattern selectPattern = (SelectionItemPattern)comboboxItem.GetCurrentPattern(SelectionItemPattern.Pattern);
selectPattern.Select();
}
For me, the answer by gotmug required the CacheRequest to be activated. I implemented this as an extension method
public static bool SelectDropDownItem(this AutomationElement comboBoxElement, string item)
{
bool itemFound = false;
AutomationElement elementList;
CacheRequest cacheRequest = new CacheRequest();
cacheRequest.Add(AutomationElement.NameProperty);
cacheRequest.TreeScope = TreeScope.Element | TreeScope.Children;
using (cacheRequest.Activate())
{
// Load the list element and cache the specified properties for its descendants.
Condition cond = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List);
elementList = comboBoxElement.FindFirst(TreeScope.Children, cond);
}
//Loop thru and find the actual ListItem
foreach (AutomationElement child in elementList.CachedChildren)
if (child.Cached.Name == item)
{
SelectionItemPattern select = (SelectionItemPattern)child.GetCurrentPattern(SelectionItemPattern.Pattern);
select.Select();
itemFound = true;
break;
}
return itemFound;
}
<pre>
public static void SetComboBox(AutomationElement ComboxBox, string SelectedValue)
{
AutomationElement ListBox = ComboxBox.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ListBox"));
AutomationElement SelectedItem = ListBox.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, SelectedValue));
((SelectionItemPattern)SelectedItem.GetCurrentPattern(SelectionItemPattern.Pattern)).Select();
}
Instruction on how to use:
1) Copy and Paste into Utility Class
2) Find your ComboBox AutomationElement
3) Utility.SetCombox(ComboxAutomationElement, "SelectedText")
To understand:
Tree structure of ComboBox as follow:
ComboBox->ListBox (children)->ListItems(children) [each combobox has a ListBox as a child, and the ListBox has all ListItems as children].
Each ListItem has SelectedItemPattern, invoke which you want to select.
I later found out that "Shaz " has better coding, and i vote him as the best code.
** Comment: To do UIAAutomation, you must map the AutomationElements of your application to a TreeView, which makes everything simple and understood.
I used this code in WindowsForms comboBox
Usage
comboBox.SetSelectedComboBoxItem("ValueYouWantToSelect");
Add this Class to your project:
public static class AutomationElementExtensions
{
public static void InvokeControl(this AutomationElement element)
{
InvokePattern invokePattern = null;
try
{
invokePattern =
element.GetCurrentPattern(InvokePattern.Pattern)
as InvokePattern;
}
catch (ElementNotEnabledException)
{
// Object is not enabled
return;
}
catch (InvalidOperationException)
{
// object doesn't support the InvokePattern control pattern
return;
}
invokePattern.Invoke();
Thread.Sleep(500);
}
public static void SetSelectedComboBoxItem(this AutomationElement comboBox, string item)
{
AutomationPattern automationPatternFromElement = GetSpecifiedPattern(comboBox, "ExpandCollapsePatternIdentifiers.Pattern");
ExpandCollapsePattern expandCollapsePattern = comboBox.GetCurrentPattern(automationPatternFromElement) as ExpandCollapsePattern;
expandCollapsePattern.Expand();
AutomationElement listItem = comboBox.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, item));
InvokeControl(listItem);
}
private static AutomationPattern GetSpecifiedPattern(AutomationElement element, string patternName)
{
AutomationPattern[] supportedPattern = element.GetSupportedPatterns();
foreach (AutomationPattern pattern in supportedPattern)
{
if (pattern.ProgrammaticName == patternName)
return pattern;
}
return null;
}
}

Categories