You set the DisplayMemberPath and the SelectedValuePath to "Name", so I assume that you have a class PhoneBookEntry with a public property Name.
Have you set the DataContext to your ConnectionViewModel object?
I copied you code and made some minor modifications, and it seems to work fine.
I can set the viewmodels PhoneBookEnty property and the selected item in the combobox changes, and I can change the selected item in the combobox and the view models PhoneBookEntry property is set correctly.
Here is my XAML content:
<Window x:Class="WpfApplication6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<Button Click="Button_Click">asdf</Button>
<ComboBox ItemsSource="{Binding Path=PhonebookEntries}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
SelectedValue="{Binding Path=PhonebookEntry}" />
</StackPanel>
</Grid>
</Window>
And here is my code-behind:
namespace WpfApplication6
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ConnectionViewModel vm = new ConnectionViewModel();
DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
((ConnectionViewModel)DataContext).PhonebookEntry = "test";
}
}
public class PhoneBookEntry
{
public string Name { get; set; }
public PhoneBookEntry(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}
public class ConnectionViewModel : INotifyPropertyChanged
{
public ConnectionViewModel()
{
IList<PhoneBookEntry> list = new List<PhoneBookEntry>();
list.Add(new PhoneBookEntry("test"));
list.Add(new PhoneBookEntry("test2"));
_phonebookEntries = new CollectionView(list);
}
private readonly CollectionView _phonebookEntries;
private string _phonebookEntry;
public CollectionView PhonebookEntries
{
get { return _phonebookEntries; }
}
public string PhonebookEntry
{
get { return _phonebookEntry; }
set
{
if (_phonebookEntry == value) return;
_phonebookEntry = value;
OnPropertyChanged("PhonebookEntry");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Edit: Geoffs second example does not seem to work, which seems a bit odd to me. If I change the PhonebookEntries property on the ConnectionViewModel to be of type ReadOnlyCollection, the TwoWay binding of the SelectedValue property on the combobox works fine.
Maybe there is an issue with the CollectionView? I noticed a warning in the output console:
System.Windows.Data Warning: 50 : Using CollectionView directly is not fully supported. The basic features work, although with some inefficiencies, but advanced features may encounter known bugs. Consider using a derived class to avoid these problems.
Edit2 (.NET 4.5): The content of the DropDownList can be based on ToString() and not of DisplayMemberPath, while DisplayMemberPath specifies the member for the selected and displayed item only.
An easier way to do all this (admittedly not with the labels you specified) is to just use ActionScript's built in ObjectUtil.toString method.
You would write something like this:
import mx.utils.ObjectUtil;
public function dumpObj():void {
myTextField.text = ObjectUtil.toString(obj);
}
This should pretty much print out every property of every multiple / nested object you have.
HOWEVER - you should make a fundamental change to your component if you want it to be reusable. You need a getter/setter for your collection. In the component, add this code:
[Bindable]
private var _myCollection:ArrayCollection;
public function set myCollection (data:ArrayCollection) : void {
_myCollection = data;
}
public function get myCollection () : ArrayCollection {
return _myCollection;
}
There are several other ways to do this - look it up if you need something different.
In your datagrid, use the private ArrayCollection variable like this:
<mx:DataGrid id="compras" x="0" y="0" width="556" dataProvider="{_myCollection}" editable="false">
<mx:columns>
<mx:DataGridColumn headerText="ID" dataField="tinteiroid" visible="false"/>
...
In the main application, you can populate your component like this:
<kgtm:myComponent x="0" y="20" myCollection="{queryDataAC}"
And you name your ArrayCollection like this:
<mx:ArrayCollection id="queryDataAC">
in your top level Application code, you define the kgtm namespace, so you can use your custom component, like so:
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:kgtm="com.kgtm.*"
Then put your component in the folder corresponding to this namespace definition.
This all leads to the final object print utility - which you define on the component, as it is the only thing that should know about how to print out it's data.
Define a public function, and get it to print out the private ArrayCollection data, using ObjectUtil or your own method.
public var getLastQueryOutput () : String {
private var output:String = "";
private var len:int = _myCollection.length;
for (var i:int = 0; i <len; i++) {
output = output +
"TinteiroID:"+_myCollection[i].tinteiroid+
"#TinteiroLABEL:"+_myCollection[i].label+
"#TinteiroREF:"+_myCollection[i].ref+
"#TinteiroMARCA:"+_myCollection[i].marca+
"#TinteiroGENERO:"+_myCollection[i].genero+
"#TinteiroQUANTIDADE:"+_myCollection[i].quantidade+
"#FIMPROD#";
}
trace(output);
}
Hopefully this will help. If you name the object correctly as you are putting it into the ArrayCollection, you can again just use ObjectUtil as I stated at the top.
Casp - Check out more of my (and my colleagues) blog entries here
Best Answer
You probably just need to call
invalidateProperties()
,invalidateDisplayList()
,invalidateSize()
, or some combination of the three (I'm something of a flex newbie myself), to force an update to the component's measurements after changing the data provider or its contents.