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
Related
I have a datagrid populated with "notes" and when a note is clicked I want the richtextbox to show the note.comments. But the Bindings isn't working.
public NoteDTO SelectedNote {get; set;}
public string stringNotes {get; set;}
public void OpenNote()
{
stringNotes = SelectedNote.Comments;
}
<DataGrid x:Name="NoteGrid" cal:Message.Attach="[Event MouseDoubleClick] = [Action OpenNote()]" ItemsSource="{Binding Notes}" SelectedItem="{Binding SelectedNote}"
<toolkit:RichTextBox Text="{Binding stringNotes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
If I may get help please.
The main problem is that you're binding to a property that has no concept of change notifications; you're not implementing INotifyPropertyChanged. That being said, why not just bind the RichTextBox directly to the property off of NoteDTO:
<toolkit:RichTextBox Text="{Binding SelectedNote.Comments, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The other option is to manually copy the comments between SelectedNote and stringNotes, then implement INotifyPropertyChanged, but this isn't ideal unless you want to have an intermediate property before propagating them to the NoteDTO object.
EDIT:
I noticed that your SelectedNote property will never notify the UI that it has changed, which will prevent bindings from working. Try something like the following:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string selectedNote;
public string SelectedNote
{
get { return this.selectedNote; }
set
{
if (this.selectedNote == value)
return;
this.selectedNote = value;
this.OnPropertyChanged("SelectedNote");
}
}
}
I have an ObservableCollection that gets it's data from a DataTable that is populate from a Postgres Database. I need to bind this ObservableCollection to a ComboBoxColumn in a DataGrid. I have seen quite a lot of examples on how to do this, yet I'm constantly missing something.
Edit: This is the new updated code and it is working except for the INotifyPropertyChanged that I have set only to "name" (yet)
namespace Country_namespace
{
public class CountryList : ObservableCollection<CountryName>
{
public CountryList():base()
{
// Make the DataTables and fill them
foreach(DataRow row in country.Rows)
{
Add(new CountryName((string)row.ItemArray[1], (int)row.ItemArray[0]));
}
}
}
public class CountryName: INotifyPropertyChanged
{
private string name;
private int id_country;
public event PropertyChangedEventHandler PropertyChanged;
public CountryName(string country_name, int id)
{
this.name = country_name;
this.id_country = id;
}
public string Name
{
get { return name; }
set {
name = value;
OnPropertyChanged("CountryName");
}
}
public int idcountry
{
get { return id_country; }
set { id_country = value; }
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
XAML:
xmlns:c="clr-namespace:Country_namespace"
<Windows.Resources>
<c:CountryList x:Key="CountryListData"/>
</Windows.Resources>
DataGrid Column:
<dg:DataGridTemplateColumn Header="country">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource CountryListData}}" DisplayMemberPath="Name"></ComboBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
first of all. you can just bind to public properties.
country_ seems no public property.
second if binding not work you always have to check datacontext first and binding path second. you can use Snoop to do this at runtime
EDIT:
you did not post your itemssource for your grid. so some assumptions here.
<DataGrid ItemsSource="{Binding MySource}">
...
<ComboBox ItemsSource="{Binding MySourcePropertyForCountries}"/>
--> this would work when your MySource object item has a public property MySourcePropertyForCountries.
but if your want to bind your combobox to a list wich is outside the MySource object. then you have to use some kind relativeSourcebinding or elementbinding.
<DataGrid x:Name="grd" ItemsSource="{Binding MySource}">
...
<ComboBox ItemsSource="{Binding ElementName=grd, Path=DataContext.MyCountries}"/>
--> this would work when the datacontext of the datagrid has a property MyCountries
I cant figure this one out. If I just have the combo-box by itself not embedded in a list box it will populate and selected the values I need.
The XAML
<ComboBox Name="comboBox1"
Height="23"
DataContext="{Binding Combox}"
ItemsSource="{Binding Comboxes}"
DisplayMemberPath="PV"
SelectedValuePath="PK"
SelectedItem="{Binding SelectedItem}"
VerticalAlignment="Top"
Width="120" />
The Code Behind
public MainWindow()
{
InitializeComponent();
DataAttribute d = new DataAttribute(2, "blue");
Combox c = new Combox();
c.SelectedItem = d;
c.Comboxes.Add(new DataAttribute(1, "red"));
c.Comboxes.Add(new DataAttribute(3, "Black"));
c.Comboxes.Add(c.SelectedItem);
comboBox1.DataContext = c;
}
Class for holding data
public class Combox: INotifyPropertyChanged
{
public Combox()
{
Comboxes = new List<DataAttribute>();
}
private DataAttribute _selectedItem;// = new DataAttribute(-1, "NA");
public List<DataAttribute> Comboxes { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DataAttribute SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem == value) return;
_selectedItem = value;
OnPropertyChanged("SelectedValue");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DataAttribute
{
public DataAttribute() { }
public DataAttribute(int pk, string pv)
{
PK = pk;
PV = pv;
}
public int PK { get; set; }
public string PV { get; set; }
public override string ToString()
{
return PV;
}
}
All that works fine, but as soon as I try to create a list of com boxes in the listbox nothing. I can see the combo but no data. How on earth to you bind to it is XAML?
public MainWindow()
{
InitializeComponent();
List<Combox> com = new List<Combox>();
DataAttribute d = new DataAttribute(2, "blue");
Combox c = new Combox();
c.SelectedItem = d;
c.Comboxes.Add(new DataAttribute(1, "red"));
c.Comboxes.Add(new DataAttribute(3, "Black"));
c.Comboxes.Add(c.SelectedItem);
com.Add(c);
lstTest.ItemSource = com;
}
As here is the XAML with a listbox. It no longer binds...
<ListBox Name="lstTest" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<ComboBox Name="comboBox1"
DataContext="{Binding Combox}"
ItemsSource="{Binding Comboxes}"
DisplayMemberPath="PV"
SelectedValuePath="PK"
SelectedItem="{Binding SelectedItem}"
VerticalAlignment="Top"
Width="120" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am stumped as it was hard enough trying to figure out how to just get the selected object to appear without the list-box...
Inside the items of an ItemsControl the DataContext is the currectly templated item, if you want to get to a list that is to be used as the ItemsSource for the ComboBoxes you normally need to modify the bindings to use another source, e.g. RelativeSource or ElementName. (This is the case for one list that is to be used for all ComboBoxes)
In this case, where the list seems to be part of the item, you should only need to remove the binding on the DataContext as the DataContext is already the item (an instance of Combox).
I am trying to update a textblock on the view by databinding to a property in the viewmodel (the datacontext for the view).
In the code below; when SelectedItem changes, I want the textblock text to update with the value of the Name property on SelectedItem.
In an attempt to achieve this I have set the binding source to the property that is changing and the binding path to the data I want to update the textblock with.
I.e. I am expecting that the binding engine will see a change on the binding Source (SelectedItem) and pull the data from the binding Path (SelectedItem.Name).
http://msdn.microsoft.com/en-us/library/ms746695.aspx
Setting the SelectedItem raises INPC but the text does not update.
public class ViewModel
{
public IConfiguration Configuration { get; set;}
}
public class Configuration : IConfiguration, INotifyPropertyChanged
{
public Item SelectedItem
{
get { return _item;}
set
{
_item = value;
ItemName = _item.Name;
RaisePropertyChangedEvent("SelectedItem");
}
}
public string ItemName
{
get { return _itemName;}
set
{
_itemName= value;
RaisePropertyChangedEvent("ItemName");
}
}
}
public class Item
{
public string Name { get; set;}
}
I know that changes on Configuration are seen because this works:
<TextBlock Text="{Binding Configuration.ItemName}"/>
But this does not:
<TextBlock Text="{Binding Path=Name, Source=Configuration.SelectedItem}"/>
And nor does this:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name, Source=Configuration.SelectedItem}"/>
I'm assuming that this should be straightforward - what have I missed?
I've never actually seen anyone use Binding.Source before, so I don't know much about it. But my guess is that it's not dynamic. When you create your binding, it's grabbing a reference to the object specified in your Source, and then that's it: it uses that same reference for the lifetime of the binding.
Why make this complicated? Just use Path. That's the normal way of doing binding, and it's dynamic all the way -- what you're doing is exactly what Path is intended for.
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}"/>
This is probably working, you just can not see it. The Binding engine has not been notified that the Name property of the Item object has changed.
Try implementing the INotifyPropertyChanged interface on the Item class as well (raising the PropertyChanged event as necessary)
This will work for your third binding situation, and also for a similar definition as below
<TextBlock DataContext="{Binding Path=Configuration.SelectedItem}" Text="{Binding Path=Name}"/>
But for a simpler fix, this should work:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}" />
Edit:
public class Configuration : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
private Item _SelectedItem = null;
public Item SelectedItem
{
get
{
return _SelectedItem;
}
set
{
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
public class Item
{
public string Name { get; set; }
}
Then in a Command Execute somewhere I have this:
Configuration.SelectedItem = new Item() { Name = "test" };
Which updates the TextBlock in the View fine:
<TextBlock Text="{Binding Path=Configuration.SelectedItem.Name}" />
I'm trying to display the data from two sql ce 3.5 sp1 database tables linked with foreign key - Customers and Orders. When the customer is selected in a datadrig, I want the other grid to be populated with the Orders.
I'm using a query:
var profiles = from c in db.Customers.Include("Orders")
select c;
And in my ViewModel:
private Models.Customers _selecteditem;
public Models.Customers SelectedItem
{
get { return _selecteditem; }
}
the view looks like this:
<Grid>
<toolkit:DataGrid x:Name="dg1" ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedItem, mode=TwoWay}">
</toolkit:DataGrid>
<toolkit:DataGrid x:Name="dg2" ItemsSource="{Binding Path=SelectedItem.Orders}">
</toolkit:DataGrid>
</Grid>
The error I'm getting is:
Warning 1 Field 'Clients.ViewModels.CustomerViewModel._selecteditem' is never assigned to, and will always have its default value null
How to make it work correctly? When I just want to display Customers it is ok.
Thanks for any suggestions.
You need a setter for SelectedItem
private Models.Customers _selecteditem;
public Models.Customers SelectedItem
{
get { return _selecteditem; }
set { _selectedItem = value; }
}
Also, since you are using it in a binding you'll want the ViewModel to implement INotifyPropertyChanged so it'll actually be:
private Models.Customers _selecteditem;
public Models.Customers SelectedItem
{
get { return _selecteditem; }
set
{
if (_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
If Martin's answer doesn't help, have a look at the DataGrid.SelectionUnit and make sure it is set to "FullRow" not to "CellOrRowHeader" like I had it.
If you have it set to "CellOrRowHeader", the first click on a cell will set the SelectedItem to null.
I thought I'd add this in case someone else had the same annoying issue.