I am trying to bind values of an enum to a combo box but the combo box remain empty with no options to choose.
This is the combo box xaml defintion:
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Path=SkillItemSource}" SelectedItem="{Binding Path=neededSkill, Mode=TwoWay}" SelectedIndex="0" Margin="5" MinWidth="100"></ComboBox>
And this is the items source and selected item which are defined in the window's cs:
public Skill neededSkill = Skill.FirstSkill;
public string[] SkillItemSource
{
get
{
return Enum.GetNames(typeof(Skill));
}
}
What is missing for the values to appear in the combobox?
What is missing for the values to appear in the combobox?
You need to set the DataContext of the ComboBox, or a parent element, to an instance of the class where the SkillItemSource property is defined. If the property is defined in the code-behind, you could just set the DataContext to the view itself: this.DataContext = this;
Also, you can't mix types. If the ItemsSource is bound to an IEnumerable<string>, the SelectedItem property should be bound to a string property.
Also note that neededSkill must be defined as a public property for you to be able to bind to it.
Try this:
public Skill neededSkill { get; set; } = Skill.FirstSkill;
public IEnumerable<Skill> SkillItemSource { get; } = Enum.GetValues(typeof(Skill)).Cast<Skill>();
Related
I have managed to bind ItemsSource and ComboBox lets me choose each option, but I cannot see which option has been chosen. ComboBox is just blank.
XAML code:
<ComboBox
Name="Position"
Grid.Row="5"
SelectedValue="{Binding Position}"
ItemsSource="{Binding Positions}"
Style="{StaticResource MaterialDesignComboBox}"
Margin="15,10,15,10"
FontSize="12"/>
Tried basic ComboBox (non-material design) and results are identical.
I will provide more code if you need it, but so far it seems that this control is just broken, it doesn't work as it should. I'm probably missing some small detail how to properly set it up.
Edit
ViewModel:
public class WindowAddEmployeesViewModel : EmployeesViewModel, INotifyPropertyChanged
{
public ObservableCollection<PositionsViewModel> Positions { get; set; }
new public event PropertyChangedEventHandler PropertyChanged;
}
Base class contains things like FirstName, LastName, Position etc. INotifyPropertyChanged not implemented because Fody.PropertyChanged does it for me.
PositionViewModel:
public class PositionsViewModel : INotifyPropertyChanged
{
public string Position { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public override string ToString()
{
return $"{Position}";
}
}
Edit
Switching IsEditable to True makes it visible, but i don't want user to be able to edit it.
You misundestood the purpose of SelectedValue. You can bind to the SelectedValue instead of SelectedItem. It has nothing to do with the value being displayed by the ComboBox.
The displayed value can be defined by setting ItemsControl.DisplayMemberPath to the desired property on the data model, but only when ItemTemplate is not defined. DisplayMemberPath is meant to replace the DataTemplate in simple scenarios.
You obviously want to set the DisplayMemberPath.
Also your current binding
<ComboBox SelectedValue="{Binding Position}" .../>
won't resolve (no matter the state of ComboBox.IsEditable) as the DataContext of the ComboBox is obviously the WindowAddEmployeesViewModel and not the PositionsViewModel. This could've been a hint that you are using SelectedValue wrong.
SelectedItem: the currently selected data model.
SelectedValue: returns the property's value on the SelectedItem, defined by SelectedValuePath.
SelectedValuePath: sets the path to the property, which should be the SelectedValue on the SelectedItem. Argument is a string.
DisplayMemberPath: sets the path to a property on each data model which is used to display the item in the ComboBox. Argument is a string.
Data model
public class PositionsViewModel : INotifyPropertyChanged
{
public string Label { get; set; }
public string Position { get; set; }
public override string ToString() => Position;
}
The view
<!-- Since DisplayMemberPath="Position" the ComboBox will show the value of the Position property as its items -->
<ComboBox x:Name="PositionComboBox"
DisplayMemberPath="Position"
SelectedValuePath="Label"
ItemsSource="{Binding Positions}" />
<!--
Displays the PositionsViewModel. Implicitly invokes PositionsViewModel.ToString().
The TextBox will therefore display the property value of `PositionsViewModel.Position`.
-->
<TextBox Text="{Binding ElementName=PositionComboBox, Path=SelectedItem}" />
<!--
Displays the SelectedValue of the ComboBox. This value is defined by ComboBox.SelectedValuePath.
The TextBox will therefore display the property value of `PositionsViewModel.Label`
-->
<TextBox Text="{Binding ElementName=PositionComboBox, Path=SelectedValue}" />
I'm trying to set a default value into a combo box when the application is first loading using the MVVM pattern and it looks like this is all the time unset, combo box being all the time empty when the page loads.
This is my xaml:
<ComboBox Grid.Row="0" Margin="10,0,0,0" Grid.Column="1"
SelectedItem="{Binding Path=JuiceOperations.SelectedItemOption, Mode=TwoWay}"
SelectedIndex="{Binding Path=JuiceOperations.SelectedComboBoxOptionIndex, Mode=TwoWay}"
SelectedValue="{Binding Path=JuiceOperations.SelectedComboBoxOptionIndex, Mode=TwoWay}"
ItemsSource="{Binding Path=JuiceOperations.JuiceOptions}" />
This is the view model code, with its default constructor:
public JuiceViewModel()
{
juiceOperations.SelectedComboBoxOptionIndex = 0;
juiceOperations.SelectedItemOption = "Cola";
}
where I am trying to set the default value of the combo box.
And this is how the properties looks like:
private List<string> juiceOptions = new List<string> { "Cola", "Sprite", "Fanta", "Pepsi" };
private string selectedItemOption = string.Empty;
private int selectedComboBoxOptionIndex = 0;
public int SelectedComboBoxOptionIndex
{
get
{
return this.selectedComboBoxOptionIndex;
}
set
{
this.selectedComboBoxOptionIndex = value;
this.OnPropertyChanged("SelectedComboBoxOptionIndex");
}
}
public List<string> JuiceOptions
{
get
{
return this.juiceOptions;
}
set
{
this.juiceOptions = value;
}
}
public string SelectedItemOption
{
get
{
return this.selectedItemOption;
}
set
{
this.selectedItemOption = value;
this.OnPropertyChanged("SelectedItemOption");
}
}
When selecting an item from combo box the selection is updated into the model and also into the view, so it is working as expected but when the page is first loaded even if the "SelectedComboBoxOptionIndex" and "SelectedItemOption" are being called and their value updated the view of the page is not updated and the empty string is being shown into the combo box where I was expected to see the "Cola" value, instead of the empty string.
Can someone explain me what I am doing wrong and how should I set the default "Cola" value into the combo box ?
Only bind the SelectedItem property of the ComboBox to the SelectedItemOption source property and set the latter to the string "Cola" in the view model. This should work:
<ComboBox Grid.Row="0" Margin="10,0,0,0" Grid.Column="1"
SelectedItem="{Binding Path=JuiceOperations.SelectedItemOption}"
ItemsSource="{Binding Path=JuiceOperations.JuiceOptions}" />
public JuiceViewModel()
{
juiceOperations.SelectedItemOption = "Cola";
}
Don't mix SelectedItem, SelectedIndex and SelectedValue. You only need one.
mm8 above absolutely right, that should fix your issue.
On a side note, what you have there will work for a static selection list, but consider using an ObservableCollection<string> instead of a List<string>. The former implements INotifyCollectionChanged, which allows the view to be notified if there has been a change in the collection. When you bind an Observable Collection to the view, the view automatically subscribes to the CollectionChanged event. You will need this if you ever need to add or remove options at run time. Side note, OnCollectionChanged will not fire if you simply modify an item, for that you would still need to callOnPropertyChanged("JuiceOptions") in the setter.
something like this (with the appropriate private backing field):
public ObservableCollection<string> JuiceOptions
{
get
{
return this.juiceOptions;
}
set
{
this.juiceOptions = value;
this.OnPropertyChanged("JuiceOptions");
}
}
The value of juiceOperations.SelectedItemOption, that is, "Cola", is not the same "Cola" stored in the ItemsSource. You would need to do something like juiceOperations.SelectedItemOption = juiceOperations.JuiceOptions.First().
I need to create an Auto search control which will show the results as rows as this one http://www.devexpress.com/Products/NET/Controls/WPF/Editors/lookup-editor.xml. However, I dont need the graphics and checkboxes here. A simple listview like appearance will work.
Please suggest how to create the user control using WPF.
Here has a nice article on Sorting, Filtering and Grouping ListView.
Basically you set CollectionViewSource to ListCollectionView. You can then use the Filter Property to filter the ListView.
If you're using an MVVM approach you could do the following:
Bind your search Textbox Text member, ListView's ItemsSource and SelectedItem to the ViewModel
Set 'UpdateSourceTrigger=PropertyChanged' on the TextBox's binding
In the setter of the property that the TextBox is bound to add logic that searches the ItemsSource collection and sets the SelectedItem bound property.
Something like this:
XAML:
<TextBox Text="{Binding Path=SearchTerm, UpdateSourceTrigger=PropertyChanged}"/>
<ListView ItemsSource="{Binding Path=SourceCollection}" SelectedItem="{Binding Path=SelectedSearchItem, Mode=TwoWay}" />
Code:
public class ViewModel : INotifyPropertyChanged
{
public string SearchTerm
{
get { return searchTerm; }
set {
searchTerm = value;
SelectedSearchItem = SourceCollection.FirstOrDefault(foo => foo.Name.Contains(searchTerm));
}
}
public Foo SelectedSearchItem
{
get { return selecedSearchItem; }
set {
selectedSearchItem = value;
// Raise PropertyChanged
}
}
public ObservableCollection<Foo> SourceCollection { get; set;}
}
Being a new to WPF/XAML/MVVM, I've got a question.
In my View, I have 2 listboxes, which derive from ItemsControl.
On my viewmodel, I'd like to expose 2 ItemsControl properties such that I can bind my listbox to this view model property... this way I can implement a command that, from the view model, lets me move the currently selected item from ListBox1 to ListBox2.
Imagine all the really cool stuff not shown the following snippets:
view model code:
public int MyStuff1SelectedIndex { get{...} set{...} }
public int MyStuff2SelectedIndex { get{...} set{...} }
public ItemsControl MyStuffItemsControl1 { set; private get; }
public ItemsControl MyStuffItemsControl2 { set; private set; }
view XAML:
<ListBox Name="x:MyStuffListBox1" SelectedIndex="{Binding MyStuff1SelectedIndex}.... />
<ListBox Name="x:MyStuffListBox2" SelectedIndex="{Binding MyStuff2SelectedIndex}...../>
given that, I want my viewmodel to be able to have a command which could move items from one list box to another, w/ code such as the following:
public void MoveItemCommandExecute(...)
{
var sourceItem = MyStuff1ItemsControl.MagicGetItemExtensionMehod(MyStuff1SelectedIndex);
MyStuff1ItemsControl.MagicRemoveItemExtensionMethod(MyStuff1SelectedIndex);
MyStuff2ItemsControl.MagicAddItemExtensionMethod(sourceItem);
}
so, basically, what would the binding XAML look like ? I am trying to set a property on the view model from the view...
thanks!
You'll want to rethink this approach. Typically, you would bind your two listboxes ItemsSource properties to two ObservableCollection<T> properties on your view model, where T is the type of object in your list.
<ListBox x:Name="MyStuffListBox1" ItemsSource="{Binding MyList1}" SelectedItem="{Binding SelectedList1Item}" />
<ListBox x:Name="MyStuffListBox2" ItemsSource="{Binding MyList2}" SelectedItem="{Binding SelectedList2Item}" />
Note: I would use x:Name in your XAML, rather than the Name attribute.
public ObservableCollection<Thing> MyList1 { get; set; }
public ObservableCollection<Thing> MyList2 { get; set; }
// these properties should raise property changed events (INotifyPropertyChanged)
public Thing SelectedList1Item { get {...} set {...} }
public Thing SelectedList2Item { get {...} set {...} }
// constructor
public MyViewModel()
{
// instantiate and populate lists
this.MyList1 = new ObservableCollection(this.service.GetThings());
this.MyList2 = new ObservableCollection(this.service.GetThings());
}
You can then format what is displayed in the lists using DisplayMemberPath or defining an ItemTemplate on each list.
You can swap items between the lists by using the standard Collection methods on the ObservableCollection type - http://msdn.microsoft.com/en-us/library/ms668604.aspx
You shouldn't be implementing controls in your view model. That makes it a view, not a model of a view. The properties on your view model should be ObservableCollection<T>, and you should be binding the ItemsSource of items controls to those properties.
If you do this, your XAML might look like this:
<ListBox ItemsSource="{Binding List1}" SelectedItem="{Binding SelectedItem1, Mode=TwoWay}"/>
<ListBox ItemsSource="{Binding List2}" SelectedItem="{Binding SelectedItem2, Mode=TwoWay}"/>
<Button Command="{Binding MoveItemFromList1ToList2Command}">Move</Button>
List1 and List2 are of type ObservableCollection<T>, SelectedItem1 and SelectedItem2 are of type T (whatever type you've decided T should be), and MoveItemFromList1ToList2Command is a RoutedCommand that has these two handlers defined:
public bool CanMoveItemFromList1ToList2
{
{ get { return SelectedItem1 != null; }
}
public void MoveItemFromList1ToList2()
{
List2.Add(SelectedItem1);
List1.Remove(SelectedItem1);
}
I'd have to test this code to be sure, but I don't think you need to bother with property-change notification in the MoveItemFromList1ToList2 method; when you remove the item from List1, the ObservableCollection will notify the ListBox that the item's been removed, and the ListBox will set SelectedItem to null, updating SelectedItem1 in the view model. (And, of course, that will make the code break if you remove it from the first collection before adding it to the second.)
I don't know what I'm doing wrong here. I have a ListBox whose DataContext and ItemsSource are set, but there is nothing in the ListBox when I run my app. When debugging, the first line of my method for getting items for the ListBox never gets hit. Here's what I have:
// Constructor in UserControl
public TemplateList()
{
_templates = new Templates();
InitializeComponent();
DataContext = this;
}
// ItemsSource of ListBox
public List<Template> GetTemplates()
{
if (!tryReadTemplatesIfNecessary(ref _templates))
{
return new List<Template>
{
// Template with Name property set:
new Template("No saved templates", null)
};
}
return _templates.ToList();
}
Here's my XAML:
<ListBox ItemsSource="{Binding Path=GetTemplates}" Grid.Row="1" Grid.Column="1"
Width="400" Height="300" DisplayMemberPath="Name"
SelectedValuePath="Name"/>
On an instance of the Template class, there's a Name property that is just a string. All I want is to display a list of template names. The user won't change any data in a Template, the ListBox just needs to be read-only.
A Template also has a Data property that I will later display in this ListBox, so I don't want to make GetTemplates return just a list of strings--it needs to return some collection of Template objects.
You can't bind to a method. Make it a property and it should work.
Its better though to set the List as DataContext, or create a ViewModel that holds the list. Thay way, you will have more control over the instances your Listbox will bind to.
Hope this helps!
You're attempting to call a method in your binding when you should be using a property. Change it to a property and you should be good to go.
public List<Template> MyTemplates {get; private set;}
public TemplateList()
{
InitializeComponent();
SetTemplates();
DataContext = this;
}
// ItemsSource of ListBox
public void SetTemplates()
{
// do stuff to set up the MyTemplates proeprty
MyTemplates = something.ToList();
}
Xaml:
<ListBox ItemsSource="{Binding Path=MyTemplates}" Grid.Row="1" Grid.Column="1"
Width="400" Height="300" DisplayMemberPath="Name"
SelectedValuePath="Name"/>