Changing selected item in GridViewComboboxColumn (Telerik, winforms) - c#

I'm wondering how you can set the selected element of a gridviewcomboboxcolumn.
As a bit of a foreword: I'm using the column with autocomplete mode in order to
have autocomplete functionality, but I also want to add new elements to the list.
It worked so far without a hitch EXCEPT for one situation:
I already have:
T1
T12
T123
In the data source.
Then when I have for example T12 selected and do backspace to select T1
I've got the problem that I need to manually click on T1 in the list AS there is no selection of T1 as there are multiple possibilities shown. Thus when I leave the editor mode without manually selecting T1 I get T12 as selected item.
I want to change this behaviour in such a way that the first found item is pre selected (always). (regardless if it is a new element or a changed to element so to say)
Currently I've already added a custom handler for the cellendedit to add the new value to the list:
private void MainFormGridView_CellEndEdit(object Sender, GridViewCellEventArgs eventArgs)
{
var virtualizedCurrentCell = ((Telerik.WinControls.UI.GridVirtualizedCellElement)(currentCell));
var currentGridviewComboBoxColumn = ((Telerik.WinControls.UI.GridViewComboBoxColumn)(virtualizedCurrentCell.Data));
if (((List<string>)currentGridviewComboBoxColumn.DataSource).IndexOf((string)currentCell.Value) > -1)
{
foundValueInList = true;
}
if (!foundValueInList)
{
((List<string>)currentGridviewComboBoxColumn.DataSource).Add((string)currentCell.Value);
}
}
The column itself is created in this way (bnefore being added to the gridview it is a part of:
GridViewComboBoxColumn newColumn;
newColumn = new GridViewComboBoxColumn();
((GridViewComboBoxColumn)newColumn).DataSource = (from c in entity.myOffer
orderby c.customer
where c.customer!= null
select c.customer)
.Distinct()
.ToList();
((GridViewComboBoxColumn)newColumn).DropDownStyle = Telerik.WinControls.RadDropDownStyle.DropDown;
((GridViewComboBoxColumn)newColumn).AutoCompleteMode = AutoCompleteMode.SuggestAppend;
newColumn.FieldName = "customer";
newColumn.Name = "customer";
newColumn.HeaderText = "Customer";
newColumn.TextAlignment = System.Drawing.ContentAlignment.MiddleCenter;
newColumn.Width = 100;
listOfColumns.Add(newColumn);
this.MainFormGridView.Columns.Add(newColumn);
So the question is there what can I do to select specific items in the dropdownlist AND is the CellEndEdit the correct location for that (as I suspect)?

A possible solution for the problem itself is to forego the usage of the GridViewComboboxcColumn to get a field with autocomplete and instead use a "normal" GridViewTextBoxColumn.
In addition to this change we will need an customized editor which brings in the autocomplete element. One thing of note here is that the height of the cell changes slightly leading to chars being chopped off a bit if you don't have the setting active that rows can grow in size automatically (thus if that is not the case, then you need to increase the row height to at least 30:
Example of how to do this in this case: MainFormGridView.TableElement.RowHeight = 30; )
The creation code for the textbox with autocomplete now changes to the following:
GridViewTextBoxColumn newColumn;
newColumn = new GridViewTextBoxColumn();
newColumn.FieldName = "customer";
newColumn.Name = "customer";
newColumn.HeaderText = "Customer";
newColumn.TextAlignment = System.Drawing.ContentAlignment.MiddleCenter;
newColumn.Width = 100;
this.MainFormGridView.Columns.Add(newColumn);
Like I mentioned before we now need a custom editor for the autocomplete box itself:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Telerik.WinControls.UI;
namespace MyProject
{
class customAutoCompleteBox : RadTextBoxControlEditor
{
protected override Telerik.WinControls.RadElement CreateEditorElement()
{
return new RadAutoCompleteBoxElement();
}
public override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
{
RadAutoCompleteBoxElement element = this.EditorElement as RadAutoCompleteBoxElement;
if (element.IsAutoCompleteDropDownOpen)
{
return;
}
base.OnKeyDown(e);
}
}
}
Then after this is done we only need to do 2 more steps to get the new autocomplete box to function:
1.) Setting the EditorRequired and CellEditorInitialized events
2.) Writing those events
For 1.) it is quite easy to do:
this.MainFormGridView.CellEditorInitialized += MainFormGridView_CellEditorInitialized;
this.MainFormGridView.EditorRequired += MainFormGridView_EditorRequired;
For 2.) the code here is also quite easy to do. Although I will put in a bit of a foreword here:
element.Delimiter can cause a few problems. Defaultwise one would use ' ' as delimiter there,
BUT if the data itself has many blanks inside that could raise an unexpected behaviour, as
each blank separated data part is seen as one "tag" or autocomplete element. Thus when the row is chosen you can get multiple tags displayed that you can delete one by one. In my case that was unwanted thus I did not use ' ' as separator but instead '\t' as the data does not have tabs inside.
Here is the code for CellEditorInitialized:
void MainFormGridView_CellEditorInitialized(object sender, GridViewCellEventArgs e)
{
if (e.ActiveEditor is customAutoCompleteBox)
{
customAutoCompleteBox editor = (customAutoCompleteBox)e.ActiveEditor;
RadAutoCompleteBoxElement element = (RadAutoCompleteBoxElement)editor.EditorElement;
element.Delimiter = '\t';
element.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
element.AutoCompleteDisplayMember = e.Column.Name;
element.AutoCompleteValueMember = e.Column.Name;
element.AutoCompleteDataSource = (from c in entity.myOffer
orderby c.customer
where c.customer!= null
select c.customer)
.Distinct()
.ToList();
}
}
And now for ViewCellFormatting
void MainFormGridView_EditorRequired(object sender, Telerik.WinControls.UI.EditorRequiredEventArgs e)
{
if (MainFormGridView.CurrentColumn.Name == "customer")
{
e.Editor = new customAutoCompleteBox();
}
}
With this an autocomplete is possible where the original problem does not occur. The only things not so nice here are that one has to press enter 2 times (after the first time the "tags" are shown from the row that was selected, and only the 2nd enter sets the tags. Same for using TAB to move out of the cell). Additionally the combobox is only shown after typing in a char and not already from the beginning.
In total, this solution functions but the behaviour (and appearance) of the autocompletebox is slightly different from the original combobox - autocomplete box.
Edit:
As it seems if you use the delimiter it adds the delimiter at the end, which can cause unwanted results (thus nothing found) if the autocomplete box is shown in an filter. A possible solution is using \0 as a delimiter so that nothing is added to the selected string from the autocomplete box.
Although this also makes it so that ALL chosen elements are treeated as being one single tag.

