I have a combobox with the following options: "HardDelete", "SoftDelete", "MoveToDeletedItems"
I want the default selection to match with the following property of an EmailAction object:
public DeleteMode DeleteMode { get; set; }
Here is the line of code I'm using to try to set this:
cmboDelMode.SelectedItem = emailActionInstance.DeleteMode.ToString();
Related XAML:
<ComboBox x:Name="cmboDelMode" HorizontalAlignment="Left" Margin="149,218,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem Content="HardDelete" HorizontalAlignment="Left" Width="118"/>
<ComboBoxItem Content="SoftDelete" HorizontalAlignment="Left" Width="118"/>
<ComboBoxItem Content="MoveToDeletedItems" HorizontalAlignment="Left" Width="118"/>
</ComboBox>
Currently the combobox is defaulting to being empty so is not working as expected. I am able to use "emailActionInstance.DeleteMode.ToString();" to view the data in a text box, so it seems I might just be setting the selected item incorrectly?
The issue is that setting ComboBox.SelectedItem doesn't work unless the ComboBox contains the item you are setting it to. In your case your ComboBox is filled with three ComboBoxItem objects which have their Content property set to a string. So ComboBox.SelectedItem is a ComboBoxItem. You are trying to set the ComboBox.SelectedItem to a string, which will not equal any of the objects contained in the ComboBox. Therefore, nothing happens.
I would suggest setting up Binding for your ComboBox like here:
<ComboBox ItemsSource="{Binding DeleteModes}" SelectedItem="{Binding SelectedDeleteMode}" />
And then create a ViewModel to bind to. If you bind an Enum to a ComboBox it will display correctly so you won't need to call DeleteMode.ToString():
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
DeleteModes = new ObservableCollection<DeleteMode>
{ DeleteMode.HardDelete, DeleteMode.SoftDelete,
DeleteMode.MoveToDeletedItems };
}
public event PropertyChangedEventHandler PropertyChanged;
DeleteMode _selected_delete_mode;
public DeleteMode SelectedDeleteMode {
get { return _selected_delete_mode; }
set {
_selected_delete_mode = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedDeleteMode"));
}
}
ObservableCollection<DeleteMode> _delete_modes;
public ObservableCollection<DeleteMode> DeleteModes {
get { return _delete_modes; }
set {
_delete_modes = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("DeleteModes"));
}
}
public void update_mode(DeleteMode mode) => SelectedDeleteMode = mode;
}
Related
What I'm trying to do
I'm trying to implement the AutoComplete feature on ComboBox without using WpfToolkit. If I'm not wrong, the ComboBox should support the AutoComplete for a simple string item so for example:
<ComboBox IsEditable="True">
<ComboBoxItem>Apple</ComboBoxItem>
<ComboBoxItem>Banana</ComboBoxItem>
<ComboBoxItem>Pear</ComboBoxItem>
<ComboBoxItem>Orange</ComboBoxItem>
</ComboBox>
Current object implementation
actually my ComboBox bound a custom object called CheckedListItem, this object have the following structure:
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked;
private T item;
public CheckedListItem() { }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
this class show as ComboBoxItem a CheckBox near the Text of the Item, similar to:
[] Item1
[] Item2
...
where [] is the Checkbox.
ComboBox structure
The ComboBox look like this:
<ComboBox IsEditable="True" ItemSource={Binding Countries}/>
where Countries is a List of CheckedListItem<Country>, the object country is implemented in this way:
public class Country
{
public string Name { get; set; }
}
the problem on this code is that when I type some text in the ComboBox this doesn't do anything, but should display on the items that contains the string typed.
What I tried so far
I tried to fix this by implementing a PreviewTextInput event, actually I managed in this way:
MyComboBox.IsDropDownOpen = true;
CountryMenuComboBox.ItemsSource = Countries.Where(c => c.Item.Name.Contains(e.Text)).ToList();
but this doesn't working correctly, cause if I type "England", the ItemSource display even all Items.
Any idea to fix this?
Thanks.
Try to set the TextSearch.TextPath property to "Item.Name":
<ComboBox IsEditable="True" ItemsSource="{Binding Countries}"
TextSearch.TextPath="Item.Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding Item.IsChecked}" />
<TextBlock Text="{Binding Item.Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have one ObservableCollection<M> fooBar {get;set;}. The class M.cs looks like this:
public class M{
private int _ID;
public int ID {
get {return this._ID;}
set {this._ID = value;}
}
private string _number;
public int Number {
get {return this._number;}
set {this._number = value;}
}
private string _power;
public int Power {
get {return this._power;}
set {this._power = value;}
}
/*
...
*/
}
Now I want to bind the names of these 3 propertys to a ComboBox. I don't want to do it like this:
<ComboBox>
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>Number</ComboBoxItem>
<ComboBoxItem>Power</ComboBoxItem>
</ComboBox>
Is there a more comfortable way?
Based on the choose of the first ComboBox I want to fill the second ComboBox. As example I choose in the first ComboBox the property Numberthen the second ComboBox should look like this
<ComboBox
SelectedValue="{Binding ???}"
ItemsSource="{Binding fooBar}"
SelectedValuePath="Number"
DisplayMemberPath="Number"
/>
Maybe someone of you can help me, because I have no idea how to connect both comboboxes.
This is how I would do it:
Make a property on the view model which exposes the properties on the model (class M) which can be selected. This way you explicitly control which properties can be selected.
Make a property to hold the selected value of each combo box.
DisplayMemberPath/SelectedValuePath in ComboBox2 binds to the SelectedValue of ComboBox1.
ViewModel:
// ComboBox 1
public Dictionary<string, string> SelectableProperties = new Dictionary<string, string>()
{
{ nameof (M.ID), "ID" }
{ nameof (M.Number), "Nummer" }
{ nameof (M.Power), "Potenz" }
}
// Selection in combobox 1 (not strictly necessary as it can be handled in view, but you may need to know what SelectedValue represents)
private string _selectedValueMember = String.Empty;
public string SelectedValueMember
{
get { return _selectedValueMember; }
set { _selectedValueMember = value; }
}
// Selection in combobox 2 (object just in case there could be other values than int)
private object _selectedValue = null;
public object SelectedValue
{
get { return _selectedValue; }
set { _selectedValue = value; }
}
public ObservableCollection<M> FooBar{ get; set; }
View:
<ComboBox x:Name="ComboBox1"
Width="100"
Margin="5"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedValue="{Binding SelectedValueMember}"
ItemsSource="{Binding SelectableProperties}">
</ComboBox>
<ComboBox Width="100"
Margin="5"
DisplayMemberPath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValuePath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValue="{Binding SelectedValue}"
ItemsSource="{Binding FooBar}">
</ComboBox>
For the 1st ComboBox: Use Reflection to get the names of all the properties of class M and put those into the ComboBox.
For the 2nd ComboBox: When selection changes in the 1st one, you get the property name. Now set the SelectedValue binding to the property that was selected in the 1st ComboBox.
In two combobox A and B.
A's ItemsSource is Custom list. and B's ItemsSource is UserControl list.
When manually setting the SelectedItem, A combobox works well, but B combobox UI do not show the selected Item. (In debugging, SelectedItem's value mapping is right, but the combobox B's UI do not be changed.)
All the other structure is same between A and B. What is the reason?
MainWindow.xaml
...
<ComboBox ItemsSource="{Binding FruitList}" SelectedItem="{Binding SelectedFruit}"
DisplayMemberPath="FruitName" />
<Button Content="Button" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" DisplayMemberPath="ItemName" />
<Button Content="Button" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Click="Button_Click2"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
FruitList.Add(f1);
FruitList.Add(f2);
FruitList.Add(f3);
UserControlList.Add(u1);
UserControlList.Add(u2);
UserControlList.Add(u3);
}
Fruit f1 = new Fruit { FruitName = "Apple" };
Fruit f2 = new Fruit { FruitName = "Banana" };
Fruit f3 = new Fruit { FruitName = "Lemon" };
MyUserControl u1 = new MyUserControl { ItemName = "Apple" };
MyUserControl u2 = new MyUserControl { ItemName = "Banana" };
MyUserControl u3 = new MyUserControl { ItemName = "Lemon" };
ObservableCollection<Fruit> _FruitList = new ObservableCollection<Fruit>();
public ObservableCollection<Fruit> FruitList
{
get { return _FruitList; }
set
{
_FruitList = value;
OnPropertyChanged();
}
}
Fruit _SelectedFruit;
public Fruit SelectedFruit
{
get { return _SelectedFruit; }
set
{
_SelectedFruit = value;
OnPropertyChanged();
}
}
ObservableCollection<MyUserControl> _UserControlList = new ObservableCollection<MyUserControl>();
public ObservableCollection<MyUserControl> UserControlList
{
get
{
return _UserControlList;
}
set
{
_UserControlList = value;
OnPropertyChanged();
}
}
MyUserControl _SelectedUserControl;
public MyUserControl SelectedUserControl
{
get { return _SelectedUserControl; }
set
{
_SelectedUserControl = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string caller = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(caller));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SelectedFruit = f3;
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
this.SelectedUserControl = u3;
}
}
public class Fruit
{
public string FruitName { get; set; }
}
}
UserControl
public partial class MyUserControl : UserControl
{
public MyUserControl()
{
InitializeComponent();
}
public string ItemName { get; set; }
}
This is not the good way of achieving this. Better define the ItemTemplate for the combobox to have the UserControl in it like:
<ComboBox ItemsSource="{Binding ItemList}" SelectedItem="{Binding SelectedItem}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<myControls:MyUserControl/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
and define the class Item
public class Item
{
public string ItemName { get; set; }
}
ObservableCollection<Item> _ItemsList = new ObservableCollection<Item>();
public ObservableCollection<Item> ItemsList
{
get
{
return _ItemsList ;
}
set
{
_ItemsList = value;
OnPropertyChanged();
}
}
Here DataContext of your UserControl will be Item object. you can bind the ItemName within you user control to show it in anyway you want.
in your user control you can have:
<TextBlock Text="{Binding ItemName}"></TextBlock>
Since you have asked "What is the reason?":
The reason why the second combo box does not show any selection is that ComboBox handles items of type ContentControl specially. In the read-only selection box, it is not the ContentControl that is used to display the value, but the content of the ContentControl. Since a UserControl is a ContentControl, the content of the UserControl is displayed inside the selection box, and therefore you have lost the data context of the UserControl; in the end, an empty string is displayed even though SelectedItem contains a reference to the UserControl that still has a valid data context. (As far as I know this behavior is undocumented; but you can see that it works like this by examining the ComboBox's code on http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/ComboBox.cs, especially the UpdateSelectionBoxItem() method).
By setting IsEditable="True" on the second ComboBox, you can see that everything works fine if the combo box has no read-only selection box.
Therefore, you generally should avoid adding UI elements to combo boxes, especially if you are using the DisplayMemberPath property, i.e. if you never want to actually display the UI element.
The recommended way to display ComboBox items with non-standard appearance (e.g. with UserControls) is described in the answer of #nit.
If you, however, insist on passing a UserControl item list to the ComboBox, you might remove DisplayMemberPath and use something like this:
<ComboBox ItemsSource="{Binding UserControlList}" SelectedItem="{Binding SelectedUserControl}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ItemName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Furthermore, in the constructor of your UserControl, you must place this line:
((FrameworkElement) Content).DataContext = this;
This is necessary to make sure that the correct data context is available in the read-only selection box, which only contains the content of the user control, not the user control itself.
Please note, that with the above example, the drop-down list contains text only (i.e. the item names), but the selection box will contain the fully rendered user control.
I have a combo box that is bound to a list of model objects. I've bound the combo box SelectedItem to a property that is the model type. All of my data binding works beautifully after the window has been loaded. The SelectedItem is set properly and I'm able to save the object directly with the repository.
The problem is when the window first loads I initialize the SelectedItem property and my combobox displays nothing. Before I moved to binding to objects I was binding to a list of strings and that worked just fine on initialization. I know I'm missing something but I can't figure it out.
Thanks in advance for any guidance you can provide.
(One note about the layout of this page. The combo boxes are actually part of another ItemTemplate that is used in a ListView. The ListView is bound to an observable collection in the main MV. Each item of this observable collection is itself a ModelView. It is that second ModelView that has the SelectedItem property.)
Here is my Model:
public class DistributionListModel : Notifier, IComparable
{
private string m_code;
private string m_description;
public string Code
{
get { return m_code; }
set { m_code = value; OnPropertyChanged("Code"); }
}
public string Name
{
get { return m_description; }
set { m_description = value; OnPropertyChanged("Name"); }
}
#region IComparable Members
public int CompareTo(object obj)
{
DistributionListModel compareObj = obj as DistributionListModel;
if (compareObj == null)
return 1;
return Code.CompareTo(compareObj.Code);
}
#endregion
}
Here the pertinent code in my ModelView:
public MailRoutingConfigurationViewModel(int agencyID)
: base()
{
m_agencyID = agencyID;
m_agencyName = DataManager.QueryEngine.GetAgencyName(agencyID);
IntializeValuesFromConfiguration(DataManager.MailQueryEngine.GetMailRoutingConfiguration(agencyID));
// reset modified flag
m_modified = false;
}
private void IntializeValuesFromConfiguration(RecordCheckMailRoutingConfiguration configuration)
{
SelectedDistributionList = ConfigurationRepository.Instance.GetDistributionListByCode(configuration.DistributionCode);
}
public DistributionListModel SelectedDistributionList
{
get { return m_selectedDistributionList; }
set
{
m_selectedDistributionList = value;
m_modified = true;
OnPropertyChanged("SelectedDistributionList");
}
}
And finally the pertinent XAML:
<UserControl.Resources>
<DataTemplate x:Key="DistributionListTemplate">
<Label Content="{Binding Path=Name}" />
</DataTemplate>
</UserControl.Resources>
<ComboBox
ItemsSource="{Binding Source={StaticResource DistributionCodeViewSource}, Mode=OneWay}"
ItemTemplate="{StaticResource DistributionListTemplate}"
SelectedItem="{Binding Path=SelectedDistributionList, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="False"
/>
#SRM, if I understand correctly your problem is binding your comboBox to a collection of objects rather than a collection of values types ( like string or int- although string is not value type).
I would suggest add a two more properties on your combobox
<ComboBox
ItemsSource="{Binding Source={StaticResource DistributionCodeViewSource},
Mode=OneWay}"
ItemTemplate="{StaticResource DistributionListTemplate}"
SelectedItem="{Binding Path=SelectedDistributionList, Mode=TwoWay}"
SelectedValuePath="Code"
SelectedValue="{Binding SelectedDistributionList.Code }"/>
I am assuming here that DistributionListModel objects are identified by their Code.
The two properties I added SelectedValuePath and SelectedValue help the combobox identify what properties to use to mark select the ComboBoxItem by the popup control inside the combobox.
SelectedValuePath is used by the ItemSource and SelectedValue by for the TextBox.
don't call your IntializeValuesFromConfiguration from the constructor, but after the load of the view.
A way to achieve that is to create a command in your viewmodel that run this method, and then call the command in the loaded event.
With MVVM light toolkit, you can use the EventToCommand behavior... don't know mvvm framework you are using but there would probably be something like this.
I got a sample mvvm app. The UI has a textbox, a button and a combobox. when I enter something in the textbox and hit the button, the text I enter gets added to an observablecollection. The Combobox is bound to that collection. How do I get the combobox to display the newly added string automaticly?
As I understand correctly, you want to add an item and select it.
Here is the example how it can be done using ViewModel and bindings.
Xaml:
<StackPanel>
<TextBox Text="{Binding ItemToAdd}"/>
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" />
<Button Content="Add" Click="Button_Click"/>
</StackPanel>
ViewModel:
public class MainViewModel:INotifyPropertyChanged
{
public ObservableCollection<string> Items { get; set; }
public string ItemToAdd { get; set; }
private string selectedItem;
public string SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
public void AddNewItem()
{
this.Items.Add(this.ItemToAdd);
this.SelectedItem = this.ItemToAdd;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The MainViewModel has 3 properties (one for the TextBox and two other for the ComboBox) and the method AddNewItem without parameters.
The method can be triggered from a command, but there is no standard class for commands, so I will call it from the code-behind:
((MainViewModel)this.DataContext).AddNewItem();
So you must explicitly set an added item as selected after you add it to a collection.
Because the method OnItemsChanged of the ComboBox class is protected and can't be used.
If the ComboBox is bound to an ObservableCollection, the ComboBox will be updated as soon as the collection is changed.
That's the advantage of using an ObservableCollection - you don't need to do any extra coding to update the UI.
If this is not the behavior you're seeing, perhaps you can post some code/xaml.