Im using combo box and I have a list that opened when you choose the arrow,I want
to put text on the combo box that you will see it witout opening the combo box,
how can I do that?
currently I try with text="name" ,name="name" witout success ,the combo box doesnt
display anything as a text. just list from drop down...
like the following
http://msdn.microsoft.com/en-us/library/ms753382%28v=vs.85%29.aspx
in addition how can I change the arrow like to be like in the link
Set the first item in the collection list as the default selection.
There is an example here:
How to show text in combobox when no item selected?
EDIT :
public class MyViewModel
{
public MyViewModel()
{
Items.Add("Select one item");
Items.Add("Item1");
Items.Add("Item2");
Items.Add("Item3");
SelectedItem = Items[0];
}
private List<String> _items;
public List<String> Items
{
get{ return _items; }
set
{
_items = value;
RaisePropertyChanged(() => Items);
}
}
private String> _selectedItem;
public String SelectedItem
{
get{ return _selectedItem; }
set
{
_selectedItem= value;
RaisePropertyChanged(() => SelectedItem);
}
}
}
In your xaml file you have to bind to the list of items and to the selected item:
<ComboBox x:Name="myComboBox"
ItemsSource="{Binding Items}"
SelectedValue="{Binding SelectedItem}" />
And don't forget to set the DataContext to your view model.
Related
I have a ComboBox binded with an ObservableCollection. How can I do when user enter a text in the ComboBox, if item not in the list, the code automatically add a new item to the list?
<ComboBox Name="cbTypePLC"
Height="22"
ItemsSource="{StaticResource TypePLCList}"
SelectedItem="{Binding TypePLC}" IsReadOnly="False" IsEditable="True">
</ComboBox>
Bind Text property of your combo box to your view model item and then add to the bound collection there, like,
Text="{Binding UserEnteredItem, UpdateSourceTrigger=LostFocus}"
Change the UpdateSourceTrigger to LostFocus because default (PropertyChanged) will communicate each character change to your viewmodel.
// user entered value
private string mUserEnteredItem;
public string UserEnteredItem {
get {
return mUserEnteredItem;
}
set {
if (mUserEnteredItem != value) {
mUserEnteredItem = value;
TypePLCList.Add (mUserEnteredItem);
// maybe you want to set the selected item to user entered value
TypePLC = mUserEnteredItem;
}
}
}
// your selected item
private string mTypePLC;
public string TypePLC {
get {
return mTypePLC;
}
set {
if (mTypePLC != value) {
mTypePLC = value;
// notify change of TypePLC INPC
}
}
}
// your itemsource
public ObservableCollection <string> TypePLCList { set; private set;}
Ok so I have spent hours now trying to figure this out and I cant.
I have the below combo box which is binding correctly to my collection of data.
<ComboBox Name="cbx" Width="250" Height="25"
Visibility="{Binding Path=IsComboBox,Converter={StaticResource BoolConverter}}"
ItemsSource="{Binding Path=Answers}"
SelectedValuePath="AnswerId"
SelectedItem="{Binding Path=SelectedAnswer, Mode=TwoWay}"
DisplayMemberPath="Answer"/>
The Selected Item however is not populating top my Selected Answer property. I put a textbox on the form and bound it to SelectedAnswer.Answer and that is binding to the answer correctly.
For some reason though my combo box will not bind the selected answer
I have read something about the layout of the combo box property and tried changing that, also stepped through the getter and setter of the property to ensure it is not clearing down (which is not as it will bind to the text box)
Please help with this.
SurveyAnswer:
public class SurveyAnswer : INotifyPropertyChanged
{
private Guid answerId;
public Guid AnswerId
{
get { return answerId; }
set {
answerId = value;
NotifyPropertyChanged("AnswerId");
}
}
private string answer;
public string Answer
{
get { return answer; }
set {
answer = value;
NotifyPropertyChanged("Answer");
}
}
public Guid SurveyLineID { get; set; }
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set {
isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
#region NotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
I think you need to change SelectedItem to SelectedValue. Sometimes that order of parameters matters as well.
<ComboBox Name="cbx" Width="250" Height="25"
Visibility="{Binding Path=IsComboBox,Converter={StaticResource BoolConverter}}"
ItemsSource="{Binding Path=Answers}"
SelectedValue="{Binding Path=SelectedAnswer, Mode=TwoWay}"
DisplayMemberPath="Answer" SelectedValuePath="AnswerId"/>
This is helpful:
http://johnpapa.net/binding-to-silverlight-combobox-and-using-selectedvalue-selectedvaluepath-and-displaymemberpath
I have a List box that I want to display a list of objects, I am following the MVVM pattern and am finding it difficult to achieve what I want.
MainWindowView.xaml
<ListBox ItemsSource="{Binding Path=MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainWindowViewModel.cs
private List<ListBoxItem> _myList = new List<ListBoxItem>();
public List<ListBoxItem> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
public SprintBacklogViewModel()
{
foreach(MyObject obj in MyObjects.MyObjectList)
{
ListBoxItem item = new ListBoxItem();
item.Content = obj;
MyList.Add(item);
}
}
MyList is getting updated correctly, but nothing is displaying in the window. (ItemsSource="{Binding Path=MyList}" also works, I tested with different data) I have not used an ItemTemplate before so any pointers are welcome. My understanding of it is that If I set it up correctly It will display the data in my objects.
eg:
<Label Content="{Binding Path=Name}"/>
there is a property in MyObject called Name, I want to display this as a label in my list
*EDIT
In my window I get a line of text - mynamespace.MyObject
MyList property in ViewModel is property of type ListBoxItem, it has property Name but it's not Name of MyObject. So you need to change your property in your ViewModel by
Replace
private List<ListBoxItem> _myList = new List<ListBoxItem>();
public List<ListBoxItem> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
with
private List<MyObject> _myList = new List<MyObject>();
public List<MyObject> MyList
{
get { return _myList ; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
Your list should not contain UI-Elements but data (you are data-binding), if you bind to a list of ListBoxItems the ListBox will disregard the ItemTemplate and just use the items as they fit the expected container for the ListBox. Containers will be generated automatically, you do not need to do that in your list.
If you add items to a collection at runtime the binding engine needs to be notified to update the changes, for this you should use an ObservableCollection or anything that implements INotifyCollectionChanged. (When doing so you further usually make the field readonly and only provide a getter) This is the reason why there are no items.
I have a Dictionary which is binded to a combobox. I have used dictionary to provide spaces in enum.
public enum Option {Enter_Value, Select_Value};
Dictionary<Option,string> Options;
<ComboBox
x:Name="optionComboBox"
SelectionChanged="optionComboBox_SelectionChanged"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding Path = SelectedOption}"
ItemsSource="{Binding Path = Options}" />
This works fine.
My queries:
1. I am not able to set the initial value to a combo box.
In above XAML snippet the line
SelectedItem="{Binding Path = SelectedOption}"
is not working. I have declared SelectOption in my viewmodel. This is of type string and I have intialized this string value in my view model as below:
SelectedOption = Options[Options.Enter_Value].ToString();
2. The combobox is binded to datadictionary which have two options first is "Enter_value" and second is "Select_value" which is actually Option enum.
Based on the Option enum value I want to perform different action.
For example
if option is equal to option.Enter_value then
Combo box becomes editable and user can enter the numeric value in it.
if option is equal to option.Select_value value then
the value comes from the database and the combo box becomes read only and shows the fetched value from the database.
Please Help!!
Try binding SelectedValue, not SelectedItem if SelectedOption is of type Option.
About your second question: Based on selection you can hide your ComboBox and display a TextBlock or TextBox in it's place. Or you can use RadioButtons and enable or disable input accordingly.
Your problem, probably, is that you've bound SelectedItem to a property of the wrong type.
An ItemsControl iterates over its ItemsSource's enumerator to build its list of items. The enumerator for your dictionary is of type KeyValuePair<Option, string>. So your SelectedOption property must also be of that type - if you look in the Output window when your application is running, you'll probably see a data-binding error to that effect there.
I can't understand your second question.
Edit
Okay, it's a lot easier to just provide a working example than to explain why code that I can't see isn't working.
First, you need a view model class that implements INotifyPropertyChanged and that exposes SelectedItem, Value, and IsValueReadOnly properties, and that correctly raises PropertyChanged events for those properties when the selected item changes.
public enum Option
{
EditOption,
OtherOption
} ;
public class MyViewModel : INotifyPropertyChanged
{
private Dictionary<Option, string> _Items;
private KeyValuePair<Option, string> _SelectedItem;
private string _Value;
public MyViewModel()
{
_Items = new Dictionary<Option, string>
{
{Option.EditOption, "Editable value"},
{Option.OtherOption, "Other value"}
};
}
public Dictionary<Option, string> Items
{
get { return _Items; }
}
public KeyValuePair<Option, string> SelectedItem
{
get { return _SelectedItem; }
set
{
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
OnPropertyChanged("IsValueReadOnly");
OnPropertyChanged("Value");
}
}
public bool IsValueReadOnly
{
get { return _SelectedItem.Key != Option.EditOption; }
}
public string Value
{
get { return IsValueReadOnly ? "Read-only" : _Value; }
set { _Value = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
{
h(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Now the XAML for your ComboBox and TextBox looks like this:
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication6="clr-namespace:WpfApplication6"
Title="MainWindow">
<Window.DataContext>
<WpfApplication6:MyViewModel/>
</Window.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding Items}"
DisplayMemberPath="Key"
SelectedItem="{Binding SelectedItem}"/>
<TextBox Text="{Binding Value}"
IsReadOnly="{Binding IsValueReadOnly}"/>
</StackPanel>
</Window>
Consider the following XAML:
<ComboBox Name="CompanyComboBox"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Path=GlobalData.Companies}"
SelectedValuePath="Id"
SelectedValue="{Binding Customer.CompanyId, ValidatesOnDataErrors=True}"
DisplayMemberPath="Name" />
GlobalData.Companies is a collection (IEnumerable<Company>) of companies; this collection can be reloaded on background (it is downloaded from a webservice). When this happens, ComboBox correctly reloads items via binding. However as a side-effect, it also resets the selected item!
I have used Reflector to inspect combo-box sources and apparently this is intended behavior.
Is there any "nice" way how to get around this? What I want to achieve, is that if the user selects "Company A" and reloads list of companies afterwards, then "Company A" stays selected (assuming it is in the new list).
Please try with the following code.
Enable the following property to the combo box
IsSynchronizedWithCurrentItem="True"
Maybe you can use ObservableCollection<Company> instead of your IEnumerable<Company>? Then, on background change you would only Add / Remove items that are new / absent in the new list, selected item should stay, unless it was removed by the change.
You can update your observable collection in a separate thread with a small hack-around.
hmm, I don't know if it is a "nice" way, but if you can access the selected item before the reload occurs, you can save it (or its key or something), and select it programatically again after the reload is done.
quick mockup:
var selectedItem = myCombo.SelectedItem;
DoReload();
myCombo.SelectedItem = selectedItem;
But I assume you mean another way than this manual work around?
Hope this helps anyway...
UPDATE
Ok I see, from a background thread.
Are you using an ICollectionView to bind your combobox too? If so, you can use the CurrentItem property to keep a reference. I made a quick mockup, and this is working on my setup. this assumes you have a reference to your UI:
XAML
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" Grid.Column="0" Grid.Row="0" DisplayMemberPath="Name"/>
<Button Command="{Binding UpdateCommand}" Grid.Column="1" Grid.Row="0">Update</Button>
</Grid>
View/ViewModel
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
this.DataContext = new ViewModel(this);
}
}
public class ViewModel
{
private readonly Window1 window;
private ObservableCollection<Item> items;
private ICollectionView view;
public ViewModel(Window1 window) {
this.window = window;
items = new ObservableCollection<Item>
{
new Item("qwerty"),
new Item("hello"),
new Item("world"),
};
view = CollectionViewSource.GetDefaultView(items);
}
public ObservableCollection<Item> Items { get { return items; } }
public ICommand UpdateCommand {
get { return new RelayCommand(DoUpdate); }
}
public Item SelectedItem { get; set; }
private void DoUpdate(object obj) {
var act = new Func<List<Item>>(DoUpdateAsync);
act.BeginInvoke(CallBack, act);
}
private List<Item> DoUpdateAsync() {
return new List<Item> {
new Item("hello"),
new Item("world"),
new Item("qwerty"),
};
}
private void CallBack(IAsyncResult result) {
try {
var act = (Func<List<Item>>)result.AsyncState;
var list = act.EndInvoke(result);
window.Dispatcher.Invoke(new Action<List<Item>>(delegate(List<Item> lst) {
var current = lst.Single(i => i.Name == ((Item)view.CurrentItem).Name);
Items.Clear();
lst.ForEach(Items.Add);
view.MoveCurrentTo(current);
}), list);
} catch(Exception exc){ Debug.WriteLine(exc); }
}
}
public class Item {
public Item(string name) {
Name = name;
}
public string Name { get; set; }
}
You will need to do some handling in case the selected item is no longer in the list.
The IsSynchronizedWithCurrentItem property is important here, else it won't work!
Also, the way the reference to the main window is made should be by a DI-framework.
As Yacoder pointed out this has to do with object equality. As long as you bind SelectedValue instead of SelectedItem you can define the ItemsSource as an anonymous type collection. Then this problem will not occur (and it is also faster if you need to read the values from a database).