Related

Combobox returns null when value is typed

Sorry if it has some obvious solution, but I am trying to solve it for hours but could not find a solution.
I use several ComboBoxes in my WindowsFormsApplication to relate ids with names. The problem is that when a user select an item from the combobox list, it works fine, but when he types an item, the SelectedValue property of the combobox is null.
To simulate the problem, I created a from with one button and a combobox.
In my actual application, I populate the comboboxes with data from tables in a sqlserver database, but for simplicity, here I populate it with a list:
public Form1()
{
InitializeComponent();
List<KeyValuePair<short,short>> l = new List<KeyValuePair<short,short>>();
l.Add(new KeyValuePair<short,short>(1,10));
l.Add(new KeyValuePair<short,short>(2,20));
l.Add(new KeyValuePair<short,short>(3,30));
this.comboBox1.DataSource = l;
this.comboBox1.DisplayMember = "Value";
this.comboBox1.ValueMember = "Key";
}
private void button1_Click(object sender, EventArgs e)
{
if (this.comboBox1.SelectedValue == null)
MessageBox.Show("NULL");
else
MessageBox.Show(this.comboBox1.SelectedValue.ToString());
}
For example, when user select the second item (20) from the list and clicks on the button, messagebox shows 2 as it is expected, but if he types the number 20 into the combobox, the SelectedValue is null.
This problem could be solved by changing the style of combobox:
this.comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
But it prevents user to type into the combobox, so I am forced to use the default ComboBoxStyle.DropDown.
Thats because the combo box does not select the item that you have typed. Add this option
comboBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
Then it will select the item when ever it was able to find it.
By default it is set to AutoCompleteMode.None.
(I think) This is mainly designed for suggestions but it can solve your problem here. also if you want to show the suggestions:
comboBox1.AutoCompleteSource = AutoCompleteSource.ListItems;
by default it is set to AutoCompleteSource.None.
https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.autocompletemode%28v=vs.110%29.aspx
An option that you have is using the EventHandler on the comboBox1.TextChange event that will allow you to personally handle how the text is translated into the different options.
This can be added to the designer (similar to a button).
this.comboBox1.TextChanged += new System.EventHandler(this.UpdateValue);
Then depending on how want to read the results and see if you have a match you can create a converter, use another key/value, or you can do a boring old tryparse as I will show you. You will need the list as a property that you can reference to see if you have found the proper results.
private void UpdateValue(object sender, EventArgs e)
{
short result;
if (short.TryParse(comboBox1.Text, out result))
{
var matches = from val in l where val.Value == result select val.Key;
{
foreach (short m in matches)
this.comboBox1.SelectedValue = m;
}
}
}
Good luck! Let me know if you need more examples with the event handler. Don't forget to vote.

