first of all: sorry for the bad english, its not my first language.
I am currently working on a project where i have a list of persons which i want to list up in a combobox.
I want the combobox to get filtered, so that there are only the people listed up i am searchig for.
For example if i type in the combobox "Joh", there should only be people who start with "Joh" like "John", "Johann", ... .
The next thing is, my combobox is not "editable", how can i make it that i can write in it ? Currently it is "locked" ...
I hope you understand whats my problem, and how to solve it!
So one of the cool things about WPF is that it allows for binding. Specifically, binding properties in your code to controls in your UI. So to have a filtered combobox I would bind a list of whatever object you have to your combobox, something like below:
C#:
private List<Person> myList = new List<Person>();
public List<Person> MyList
{
get { return myList; }
set { myList = value; }
}
WPF:
<ComboBox Name="cboObjects" ItemsSource="{Binding MyList}"/>
That sets up your combobox to be bound to a list. So now we have to filter it down. So next I would use the KeyDown event to fire everytime the Combobox gets typed into.Then during that event you could capture the user's text, and try to find anything that matched that in the list, then set your list property equal to what was found..
private void cboObjects_KeyDown(object sender, KeyEventArgs e)
{
string temp = ((ComboBox)sender).Text;
var newList = MyList.Where(x => x.Name.Contains(temp));
MyList = newList.ToList();
}
Now your list of people objects has been filtered! Although there are a few issues with doing it this way, like the fact that you now no longer have your original list. Another thing is, if you go this approach, your UI will not update unless its told to. So make use of the INotifyPropertyChanged interface. It will essentially fire an event anytime you update a property which then tells your UI to retrieve the value again.
Finally, As for your combobox not being editable try setting IsReadOnly = false, and IsEditable = true!
This is actually built in!
What you need to do is set the following properties on your ComboBox control.
<ComboBox ItemsSource="{Binding PersonList}"
IsTextSearchEnabled="True"
TextSearch.TextPath="Name"/>
This example assumes you have a PersonList of type Person, in which type Person has a property of Name.
You'll want to set the TextSearch.TextPath to whatever property you want to search on (based on a property of the items in your ItemsSource collection).
More info, see https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.combobox.istextsearchenabled
Related
I tried to set DataSource of CheckedListBox like this:
private void Form1_Load(object sender, EventArgs e)
{
checkedListBox1.DisplayMember = "Name";
checkedListBox1.ValueMember = "Checked";
_bindingList = new BindingList<CustomBindingClass>(
new List<CustomBindingClass>
{
new CustomBindingClass {Checked = CheckState.Checked, Name = "Item1"},
new CustomBindingClass {Checked = CheckState.Checked, Name = "Item2"},
new CustomBindingClass {Checked = CheckState.Unchecked, Name = "Item3"},
});
checkedListBox1.DataSource = _bindingList;
}
And It's working but partially. I'm able to do the fallowing later
_bindingList.RemoveAt(0);
or _bindingList[0].Name = "TestTest"; and CheckedListBox updates well except items are not checked. This is not working
_bindingList[0].Checked=CheckState.Checked;
I also tested to do it when CheckedProperty from my CustomBindingClass is of type bool, but doesn't works either. Any suggestion what should be the type of ValueMember property ?
Consider these facts:
CheckedListBox does't have a built-in data-binding support for checking items. You need to handle check state of items yourself.
You set checkedListBox1.ValueMember = "Checked";. You didn't set item check state, you just said when you select the item, the value which returns by SelectedValue comes from Checked property of your object which is behind the seected item. For example you can use this code in a Click event of a Button to see the result; regardless of check-state of items, the message box, will show value of Checked property of the object behind the item:
MessageBox.Show(checkedListBox1.SelectedValue.ToString());
Selecting and checking items are completely different.
I prefer to use DataGridView for such purpose. You can simply have a CheckBox column and a readonly TextBox column and bind DataGridView to the list of your objects.
If you need to have two-way data binding, you need to implement INotifyPropertyChanged interface regardless of what control you are using to show data. If you don't implement that interface, when changing properties on your model ListChange event will not raise and you can not see changes in UI automatically.
If you take a look at CheckedListBox class, you'll notice that DataSource, DisplayMember and ValueMember are marked with
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
This a common technique used in Windows Forms controls to indicate that some public properties inherited from a base class (hence cannot be removed) are not applicable for that concrete derived class and should not be used.
There must be a reason for doing that for the aforementioned properties of the CheckedListBox. As you already saw, it's "sort of working", but the point is that it isn't guaranteed to work at all. So don't use them. If you wish, create a helper class that holds CheckedListBox and BindingList, listens to ListChanged event and synchronizes the control.
I have a ComboBox bound to a List via a DataSource. For some reason, when the datasource items change, the items in the combo box don't seem to automatically update. I can see in the debugger the datasource contains the correct items.
There are lots of answers on StackOverflow about this, but most are either unanswered, don't work for me, or require changing from using Lists to BindingLists which I cannot do this instance due to the volume of code which uses methods BindingLists don't have.
Surely there must be a simple way of just telling the ComboBox to refresh it's items? I can't believe this doesn't exist. I already have an event which fires when the Combo needs to be updated, but my code to update the values has no effect.
Combo declaration:
this.devicePortCombo.DataBindings.Add(
new System.Windows.Forms.Binding("SelectedValue",
this.deviceManagementModelBindingSource, "SelectedDevice", true,
DataSourceUpdateMode.OnPropertyChanged));
this.devicePortCombo.DataSource = this.availableDevicesBindingSource;
Code to update the combobox:
private void Instance_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "AvailableDevices")
{
// Rebind dropdown when available device list changes.
this.Invoke((MethodInvoker)delegate
{
devicePortCombo.DataSource = AvailableDevicesList;
devicePortCombo.DataBindings[0].ReadValue();
devicePortCombo.Refresh();
});
}
}
You are not binding the DataGridview's DataSource to same BindingSource object in your case this.availableDevicesBindingSource which bound first time. but later you are binding to different object AvailableDevicesList. again you are using another binding source for SelectedValue i.e this.deviceManagementModelBindingSource.
use one BindingSource only, may solve your issue
I have a DataGrid in WPF (a class that extends DataGrid), and I would like to edit the items in it. But of course I am getting the following error:
Operation is not valid while ItemsSource is in use.
Access and modify elements with ItemsControl.ItemsSource instead.
I have tried changing the itemsSource of the DataGrid, and then adding the items, but I still get the same error. Something like:
public class MyDG:DataGrid{
public void add(){
List<TimesheetRecord> records = new List<TimesheetRecord>();
foreach(TimesheetRecord rec in this.Items){
records.Add(rec);
}
//DO SOME STUFF, ADD MORE ITEMS TO records
ItemCollection col = this.Items;
this.ItemsSource = records;
col.Clear();
foreach(TimesheetRecord rec in records){
col.add(red);//exception thrown here
}
this.ItemsSource = col;
}
}
I don't understand why I am getting that error, when I have already changed the itemsSource to a different list...?
I can't (easily) add the items to the list which is originally bound as the itemsSource, because that list exists in a different class. Would it be best for me to just have a global variable in the MyDG class that is List<TimesheetRecord> myItems = new List<TimesheetRecord>(); and then in the constructor for MyDG go this.ItemsSource = myItems
Or do you have any other suggestions how I should go about doing this? I am open to anything, as this is the first time I have used databinding, so I am probably doing something wrong...
Decalre records collection as:
ObservableCollection<TimesheetRecord> records = new ObservableCollection<TimesheetRecord>();
and keep it data-bound to the DataGrid. Manipulate records collection as needed, data binding will take care of keeping UI in sync with the collection.
You have to choose whether to use Items or ItemsSource, you can't use both interchangably. Attempting to modify Items while using ItemsSource assumes an implicit conversion that isn't supported, hence the error.
In this case, it seems like the best approach might be to just set Items and add to that collection directly. To use ItemsSource, you'd need to, exactly as you wrote, pass a reference to the ItemsSource collection (List<TimesheetRecord>) in to your DataGrid class.
Once you assign "records" to the ItemsSource, you've already updated your collection. There's no need to manually add items to the dataGrid.Items collection.
I'm new to Windows Phone 7 development. I need to create a page very similar to the settings app page. Something like this (but without the menu at the top and the subtext for each item):
(source: dotnetapp.com)
So far I've got a listbox with items, but clicking on one of the items, the item color changes and it doesn't have the "pushed button" effect like the settings application has.
First question is how do I create this beautiful pushed button effect (notice that the button tilts when pressed depending on the position of the click).
My second question is about styling items differently. The ItemsSource of the listBox is defined like this:
List<string> firstList;
List<string> secondList;
public MainPage()
{
...
List<string> lst = new List<string>();
lst.AddRange(firstList);
lst.AddRange(secondList);
listBox1.ItemsSource = lst;
...
I need to style the items differently whether they come from firstList or secondList, for example if the item is from firstList its color should be blue.
I think it should be done using StaticResource, but i'm not sure. Maybe I'll need to somehow wrap the string so that it will have a getter for defining from which list it comes from.
Thanks.
Question 1 is answered (see William Mekanis comment)
For question 2 you have one big problem... you are binding a list of strings... no change to see which item is coming from which list.
I would create something like a view model for my DataSource list.
Something like (NotifyPropertyChanged is ignored here, implement it if needed and use an ObservableCollection also ;) ):
public class ListDataSourceViewModel
{
public string Text {get; set;}
public bool IsFromFirstList {get; set;}
}
In case you have more lists you could also use an enum or whatever as list identifier...
That you create a new list for the DataSource like:
lst.AddRange(firstList.Select(item => new ListDataSourceViewModel
{
Text = item, IsFromFirstList = true
}
).ToArray());
lst.AddRange(secondList.Select(item => new ListDataSourceViewModel
{
Text = item, IsFromFirstList = false
}
).ToArray());
Afterwards create a datatemplate for your listitem binding the text to a textblock and the font color for your textblock to the IsFromFirstList property using a converter.
This code is written from mind, without VS... not shure if u can copy paste without problem but it should give you the idea ;)
If you need help with creating the datatemplate and the converter just tell me!
Edit:
I rethought my suggestion... using converters, specialy in (potential) large lists, is not a good idea (for performance point of view). In your case it is anyway not a problem to use the needed color directly in the viewmodel.
I would change
public bool IsFromFirstList {get; set;}
to
public Color WhatEverColor {get; set;}
set it as needed when the VMs are created and bind it to wherever you need it.
Hope it helps!
I have a combobox on my form that is bound to a generic list of string like this:
private List<string> mAllianceList = new List<string>();
private void FillAllianceList()
{
// Add alliance name to member alliance list
foreach (Village alliance in alliances)
{
mAllianceList.Add(alliance.AllianceName);
}
// Bind alliance combobox to alliance list
this.cboAlliances.DataSource = mAllianceList;
}
The user may then add or remove items in the combobox.
I have read elsewhere that by simply adding or removing the item in the generic list, the contents of the combobox should automatically be updated; same thing should occur if I use Sort() on it.
But for some reason, I cannot make this work. I can see the combobox's DataSource property is correctly updated as I add/remove/sort items, but the contents displayed in the combobox are not those in the DataSource property.
I am surely missing something or doing something wrong.
Thanks in advance!
EDIT:
The answer I chose solved the issue for adding and removing, but a BindingList object cannot be sorted, and this is necessary for me. I've found a solution where a custom class is built by inheriting BindingList and adding sorting capabilities, but I would like to know if there's an easier solution in my case.
Any suggestions on how to solve this easily?
The easiest way around this would be to simply use a BindingList like so:
private List<string> mAllianceList = new List<string>();
private BindingList<string> bindingList;
private void FillAllianceList()
{
// Add alliance name to member alliance list
foreach (Village alliance in alliances)
{
mAllianceList.Add(alliance.AllianceName);
}
bindingList = new BindingList<string>(mAllianceList);
// Bind alliance combobox to alliance list
this.cboAlliances.DataSource = bindingList;
}
Then, from here on out, just deal with the binding list to add and remove items from there. That will remove it both from the List and from the ComboBox.
EDIT: To answer your question regarding sorting, I guess the easiest (but possibly "hacky" way to do it would be something like this:
mAllianceList.Sort();
bindingList = new BindingList<string>(mAllianceList);
this.cboAlliances.DataSource = bindingList;
So basically, after you sort, you create a new binding list and reset the data source. Maybe there's a more elegant way to go about this, however this should work.