Filtering CollectionViewSource - c#

I want to make a ComboBox bound to my data, with a filter. For that I've created a TextBox and a ComboBox. In the code behind I read a file and generate objects of class Channel that are stored as items of the ComboBox. Although the compiler throws no error the filtering doesn't work properly. If I write something the data is gone, if I erase, it's back. After trying and trying I've realized that if I started typing "myNamespace.myChannel" (Unico.Canal) the data remained, but don't filter. Strange behaviour, indeed. I suspect that I've put something in wrong place.
(for better understanding I've translated the code, Canal=Channel)
Here is the scheme of my code:
namespace Unico
{
public partial class ControlesArchivo : UserControl, INotifyPropertyChanged
{
public ControlesArchivo()
{
InitializeComponent();
}
public ObservableCollection<Channel> myListChannels //with INotifyPropertyChanged implemented. But I think I don't need it.
private void loadButton_Click(object sender, RoutedEventArgs e)
{
File loadedFile = new File();
loadedFile.read(); //Generates a bunch of data in lists.
foreach (Channel mychan in loadedFile.channels) //Just duplicating the data (maybe this can be avoided)
{
myListChannels.Add(mychan);
}
var view = CollectionViewSource.GetDefaultView(this.miListaDeCanales);
view.Filter = delegate(object o)
{
if (o.ToString().Contains(myTextBox.Text)) //Delicate place
{
return true;
}
return false;
};
myComboBox.ItemsSource = view;
DataContext = this;
}
private void myTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((ICollectionView)myComboBox.ItemsSource).Refresh();
myComboBox.SelectedIndex = 0;
}
}
}
The data is bound in XAML with:
ItemsSource="{Binding view}"
EDIT: I think I know where is the problem: I'm not specifing the property to filter. I mean, what you see in the ComboBox is the property channelName of the class Channel listed in myListChannels. When I'm setting the filter, shouldn't I let know what I'm filtering? How could I write this? Thank you very much.

Yes your assumption is correct.
I'm assuming with your translations,
public ObservableCollection<Channel> myListChannels;
is actually
public ObservableCollection<Canal> miListaDeCanales;
with the class Canal in the namespace Unico
Update:
In your filter try using the property that is rendered in the ComboBox than use the ToString() on the object(o) if you've not overridden ToString() from System.Object.
try switching
if (o.ToString().Contains(myTextBox.Text))
to
if (((Canal)o).NameProperty.Contains(myTextBox.Text))
^^ that should fix your issue.
Do you have a DataTemplate for ComboBox.ItemTemplate in xaml. That will explain why you see the valid value rendered in the ComboBox, else all the ComboBoxItem's will also render as Unico.Canal

Related

Property or indexer '' cannot be assigned to -- it is read only