Silverlight C#: How to filter a combobox based on another combobox?

How to filter a combobox based on another combobox? ... again :)
I'm writing an web app to learn. I'm using Visual Studio 2012, Silverlight 5, C#, and SQl Server for the data source.
I have one table loading into a datagrid and comboboxes to filter the datagrid. Up to this point everything is working just right.
The comboboxes are "FilterState" and "FilterWaterWay". Note they are not in the datagrid.
I want to select a state and re-populate the FilterWaterWay with only those waterways in the state.
I've seen a lot of ways to do this but none of them seem to match my setup. I could be wrong and just not know it.
From a learning standpoint, I would like to know how to implement this in all 3 of the following query data examples but I'll settle for just one. The last one is my favorite.
Thanks for any and all help.
I would not mind using the following to load comboboxes, filtered or not, but I can't firgure out how to
Restirct the GetQuery to only one field
Make that field distinct
This loads all data from the GetQuery to the datagrid.
LoadOperation<MASTER_DOCKS> loadOp = this._DocksContext.Load(this._DocksContext.GetMASTER_DOCKSQuery());
DocksGrid.ItemsSource = loadOp.Entities;
This loads all data from the GetQuery to the datagrid after it's been filtered
EntityQuery<MASTER_DOCKS> query = _DocksContext.GetMASTER_DOCKSQuery();
query = query.Where(s => s.WTWY_NAME == WaterwaytoFilterBy && s.STATE == StateToFilterBy);
LoadOperation<MASTER_DOCKS> loadOp = this._DocksContext.Load(query);
DocksGrid.ItemsSource = loadOp.Entities;
This is how I am currently loading the comboboxes. This works fine for the load but I don't see how to filter.
The DomainService.cs does not know my other combobox (FilterState) that I want to use as the filter for this combobox (FilterWaterway).
If I could query the ObservableCollection in the xaml I might be able to get it to work but it seems kind of chunky.
Adapted from http://www.jonathanwax.com/2010/10/wcf-ria-services-datagrid-filters-no-domaindatasource-2/
XAML =
private ObservableCollection<string> waterWayFilterList;
public ObservableCollection<string> WaterWayFilterList
{
get { return waterWayFilterList; }
set { waterWayFilterList = value; }
}
private void DoPopulateFilter()
{
//Call Invoke Method to get a list of distinct WaterWays
InvokeOperation<IEnumerable<string>> invokeOp = _DocksContext.FillWaterWayList();
invokeOp.Completed += (s, e) =>
{
if (invokeOp.HasError)
{
MessageBox.Show("Failed to Load Category Filter");
}
else
{
//Populate Filter DataSource
WaterWayFilterList = new ObservableCollection<string>(invokeOp.Value);
//Add a Default "[Select]" value
WaterWayFilterList.Insert(0, "[Select WaterWay]");
FilterWaterWay.ItemsSource = WaterWayFilterList;
FilterWaterWay.SelectedItem = "[Select WaterWay]";
}
};
}
DomainService.cs =
[Invoke]
public List<string> FillWaterWayList()
{
return (from r in ObjectContext.MASTER_DOCKS
select r.WTWY_NAME).Distinct().ToList();
}
Here's the closest I've gotten so far and it seems straight forward.
It returns no errors but the displayed result reads System.Collections.Generic.List'1[System.Char]
The record count in the dropdown is correct which leads me to think it's on the right track.
Only what is displayed is wrong. A casting problem perhaps?
I would still have to get the result from the FilterState Combo box in where "TX" is.
var filter = from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r.WTWY_NAME.Distinct().ToList();
MyComboBox.ItemsSource = filter;
Without parentheses, you're doing the .Distinct().ToList() on the string (which implements IEnumerable<char>, which is why those operations work), which results in a List<char> (which isn't what you're looking for). You need to add parentheses so you get the distinct waterways:
var filter = (from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r.WTWY_NAME).Distinct().ToList();
Note that if two waterways might have the same name, but actually be distinct, you'll need to instead select distinct r, and then differentiate them in the dropdown somehow, e.g.
var filter = (from r in _DocksContext.MASTER_DOCKS
where r.STATE.Equals("TX")
select r).Distinct().ToList();
// generated classes are partial, so you can extend them in a separate file
public partial class MASTER_DOCKS
{
// the dropdown uses the ToString method to show the object
public override string ToString()
{
return string.Format("{0} ({1})", WTWY_NAME, ID);
}
}

