How to bind an ObservableCollection to a combobox in WPF? - wpf

I'm stuck with this and even after reading a lot of topics I can find the answer.
Here my attempt to bind an observable Collection to a combobox in WPF using MVVM Pattern:
Scenario.cs
{
public class Scenario
{
public string name { get; set; }
public string codeClient { get; set; }
public string codeAppli { get; set; }
public string infoComplementaire { get; set; }
}
}
scenarioVM.cs
{
public ObservableCollection<Scenario> Scenarios { get; set; }
}
MainWindows.xaml
<ComboBox x:Name="cbScenario" ItemsSource="{Binding Scenarios}" DisplayMemberPath="{Binding Path=Name}" HorizontalAlignment="Left" Margin="407,8,0,0" VerticalAlignment="Top" Width="226" BorderBrush="#FF1585B5" Height="26"/>
Thanx for the help :)

If you set your DataContext to this, ofc you can't find Scenarios if its part of your ScenarioVM and not your Window. DataContext is the root of any Binding.
For a start you could do this.
public ScenarioVM VM {get; private set;}
public MainWindow()
{
VM = new ScenarioVM();
InitializeComponent();
DataContext = VM;
this.Loaded += MetroWindow_Loaded;
VM.Scenarios.Add(new Scenario());
}
Now your window owns an instance of the ScenarioVM. Not necessarily a good design, but a start.
Also sooner than later you run into Trouble that your Scenario is not deriving from INotifyPropertyChanged so you might want to address that as well.

Related

Datagridcomboboxcolumn bind to ObservableCollection in item of Datagrid source