I have a class that is a list of class objects.
[Serializable]
public class UserRequestOrders
{
private List<RTORequestOrder> m_thisUsersOrders = new List<RTORequestOrder>();
public List<RTORequestOrder> RequestOrders
{
get { return m_thisUsersOrders; }
set { m_thisUsersOrders = value; }
}
}
When I create an instance of this object I need to populate the list variable When m_thisUsersOrders with an existing list of requests (from a viewstate).
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
When the page posts back, I cast the viewstate into a list of RTORequestOrder objects, and try to set the RequestOrders property, I get the message that the property or indexer cannot be assigned to. I have a "SET" accessor for the property.
All posts that I have read on this topic state that I need to modify scope in the application settings, but I am not sure how that applies here. I don't have any application settings set.
I am hoping someone can help me understand how I can populate the list from an existing list.
EDIT: Nico, Thanks for your response! Below is the full code from the code behind page. "MyOrders" and "lst" are essentially the same thing. When I am working with "lst" everything works the way that I want it to. The reason that I want to use the "MyOrders" object instead is because I have a method that returns the list as a datatable with only the fields I need to display. (I didnt' show that code because the issue appears to be with the "SET" accessor.) "lst" has the same signiture as the "MyOrders.RequestOrders". Why can I cast the the viewstate into the lst object, but not the MyOrders object?
EDIT: Grant, thanks for your response as well. I don't know how to set breakpoints for ASP pages... :(
public partial class Default3 : System.Web.UI.Page
{
RTODataEntryObjects.UserRequestOrders MyOrders = new RTODataEntryObjects.UserRequestOrders();
List<RTODataEntryObjects.RTORequestOrder> lst = new List<RTODataEntryObjects.RTORequestOrder>();
void Page_PreRender(object sender, EventArgs e)
{
if (ViewState["vsMyOrders"] == null)
{
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"]; // code to show the data in the grid when page loading
}
}
protected void Page_Load(object sender, EventArgs e)
{
NewDataGrid.EnableViewState = true;
NewDataGrid.DataSource = (RTODataEntryObjects.UserRequestOrders)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
public void btnSubmit(object sender, EventArgs e)
{
int id = Int32.Parse(TextBox1.Text); // dynamically getting the data from the frontend.
string name = TextBox2.Text; // dynamically getting the data from the frontend.
if (ViewState["vsMyOrders"] != null) // if the view state is already having data then can update the list and assign again to the viewstate later.
{
lst = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
MyOrders.RequestOrders = (List<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
}
RTODataEntryObjects.RTORequestOrder thisOrder = new RTODataEntryObjects.RTORequestOrder(id, name, User.Identity.Name, System.Environment.MachineName);
lst.Add(thisOrder); //
MyOrders.AddNew(thisOrder);
ViewState["vsMyOrders"] = MyOrders;
NewDataGrid.DataSource = (IList<RTODataEntryObjects.RTORequestOrder>)ViewState["vsMyOrders"];
NewDataGrid.DataBind();
}
}

The BindingList Datasource of a Combobox refreshes correctly but the Combobox displays items in the wrong order

I have a BindingList< KeyValuePair < string, string > > that is bound to a ComboBox control. Based on some conditions, the BindingList will be added a new KeyValuePair. Now, the Newly added item shows up at index 0 of the Combobox, instead of at the end.
While debugging, I found that the BindingList has got the right order. (i.e, the new KeyValuePair is appended)
Also, I check the SelectedValue of the ComboBox in it's SelectedIndexChanged handler and it seems to be not of the ListItem that got selected. Instead, it is that of the supposed ListItem, if the ComboBox had got the right order as in its DataSource, - the BindingList..
The code is a small part of a large project.. Plz let me know if the question is not clear. I can put the relevant parts of the code as per our context.
How could something like this happen? What can I do differently?
I have this class something like this.
public class DropdownEntity
{
//removed all except one members and properties
private string frontEndName
public string FrontEndName
{
get {return this.frontEndName; }
set {this.frontEndName= value; }
}
//One Constructor
public DropdownEntity(string _frontEndName)
{
this.FrontEndName = _frontEndName;
//Removed code which initializes several members...
}
//All methods removed..
public override string ToString()
{
return frontEndName;
}
}
In my windows form, I have a tab control with several tabs. In one of the tabs pages, I have a DataGridView. The user is supposed to edit the cells and click on a Next - button. Then, some processing will be done, and the TabControl will be navigated to the next tab page.
The next tab page has the combobox that has the problem I mentioned. This page also has a back button, which will take back.. the user can modify the gridview cells again.. and click on the next button. This is when the order gets messed up.
I am posting here the Click event handler of the Next Button.. Along with the class, with the rest of the code removed.
public partial class AddUpdateWizard : Form
{
//Removed all members..
BindingList<KeyValuePair<string, string>> DropdownsCollection;
Dictionary<string, DropdownEntity> DropdownsDict;
//Defined in a partial definition of the class..
DataGridView SPInsertGridView = new DataGridView();
ComboBox DropdownsCmbBox = new ComboBox();
Button NextBtn2 = new Button();
Button BackBtn3 = new Button();
//Of course these controls are added to one of the panels
public AddUpdateWizard(MainForm mainForm)
{
InitializeComponent();
DropdownsDict = new Dictionary<string, DropdownEntity>();
}
private void NextBtn2_Click(object sender, EventArgs e)
{
string sqlArgName;
string frontEndName;
string fieldType;
for (int i = 0; i < SPInsertGridView.Rows.Count; i++)
{
sqlArgName = "";
frontEndName = "";
fieldType = "";
sqlArgName = SPInsertGridView.Rows[i].Cells["InsertArgName"].Value.ToString().Trim();
if (SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value != null)
{
frontEndName = SPInsertGridView.Rows[i].Cells["InsertArgFrontEndName"].Value.ToString().Trim();
}
if (SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value != null)
{
fieldType = SPInsertGridView.Rows[i].Cells["InsertArgFieldType"].Value.ToString().Trim();
}
//I could have used an enum here, but this is better.. for many reasons.
if (fieldType == "DROPDOWN")
{
if (!DropdownsDict.ContainsKey(sqlArgName))
DropdownsDict.Add(sqlArgName, new DropdownEntity(frontEndName));
else
DropdownsDict[sqlArgName].FrontEndName = frontEndName;
}
else
{
if (fieldType == "NONE")
nonFieldCount++;
if (DropdownsDict.ContainsKey(sqlArgName))
{
DropdownsDict.Remove(sqlArgName);
}
}
}
//DropdownsCollection is a BindingList<KeyValuePair<string, string>>.
//key in the BindingList KeyValuePair will be that of the dictionary.
//The value will be from the ToString() function of the object in the Dictionary.
DropdownsCollection = new BindingList<KeyValuePair<string,string>>(DropdownsDict.Select(kvp => new KeyValuePair<string, string>(kvp.Key, kvp.Value.ToString())).ToList());
DropdownsCmbBox.DataSource = DropdownsCollection;
DropdownsCmbBox.DisplayMember = "Value";
DropdownsCmbBox.ValueMember = "Key";
//Go to the next tab
hiddenVirtualTabs1.SelectedIndex++;
}
private void BackBtn3_Click(object sender, EventArgs e)
{
hiddenVirtualTabs1.SelectedIndex--;
}
//On Selected Index Changed of the mentioned Combobox..
private void DropdownsCmbBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (DropdownsCmbBox.SelectedValue != null)
{
if (DropdownsDict.ContainsKey((DropdownsCmbBox.SelectedValue.ToString())))
{
var dropdownEntity = DropdownsDict[DropdownsCmbBox.SelectedValue.ToString()];
DropdownEntityGB.Text = "Populate Dropdowns - " + dropdownEntity.ToString();
//Rest of the code here..
//I see that the Datasource of this ComboBox has got the items in the right order.
// The Combobox's SelectedValue is not that of the selected item. Very Strange behavior!!
}
}
}
}
The very first time the user clicks the Next Button, it's fine. But if he clicks the Back Button again and changes the Data Grid View cells.. The order will be gone.
I know, it can be frustrating to look at. It's a huge thing to ask for help. Any help would be greatly appreciated!
Please let me know if you need elaboration at any part.
Thanks a lot :)
I think you have two problems here.
First, if you want to retain the order of the items you should use an OrderedDictionary instead of a regular one. A normal collection will not retain the order of the items when you use Remove method. You can see more info about this related to List here.
You could use such dictionary like this:
DropDownDict = new OrderedDictionary();
// Add method will work as expected (as you have it now)
// Below you have to cast it before using Select
DropDownCollection = new BindingList<KeyValuePair<string, string>>(DropDownDict.Cast<DictionaryEntry>().Select(kvp => new KeyValuePair<string, string>(kvp.Key.ToString(), kvp.Value.ToString())).ToList());
The second problem could be that you change the display name (FrontEndName) of already existing items, but the key is preserved. When you add a new item, try to remove the old one that you're not using anymore and add a new item.
The Sorted Property of the Combobox is set to True! I didn't check that until now. I messed up. Terribly sorry for wasting your time Adrian. Thanks a lot for putting up with my mess here.. :)

Browsable(false) at run time?

I am using a datasource to populate my datagridview with the data. However, im trying to find a way for the user to be able to hide columns that he does not want to see.
I am able to hide and show columns before the program runs using:
[Browsable(false)]
public string URL
{
get
{
return this._URL;
}
set
{
this._URL = value;
this.RaisePropertyChnaged("URL");
}
}
I cannot seem to figure out how to change the [Browsable(false)] at run time.
Any ideas how I could accomplish this?
Basically, I want to bind an "on/off" to a menu.
Apologies if im not using the right terminology when explaining my problem, I am self taught and started a few weeks ago - so still very newbie :)
Edit:
Cant hide the column because when i run my update function all columns appear again. Here is my function for updating:
private void UpdateResults()
{
Invoke(new MethodInvoker(
delegate
{
this.dgvResults.SuspendLayout();
this.dgvResults.DataSource = null;
this.dgvResults.DataSource = this._mySource;
this.dgvResults.ResumeLayout();
this.dgvResults.Refresh();
}
));
}
At run time, you can just specify the column as being invisible:
dgv.Columns["ColumnName"].Visible = false;
The way to do this properly at runtime is to provide a custom ITypedList implementation on the collection, or provide a TypeDescriptionProvider for the type, or (for single-object bindings, not lists), to implement ICustomTypeDescriptor. Additionally, you would need to provide your own filtered PropertyDescriptor implementation. Is it really worth it? In most cases: no. It is much easier to configure the grid properly, showing (or not) the appropriate columns by simply choosing which to add.
Indeed, as others had mention the purpose of BrowsableAttribute is different, but I understand what you want to do:
Let's suppose that we want to create a UserControl than wraps a DataGridView and gives the user the ability to select which columns to display, allowing for complete runtime binding. A simple design would be like this (I'm using a ToolStrip, but you can always use a MenuStrip if that's what you want):
private void BindingSource_ListChanged(object sender, ListChangedEventArgs e) {
this.countLabel.Text = string.Format("Count={0}", this.bindingSource.Count);
this.columnsToolStripButton.DropDownItems.Clear();
this.columnsToolStripButton.DropDownItems.AddRange(
(from c in this.dataGrid.Columns.Cast<DataGridViewColumn>()
select new Func<ToolStripMenuItem, ToolStripMenuItem>(
i => {
i.CheckedChanged += (o1, e2) => this.dataGrid.Columns[i.Text].Visible = i.Checked;
return i;
})(
new ToolStripMenuItem {
Checked = true,
CheckOnClick = true,
Text = c.HeaderText
})).ToArray());
}
In this case, bindingSource is the intermediary DataSource of the dataGrid instance, and I'm responding to changes in bindingSource.ListChanged.