Selected Checkbox from a bunch of checkboxes in .c#.net

The problem is faced under c# .NET, Visual Studio, Windows Form Application
I have a bunch of checkboxes placed randomly in one form and in one panel.
So, If any checkbox is selected in the form its value is supposed to be added up.
Bottomline: Instead of using plenty of "If-else loops", to evaluate whether its been checked or not. I wanna simplify it using a "for loop ".
Is there any Checkbox group name type feature, which I can use???
I wanna code something like this:
for(int i=0;i<checkboxes.length;i++)
{
string str;
if(chkbox.checked)
{
str+=chkbox.value;
}
}
Where checkboxes is a group name.
You can use a simple LINQ query
var checked_boxes = yourControl.Controls.OfType<CheckBox>().Where(c => c.Checked);
where yourControl is the control containing your checkboxes.
checked_boxes is now an object which implements IEnumerable<CheckBox> that represents the query. Usually you want to iterate over this query with an foreach loop:
foreach(CheckBox cbx in checked_boxes)
{
}
You also can convert this query to a list (List<Checkbox>) by calling .ToList(), either on checked_boxes or directly after the Where(...).
Since you want to concatenate the Text of the checkboxes to a single string, you could use String.Join.
var checked_texts = yourControl.Controls.OfType<CheckBox>()
.Where(c => c.Checked)
.OrderBy(c => c.Text)
.Select(c => c.Text);
var allCheckedAsString = String.Join("", checked_texts);
I also added an OrderBy clause to ensure the checkboxes are sorted by their Text.
CheckBox[] box = new CheckBox[4];
box[0] = checkBox1;
box[1] = checkBox2;
box[2] = checkBox3;
box[3] = checkBox4;
for(int i=0; i<box.length; i++)
{
string str;
if(box[i].checked== true)
{
str += i.value;
}
}
I think this code will work with DotNet4.0. Plz let me know any error occurs. Treat 'box' as regular array.
If all the checkboxes are in a groupbox you can do this:
foreach(Control c in myGroupBox.Controls)
{
if (c is CheckBox)
{
//do something
CheckBox temp = (CheckBox)c;
if(temp.Checked)
//its checked
}
}
Subscribe all checkboxes to one CheckedChanged event handler and build your string when any checkbox checked or unchecked. Following query will build string, containing names of all Form's checked checkboxes:
private void Checkbox_CheckedChanged(object sender, EventArgs e)
{
// this will use all checkboxes on Form
string str = Controls.OfType<CheckBox>()
.Where(ch => ch.Checked)
.Aggregate(new StringBuilder(),
(sb, ch) => sb.Append(ch.Name),
sb => sb.ToString());
// use string
}
I suppose other than subscribing to event CheckedChanged there is no alternative even if it is contained in some panel or form, You have to use if else,
if it would have been web base eg asp.net or php we could use jquery because it gives us the option to loop through each particular event using .each and getting its value