I have datagrid with two columns: text and combobox. And combobox should have binding to observable collection.
This is pseudocode for datagrid items source:
public class ModeObjectState
{
public int ID { get; set; }
public int ObjectTypeID { get; set; }
public string State { get; set; }
}
public class ModeObject
{
public string Name { get; set; }
public int objID { get; set; }
public int Type { get; set; }
public int StateID { get; set; }
public bool Format { get; set; }
}
public class _dataContext
{
public ObservableCollection<ModeObjectState> ListObjectState { get; set; }
public ModeObject ModeObj { get; set; }
}
ObservableCollection<_dataContext> SourceObjList
objTable.ItemsSource = SourceObjList;
This is xaml code for datagrid:
<DataGrid x:Name="objTable" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn x:Name="ColumnName" Binding="{Binding Path=ModeObj.Name}" IsReadOnly="True" />
<DataGridComboBoxColumn x:Name="ColumnState" ItemsSource="{Binding ListObjectState}" DisplayMemberPath="State" SelectedValuePath="ID" SelectedValueBinding="{Binding Path=ModeObj.StateID}" />
</DataGrid.Columns>
</DataGrid>
But datagrid doesn't show any items in comboboxcolumn. Please, help me with binding the datagridcombobox to observable collection "ListObjectState" in "_dataContext" class.
Thanks!
Implement with INotifyPropertyChanged for the _dataContext
public class _dataContext : INotifyPropertyChanged
{
private ObservableCollection<ModeObjectState> _listObjectState;
public ObservableCollection<ModeObjectState> ListObjectState
{
get { return _listObjectState; }
set
{
_listObjectState = value;
OnPropertyChagned("ListObjectState");
}
}
public ModeObject ModeObj { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChagned(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Since the 1st set may done after the Binding, so it wont affect the UI..
Its difficult to figure out without looking at the whole code. You have binding issue, and it would be easier to find where the issue is using XAML debugging tools like Snoop or WPF Inspector. You just need to attach your running application to see the Datacontext.
You can easily find if the datacontext is valid or not.
WPF Inspector has a better User interface, but its prone to crash. Press Ctrl+Shift and hover mouse over your control to see it getting reflected in Snoop/WPF Inspected.
Also see your Output window for whats the binding error you are getting.

WPF binding static list to combobox

Trying to understand how to bind this static list to a combobox that located on different window.
public partial class MainWindow : Window
{
public static List<Classes.Entity> EntityList { get; set; }
public MainWindow()
{
EntityList = new List<Classes.Entity>();
InitializeComponent();
}
...
the object:
public class Entity
{
public string entityName { get; set; }
...
XAML (In a diffrent window, call "NewRelationship.xaml.cs"
<ComboBox x:Name="cb_from" ItemsSource="{Binding Path=EntityList}" DisplayMemberPath="entityName" SelectedValue="{Binding Path=Entity}" />
Of course I fill the list later in the code...
if I moving the list to the newRelationship window and add "this.datacontext = this;" its working,
How do I make this work when the list is in the mainWindow? Thanks...
A better approach would be to keep the EntityList in a separate object that both windows could reference:
class ViewModel
{
private List<Classes.Entity> _entityList = new List<Classes.Entity>();
public IEnumerable<Classes.Entity> EntityList
{
get { return _entityList; }
}
}
partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
When the second window is created, you can pass an instance of the ViewModel class to it, and set it as the DataContext.

WPF Combobox initial dictionary binding value not showing

I have a wpf combobox bound to a property LogicalP of a class SInstance. The ItemSource for the combobox is a dictionary that contains items of type LogicalP.
If I set LogicalP in SInstance to an initial state, the combobox text field shows empty. If I select the pulldown all my dictionary values are there. When I change the selection LogicalP in SInstance gets updated correctly. If I change Sinstance in C# the appropriate combobox value doesn't reflect the updated LogicalP from the pulldown.
I've set the binding mode to twoway with no luck. Any thoughts?
My Xaml:
<UserControl.Resources>
<ObjectDataProvider x:Key="PList"
ObjectType="{x:Type src:MainWindow}"
MethodName="GetLogPList"/>
</UserControl.Resources>
<DataTemplate DataType="{x:Type src:SInstance}">
<Grid>
<ComboBox ItemsSource="{Binding Source={StaticResource PList}}"
DisplayMemberPath ="Value.Name"
SelectedValuePath="Value"
SelectedValue="{Binding Path=LogicalP,Mode=TwoWay}">
</ComboBox>
</Grid>
</DataTemplate>
My C#:
public Dictionary<string, LogicalPType> LogPList { get; private set; }
public Dictionary<string, LogicalPType> GetLogPList()
{
return LogPList;
}
public class LogicalPType
{
public string Name { get; set; }
public string C { get; set; }
public string M { get; set; }
}
public class SInstance : INotifyPropertyChanged
{
private LogicalPType _LogicalP;
public string Name { get; set; }
public LogicalPType LogicalP
{
get { return _LogicalP; }
set
{
if (_LogicalP != value)
{
_LogicalP = value;
NotifyPropertyChanged("LogicalP");
}
}
}
#region INotifyPropertyChanged Members
#endregion
}
They are not looking at the same source.
You need to have SInstance supply both the LogPList and LogicalP.
_LogicalP is not connected to LogPList
If you want to different objects to compare to equal then you need to override Equals.
Here's my working solution. By moving the dictionary retrieval GetLogPList to the same class as that supplies the data (as suggested by Blam) I was able to get the binding to work both ways. I changed binding to a list rather than a dictionary to simplify the combobox
Here's the updated Xaml showing the new ItemsSource binding and removal of the SelectedValuePath:
<DataTemplate DataType="{x:Type src:SInstance}">
<Grid>
<ComboBox ItemsSource="{Binding GetLogPList}"
DisplayMemberPath ="Name"
SelectedValue="{Binding Path=LogicalP,Mode=TwoWay}">
</ComboBox>
</Grid>
</DataTemplate>
I then changed the dictionary LogPList to static so that it would be accessible to the class SInstance:
public static Dictionary<string, LogicalPType> LogPList { get; private set; }
Finally, I moved GetLogPList to the class SInstance as a property. Note again it's returning a list as opposed to a dictionary to make the Xaml a little simpler:
public class SInstance : INotifyPropertyChanged
{
public List<LogicalPType> GetLogPList
{
get { return MainWindow.LogPList.Values.ToList(); }
set { }
}
private LogicalPType _LogicalP;
public string Name { get; set; }
public LogicalPType LogicalP
{
get { return _LogicalP; }
set
{
if (_LogicalP != value)
{
_LogicalP = value;
NotifyPropertyChanged("LogicalP");
}
}
}
#region INotifyPropertyChanged Members
#endregion
}

How do I sort a WPF treeview that has items bound to the properties of an Item subclass?

I have two classes,
public class BookItem
{
public string BookID { get; set; }
public string ItemID { get; set; }
public Item Item { get; set; }
public ItemType Type { get; set; }
public string ParentID { get; set; }
public string BoxID { get; set; }
public string StyleID { get; set; }
public string NotesID { get; set; }
public string Code_XAML { get; set; }
public string Description_XAML { get; set; }
public CompositeCollection SubItems { get; set; }
}
public class Item : ClaunchBaseClass
{
public string ItemID { get; set; }
public int Type { get; set; }
public string Code { get; set; }
public string Description { get; set; }
private BookList _books = new BookList();
public BookList Books { get {return _books;} set { _books = value; }}
}
and I've created the following XAML:
<pre>
<TreeView Name="tvList" Grid.Row="2" MouseDoubleClick="tvList_MouseDoubleClick">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="x:Type j:BookItem" ItemsSource="{Binding SubMenu}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Item.Code}" Grid.Column="0" />
<TextBlock Text="{Binding Item.Description}" Grid.Column="1"/>
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<code>
This XAML binds the treeview items to the collection of book items and displays the Item subclass's Description and Code, the treeview populates and displays correctly but now I want to sort the treeview on either Item.Code or Item.Description and have tried the following with no results:
<pre>
var bookItemsSort = CollectionViewSource.GetDefaultView(_bookItemList) as ListCollectionView;
tvList.ItemsSource = _bookItemList; //bind the book items to the treeview
bookItemsSort.SortDescriptions.Clear();
bookItemsSort.SortDescriptions.Add(new SortDescription(sort, Ascending));
<code>
I've had this code work correctly for other treeviews so I can only guess it is a problem with binding to a subclass.
While the answers here provided partial answers to my question, none of them gave the answer that I needed.
The most sensible solution for this problem was to write my own object comparer for this object type and sort the underlying list and then re-bind the new list to the treeview. This allowed for comparing sublasses at any nested level which I couldn't make work any other way :)
You need to get the default view for each sub-list, and apply a CollectionViewSource sorting to it. The code you posted only affects the top level items.
Bind your TreeView.ItemsSource to the DefaultView. SortDescriptions will not change your DataList, only the View of it.
tvList.ItemsSource = bookItemsSort;
See Bea Stollnitz blog: How can I sort a hierarchy?

Silverlight DataContext Databinding behavior

I'll start off with a stripped-down/sanitized version of my code:
Model:
class DataObj : INotifyPropertyChanged {
// these actually call OnPropertyChanged, and have associated private variables
public string Name { get; set; }
public int Age { get; set; }
}
class DataContextObj : INotifyPropertyChanged {
public List<DataObj> DataItems { get; set; }
}
View:
<StackPanel x:Name="MyPanel">
<TextBlock Text="{Binding Path=DataItems[0].Name}" />
<TextBlock Text="{Binding Path=DataItems[0].Age}" />
</StackPanel>
View code-behind:
//in the constructor
MyPanel.DataContext = new DataContextObj();
Now, my question is, if the DataItems list is initialized but empty, what is the expected behavior when something tries to bind to, say, the first element in the list? My understanding is that it just ignores the binding; is that true?
Yes it will ignore the binding. If subsequently an item is added to the empty list the text blocks will not update since the binding expression associated with them will not know that the change happened.
The appropriate solution is to use:
public class DataContextObj
{
public ObservableCollection<DataObj> DataItems {get; private set; }
}
Additions to the collection will notify "Item[]" has changed which will allow the binding expression to re-evaluate.

Resources