Adding Items to ListView too slow in C#

I want to add item to listview control. This is a bit of code :
this.lView.ListViewItemSorter = null;
ListViewItem[] lvitems = new ListViewItem[ListMyObjects.Count];
int index = 0;
foreach (MyObject object in ListMyObjects)
{
ListViewItem item = new ListViewItem();
item.Text = object.Name;
lvitems[index++] = item;
}
this.lView.BeginUpdate();
this.lView.Items.AddRange(lvitems); // Slow in here with debugger
this.lView.EndUpdate();
I'm only add about 1000 item but it's very slowly. It's spend about 15secs to finish.
why does anyone know the reason ? Thank in advance.
Edit :
I have customized listview before.
public partial class MyListView: ListView
{
public MyListView()
{
InitializeComponent();
this.View = View.Details;
this.FullRowSelect = true;
this.DoubleBuffered = true;
}
private bool mCreating;
private bool mReadOnly;
protected override void OnHandleCreated(EventArgs e)
{
mCreating = true;
base.OnHandleCreated(e);
mCreating = false;
}
public bool ReadOnly
{
get { return mReadOnly; }
set { mReadOnly = value; }
}
protected override void OnItemCheck(ItemCheckEventArgs e)
{
if (!mCreating && mReadOnly) e.NewValue = e.CurrentValue;
base.OnItemCheck(e);
}
}
I do it because i don't want to hang when i use multiple threading. I don't know what does this influenced to it ?
You could make it much faster by enabling virtual mode.
However, that will take some work.
The preferred way of adding multiple items is to use the AddRange() method. However if you must add the items one by one you can use the BeginUpdate() and EndUpdate() methods around your loop. Following is from the MSDN
The preferred way to add multiple items to a ListView is to use the AddRange method of the ListView.ListViewItemCollection (accessed through the Items property of the ListView). This enables you to add an array of items to the list in a single operation. However, if you want to add items one at a time using the Add method of the ListView.ListViewItemCollection class, you can use the BeginUpdate method to prevent the control from repainting the ListView every time that an item is added.
Appologies for a more architectural solution, but if your domain objects are large this might cause the bottleneck (reading the comments it sounds like they may be slowing it down). Before you get to the presentation layer you could flatten them into some (very simple) domain transfer objects (DTOs): literally just a bag of getters-and-setters.
A tool like AutoMapper could potentially take a lot of the donkey work out there
That way your domain objects stay in the business logic domain (where they belong) and your presentation layer just gets the data in needs from the DTO.
Sorry for the non-code-based suggestion :) good luck!

WP7 Silverlight - Change DataContext in runetime

how do I change the DataContext to another view model in runtime.
Now the data is not changed, after I run the click event:
public PivotPage1()
{
InitializeComponent();
DataContext = App.TeamDocViewModel;
}
private void Button_Click(object sender, EventArgs e)
{
DataContext = App.TaskViewModel;
}
Some suggestions?
First I think you have a typo: App.TaskViewMode should be App.TaskViewModel. Second: Your code should work. There might be problem with binding in your xaml file, would be nie if you post it here. (Maybe you bind to submembers like "User.Name" and don't implement INotifyPropertyChanged in your models)
What happens if you change DataContext = App.TaskViewModel; to DataContext = null; ?) Xaml is interesting to see.
The code you're written should be fine. It depends on what TaskViewMode is and how it works.
Assuming that it is based on the MainViewModel in the default Pivot project template. Are you calling LoadData() on it to populate the Items Collection?

Categories

Resources