Setting default item in combo box

I have a function for setting items in a combobox and one item is to be set by default like
--SELECT LIST--
public void SetOperationDropDown()
{
int? cbSelectedValue = null;
if(cmbOperations.Items.Count == 0)
{
//This is for adding four operations with value in operation dropdown
cmbOperations.Items.Insert(0, "PrimaryKeyTables");
cmbOperations.Items.Insert(1, "NonPrimaryKeyTables");
cmbOperations.Items.Insert(2, "ForeignKeyTables");
cmbOperations.Items.Insert(3, "NonForeignKeyTables");
cmbOperations.Items.Insert(4, "UPPERCASEDTables");
cmbOperations.Items.Insert(5, "lowercasedtables");
//ByDefault the selected text in the cmbOperations will be -SELECT OPERATIONS-.
cmbOperations.Text = "-SELECT OPERATIONS-";
}
else
{
if(!string.IsNullOrEmpty("cmbOperations.SelectedValue"))
{
cbSelectedValue = Convert.ToInt32(cmbOperations.SelectedValue);
}
}
//Load the combo box cmbOperations again
if(cbSelectedValue != null)
{
cmbOperations.SelectedValue = cbSelectedValue.ToString();
}
}
Can anyone suggest a way to do this?
I've rewritten this answer to clarify some stuff.
First, the "default" text must be added as combo item as well.
Usage of combo.Text property just adds descriptive text to combobox which is "lost" first time user do something with a control.
If you like to permanently have "default" text in your combo, you must add it as an combobox item.
By the code you provided, just modify the
cmbOperations.Text = "-SELECT OPERATIONS-"; to
cmbOperations.Items.Insert(0, "-SELECT OPERATIONS-");
Note that this way you add the item "-SELECT OPERANDS-" to the 0th (read first) position in the list.
Also make sure that all your following items are increased by 1, because they are now moved by one space down in list.
Finally, put cboOperations.SelectedIndex = 0; line at the end of code. By doing so, you're telling combobox to display your "default" item initially when the form (or control) loads.
One more thing. I'm not pretty sure what do you want to achieve with the code beyond setting combo items, but if you like to check what user selected use cboOperations.SelectedIndex property which contains currently selected item in combo. You can add simple if(cboOperations.SelectedIndex == someIntValue){...}
The rest is your program logic ;)

Winforms control that works like ajax tag-completion

I want to create a winforms app where you can assign tags to an entity.
ofc I want the customer to re-use existing tags a lot. That's why I want
to show them the list of tags while they are typing (similar to intellisense
in VS and the tags-dropdown even here in stackoverflow ;))
do you have any control(s) in mind that offers this functionality?
can I reuse a ComboBox for this? (here I need to drop it down programatically - how?)
I want to have the taglist getting input-focus but not lose the mainform-focus,
and I want it to be on top over all windows and even range out of the mainform-area
(like intellisense in vs)
thx!
Here I have made a function to which pass the table name from which auto-complete has to be done, name of the field which needs to be auto-complete and the combobox which needs to be targeted.
Try the following code:
public void AutoCompleteTextBox(string tableName, string fieldName, ComboBox combToAutoComp)
{
AutoCompleteStringCollection txtCollection = new AutoCompleteStringCollection();
DataTable dtAutoComp = Dal.ExecuteDataSetBySelect("Stored_Procedure", fieldName, tableName);
if (dtAutoComp.Rows.Count >= 0)
{
for (int count = 0; count < dtAutoComp.Rows.Count; count++)
{
txtCollection.Add(dtAutoComp.Rows[count][fieldName].ToString());
}
}
combToAutoComp.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
combToAutoComp.AutoCompleteSource = AutoCompleteSource.CustomSource;
combToAutoComp.AutoCompleteCustomSource = txtCollection;
}
Here Dal.ExecuteDataSetBySelect is my implementation where i create the connection, command and dataadapter for calling the stored-procedure. You can replace it with your own implementation for the same. For more refer this link

Categories