The Scenario
I have a UserControl which contains a couple of controls, one of which is a ComboBox. The UserControl is conatined with a Page.
The UserControl exposes a couple of dependency properties to allow the Items and SelectedValue of the ComboBox to be accessed. These are defined as follows:
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(int?), typeof(CustomComboBox), new FrameworkPropertyMetadata((int?)null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int? SelectedValue
{
get { return (int?)this.GetValue(SelectedValueProperty); }
set { this.SetValue(SelectedValueProperty, value); }
}
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(List<ComboItem>), typeof(CustomComboBox), new FrameworkPropertyMetadata((List<ComboItem>)new List<ComboItem>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public List<ComboItem> Items
{
get { return (List<ComboItem>)this.GetValue(ItemsProperty); }
set { this.SetValue(ItemsProperty, value); }
}
These are binded to the ComboBox control as follows:
<ComboBox ItemsSource="{Binding Path=Items, ElementName=ThisControl}" DisplayMemberPath="Text" SelectedValuePath="ID" SelectedValue="{Binding Path=SelectedValue, ElementName=ThisControl, UpdateSourceTrigger=PropertyChanged}" />
Where the UserControl is named "ThisControl".
Also, in the above, ComboItem is a class which contains the definitions for each item, namely two properties called ID and Text. The idea here is that ID is the key that is stored in the database, and Text is the user friendly value that is displayed in the list.
When the Page containing the UserControl is created (using the Constructor), the ComboBox items list is populated with the following code:
MyComboBox.Items.Clear();
MyComboBox.Items.Add(new ComboItem() { ID = 1, Text = "One" });
MyComboBox.Items.Add(new ComboItem() { ID = 2, Text = "Two" });
Finally, the SelectedValue is binded to a class property via XML like so:
<c:CustomComboBox x:Name="MyComboBox" SelectedValue="{Binding Path=MyClassInstance.ItemID}" />
where the property ItemID is of type int. It is important to note that the class instance is set after the combo box items are populated.
The Problem
When the page is first shown, the initial selected value is not set correctly - it is an empty item selection. I can assure that the ItemID property is already set to a valid value (i.e. 1).
When an item is selected from the ComboBox, the binding does successfully set the ItemID property, and is correctly persisted to the database upon a 'save' function.
One strange behavior, which may help to identity the problem, is that if the window containing the Page is closed, and then re-opened. The ComboBox does get th correct initial value - and that is regardless of the ComboBox being set on first attempt, or otherwise.
So, the question is: Why is the initial value not shown the first time the page is displayed?
OK, I was doing some refactoring to improve how I populate the ComboBox Items and I seem to have stumbled across the solution...
The problem seems to be related to the following piece of code that populates the list items:
MyComboBox.Items.Clear();
MyComboBox.Items.Add(new ComboItem() { ID = 1, Text = "One" });
MyComboBox.Items.Add(new ComboItem() { ID = 2, Text = "Two" });
The solution is to directly set the Items property, rather than to clear it and populate it:
MyComboBox.Items = GetMyList();
Where GetMyList() is returning a List<ComboItem> type.
Related
I have the following observable collection called SalesOrderCollection
private static ObservableCollection<SalesOrder> _salesOrderCollection = new ObservableCollection<SalesOrder>();
public static ObservableCollection<SalesOrder> SalesOrderCollection
{
get { return _salesOrderCollection; }
set
{
_salesOrderCollection = value;
}
}
I am assigning values like this:
SalesOrder order = new SalesOrder();
order.ItemSubTotal = 234;
order.TotalTaxes = 12;
order.TOTAL = 12345;
SalesOrderCollection.Add(order);
In the view, I am setting it like this to the label:
<Label Content="{Binding Path=TOTAL, UpdateSourceTrigger =PropertyChanged}"/>
However the label is empty. Any suggestions as to what I am doing wrong here ?
First of all, you are binding incorrectly - the label should not be bound to a specific item in a collection. Instead you should use a list control to show the contents of the ObesrvableCollection, then bind the selected item in the list control to a property on the viewmodel. Then the label should also be bound to the same property which contains the selected object.
If you insist on doing indexed binding to a specific item in the ObservableCollection then this syntax should do it for you:
<Label Content="{Binding Path=SalesOrderCollection[0].TOTAL}"/>
Here is an example.
Just another pointer: I'm not sure why you've made your SalesOrderCollection property static - this looks like the start of some potentially smelly code.
I build a DataGrid the contains a custom ComboBoxColumn that shows and selects the proper fields when I select it with a mouse, but resets to the first item in the list after the ComboBox loses focus. Additionally, when when I import data from an Excel spreadsheet, the ComboBox does not change to match the value in the worksheet.
Note: I'm planning on reusing this class to create a series of ComboBoxes for different data.
What is preventing the Combo Box from accepting a new value?
ComboList class
namespace x.Models
{
public class ComboList
{
public int FieldID {get;set;}
public string FieldString {get;set;}
}
}
XAML Combo Box code
<DataGridComboBoxColumn Header="Platform Type" x:Name="PlatformTable" SelectedValueBinding="{Binding FieldID}" DisplayMemberPath="FieldString" SelectedValuePath="1" />
Code(behind)
public partial class MainWindow: Window
{
public ObservableCollection<Models.ComboList> PlatformCombo {get;set;}
public MainWindow()
{
PlatformCombo = new ObservableCollection<Models.ComboList>()
{
new Models.ComboList() {FieldID=1,FieldString="Mounted"},
new Models.ComboList() {FieldID=2,FieldString="Dismounted"}
};
PlatformTable.ItemsSource = PlatformCombo;
}
Sample Excel data
Platform Type
1
2
Changing SelectedValueBinding to SelectedItemBinding fixes one part of the problem on the field not accepting a new value. [link] (DataGridComboBoxColumn not updating model WPF)
I'm still left with the question as to why when importing from Excel, the combo box is never updated; left empty for each row, even though there is appropriate data for the combo box.
The XAML Combo Box SelectedValueBinding must be set to the DataGrid Combo Box Control Name instead of the generic Combo Box FieldID and the UpdateSourceTrigger needs to be set to LostFocus.
SelectedValueBinding="{Binding Platform_Type, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
My ComboBoxItems have string values that represent the name of the item to be selected, but I've stored the value for that item in Tag property of the ComboBoxItem. I use the Tag as the asp.net equivalent to the dropdownlist Item Value.
In the code behind where I set the datagrid template column, I set the ComboBoxItem as follows:
<ComboBoxItem Tag='" + product.ProductGuid + "' Content='" + product.Name + "'></ComboBoxItem>
I need to programmatically select a ComboBoxItem based on the Tag value not the content. In the code below, currentProduct holds the ProductGuid value that I need to select, but this code will select the ComboBoxItem whose Content is my currentProduct
((ComboBox)QuotationDG.Columns[0].GetCellContent(MyData[MyData.Count - 1])).SelectedValue = currentProduct;
Is there a way to set the ComboBox Selected value to the ComboBoxItem whose Tag value is currentProduct?
EDIT:
Here's the code I use to bind my ComboBox Column:
private string CreateDDLColumnEditTemplate(int index, string propertyName, List<Product> ProductList)
{
StringBuilder CellTemp = new StringBuilder();
CellTemp.Append("<DataTemplate ");
CellTemp.Append("xmlns='http://schemas.microsoft.com/winfx/");
CellTemp.Append("2006/xaml/presentation' ");
CellTemp.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>");
CellTemp.Append(String.Format("<ComboBox SelectedValue='{{Binding [{0}], Mode=TwoWay}}'>",0));
foreach (Product product in ProductList)
{
CellTemp.Append("<ComboBoxItem Tag='"+product.ProductGuid+"' Content='" + product.Name + "'></ComboBoxItem>");
}
CellTemp.Append(String.Format("</ComboBox>"));
CellTemp.Append("</DataTemplate>");
return CellTemp.ToString();
}
What you need is
YourComboBox.SelectedValue = currentProduct
and
<ComboBox SelectedValuePath="Tag" etc. etc. etc. />
which means your SelectedValue gets its value from your Tag
The use of the combobox has the ability bind to objects and specify what is to be displayed in the dropdown. Why not use the Product object which you are loading into the tag but show something different on the drop down?
If need be create an extended, Partial object, to display something wholly different in the drop down, but still access the Product item to change the selection.
The suggestion here is to simply bind the combobox to the Product data item. Then you can change the value dynamically without using the Tag property.
For example I have this data type similar to your Product type:
public class PriceItem
{
public string Name { get; set; }
public int Price { get; set; }
}
and here is the data list of values which is saved on my VM as Items.
Items = new List<PriceItem>()
{
new PriceItem() { Name = "Alpha", Price=100 },
new PriceItem() { Name = "Beta", Price=200 },
};
Scenario
Two combo boxes, both bound to the Items data. One combo box shows the Name in its dropdown while the other shows the Price. The Name combobox controls the price combo box, when it changes the price changes as well.
<ComboBox x:Name="comboName" ItemsSource="{Binding Items}" DisplayMemberPath="Name" />
<ComboBox x:Name="comboValue"
ItemsSource="{Binding Items}"
DisplayMemberPath="Price"
SelectedValuePath="Price"
SelectedValue="{Binding SelectedItem.Price, ElementName=comboName, Mode=OneWay}" />
In action both combo boxes are blank. But whenever I select the first one it changes the second. All using the same data/data objects.
Is it possible to insert values from combobox to specific column in database?
For example if i have combobox with items: item1, item2, item3 and I want to bind them to some column: column1, what I want to manage is this: if item1 is selected, when I click on button, I want to insert value of that item in the column1, otherwise if selected item is item2 then I want value of item2 to be inserted in column1, etc...
Now I know that the question is not really well written, but I just want to know if this is possible to do.
I've been Googling for this type of problem but I couldn't find the solution. I know how to make column records to be inserted into combobox items list but don't know the way to do the opposite.
Also would like to say that I have this problem in my WPF/WCF/MVVM application, so I would like to know if this is possible (and how) to solve it that way.
This solution is based on MVVM pattern .
Bind the Selected item of combo box control to some property in View model.
So your view should look like
<ComboBox ItemsSource="{Binding SomeItems,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}" SelectedValue="{Binding SelectedItemBinding,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}" />
<Button Command={Binding ButtonClickCommand} ..../>
so once the Button is clicked you will get the RelayCommand handle in viewmodel and you can have a logic there to Get the selected item and use the value to insert into the column. Your view model should look like ,
public class ViewModel : //implement notify property changed and ICommand
{
public RelayCommand ButtonClickCommand
{
get new RelayCommand(EventHandlerToBeCalled);
}
public string SelectedItemBinding
{
get;
set
{
//notify property changed.
}
}
//method called when button is clicked.
private void EventHandlerToBeCalled()
{
//here set the SelectedItemBinding to a column.
}
}
ofc you can. On the button click event you can just get the selected value of the combobox and save it.
var selectedItem = ComboBoxName.SelectedItem;
But if you have binded the combobox with objects then you can cast it.
var selectedItem = ComboBoxName.SelectedItem as (objecttypehere)
Update
I missed that you used MVVM. Then in the view you can use bind the combobox with selected item.
<ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding TheSelectedItem}">
//Itemtemplates.
</ComboBox>
In the viewModel just acces the property you binded with the selecteditem in my test case "TheSelectedItem" property.
Save it!
in combobox you can store values with prefix (col1_12, col2_24 etc)
on button click you should parse value: separate it on prefix and original value
so you can write value to needed column:
switch (prefix) {
case "col1" : table.column1 = value; break;
case "col2" : table.column2 = value; break;
// etc
}
I am having a combobox and the data is assigned dynamically as follows
<combobox
name="clientbox"
itemsource={Binding},
displaymemberpath="ClientName"
selectedvaluepath="clientid" />
I am loading the client details from DB and setting them to a listbox and assigning to the combobox as follows.
clientbox.DataContext = <list>
I am able to see the data in the combox after run. This will select the 0th item, but I want to default select different item. How to do this?
clientbox.SelectedItem = ((ComboBoxItem)clientbox.Items[1]);
clientbox.Text = ((ComboBoxItem)clientbox.Items[1]).Content.ToString();
There are several possibilities:
Code-behind:
// Setting the 0-based index
clientBox.SelectedIndex = 1;
// Setting the item
clientBox.SelectedItem = activeClient;
// Setting the value (explanation follows..)
clientBox.SelectedValue = activeClientValue
Using the SelectedValue property you can define a property of the item which is used to fill the ComboBox. An example: You fill the ComboBox with items of a class Client which has the properties Id, Name and so on. If you select an item, the SelectedItem property will be an instance of the class Client. By setting the SelectedValuePath property of the ComboBox to Id the SelectedValue will always just contain the id of the selected client.
Binding:
Of course you can always use bindings.
<ComboBox x:Name="clientBox"
ItemsSource={Binding ClientList}, DisplayMemberPath = "Name"
SelectedValuePath="Id"
SelectedValue={Binding ActiveClient} />