WPF get different value from ComboBox - c#

My ComboBox looks like this:
<ComboBox Name="cbMDLName" Style="{StaticResource ComoboBox}"
DisplayMemberPath="Name" ItemsSource="{Binding Path=MDLCollection}"
SelectionChanged="cbMDLName_SelectionChanged" Grid.Row="0" Grid.Column="1"/>
The DataContext is set from the code behind file.
this.DataContext = this.ManagerRoot.MDLM;
The ItemSource of the ComboBox is set to ObservableCollection<MDL> MDLCollection {get;set;}.
MDL.cs
public class MDL : INotifyPropertyChanged
{
public AdditionManager AdditionManager { get; set; }
private string _name;
public string Name
{
get { return this._name; }
set
{
this._name = value;
this.OnPropertyChanged("Name");
}
}
private int _index;
public int Index
{
get { return this._index; }
set
{
this._index = value;
}
}
}
Now I want to get the index from the selected entry. How can I do that?

You may try this.
int selectedValue = (int)cbMDLName.SelectedValue;

Related

GridTemplateColumn databinding to complex properties

I have 3 model classes as follows (Vegetable, Fruit and ItemInBasket):
public class Vegetable
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public Vegetable(string _Name)
{
this.Name = _Name;
}
}
public class Fruit
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public Fruit(string _Name)
{
this.Name = _Name;
}
}
public class ItemInBasket
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
object fruitorvegetable;
public object FruitOrVegetable
{
get { return fruitorvegetable; }
set { fruitorvegetable = value; }
}
int quantity;
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
public ItemInBasket(string _Name, object _FruitOrVegetable, int _Quantity)
{
this.Name = _Name;
this.FruitOrVegetable = _FruitOrVegetable;
this.quantity = _Quantity;
}
}
My ViewModel is:
public class ViewModel
{
private ObservableCollection<object> _availableItems;
public ObservableCollection<object> AvailableItems
{
get { return _availableItems; }
set { _availableItems = value; }
}
private ObservableCollection<ItemInBasket> _itemsInBasket;
public ObservableCollection<ItemInBasket> ItemsInBasket
{
get { return _itemsInBasket; }
set { _itemsInBasket = value; }
}
public ViewModel()
{
_availableItems = new ObservableCollection<object>();
_itemsInBasket = new ObservableCollection<ItemInBasket>();
this.GenerateAvailableItems();
this.GenerateItemsInBasket();
}
private void GenerateAvailableItems()
{
_availableItems.Add(new Vegetable("Broccoli")); // index 0
_availableItems.Add(new Vegetable("Kale")); // index 1
_availableItems.Add(new Vegetable("Spinach")); // index 2
_availableItems.Add(new Vegetable("Carrots")); // index 3
_availableItems.Add(new Vegetable("Garlic")); // index 4
_availableItems.Add(new Fruit("Apple")); // index 5
_availableItems.Add(new Fruit("Orange")); // index 6
_availableItems.Add(new Fruit("Pear")); // index 7
_availableItems.Add(new Fruit("Cherry")); // index 8
_availableItems.Add(new Fruit("Grape")); // index 9
}
private void GenerateItemsInBasket()
{
_itemsInBasket.Add(new ItemInBasket("Apples",_availableItems[5],3));
_itemsInBasket.Add(new ItemInBasket("Kale", _availableItems[1], 10));
_itemsInBasket.Add(new ItemInBasket("Grape", _availableItems[9], 2));
_itemsInBasket.Add(new ItemInBasket("Carrots", _availableItems[3], 1));
}
}
I am trying to be able to modify the FruitOrVegetable inside of each ItemInBasket displayed in the datagrid but I have an issue with the data binding. I am using Syncfusion datagrid but I think it shouldnt affect anything.
<syncfusion:SfDataGrid AutoGenerateColumns="False" SelectionMode="Single"
AllowEditing="True" AllowDeleting="True" ItemsSource="{Binding ItemsInBasket,Source={StaticResource viewModel}}">
<syncfusion:SfDataGrid.Columns>
<syncfusion:GridTemplateColumn MappingName="FruitOrVegetable" HeaderText="Order">
<syncfusion:GridTemplateColumn.EditTemplate>
<DataTemplate>
<ComboBox IsEditable="True" DisplayMemberPath="Name" SelectedValuePath="Name"
Text="{Binding FruitOrVegetable.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding AvailableItems, Source={StaticResource viewModel}}"/>
</DataTemplate>
</syncfusion:GridTemplateColumn.EditTemplate>
<syncfusion:GridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</syncfusion:GridTemplateColumn.CellTemplate>
</syncfusion:GridTemplateColumn>
<syncfusion:GridNumericColumn HeaderText="Quantity" MappingName ="Quantity"/>
</syncfusion:SfDataGrid.Columns>
</syncfusion:SfDataGrid>
Your requirement to display the modified value of the FruitOrVegetable in SfDataGrid can be achieved by binding the SelectedValue property of ComboBox. Please refer to the below code snippet,
<ComboBox IsEditable="True" DisplayMemberPath="Name" SelectedValuePath="Name"
SelectedValue="{Binding Name}"
Text="{Binding FruitOrVegetable.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding AvailableItems, Source={StaticResource viewModel}}"/>
This is where what we call an "interface" comes in handy. This gives you the surety that the properties are indeed present on this object. Consider it a contract between the class and interface to always include some properties.
You can learn more about it here
You can define an interface like this
public interface INamedItem
{
string Name {get;set;}
}
Now instead of the object type use this interface(first implement them in your vegetable and fruit class) so that you can easily bind to it(and many other benefits)
public class Vegetable : INamedItem
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public Vegetable(string _Name)
{
this.Name = _Name;
}
}
public class Fruit: INamedItem
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public Fruit(string _Name)
{
this.Name = _Name;
}
}
public class ItemInBasket
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
INamedItem fruitorvegetable;
public object FruitOrVegetable
{
get { return fruitorvegetable; }
set { fruitorvegetable = value; }
}
int quantity;
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
public ItemInBasket(string _Name, INamedItem _FruitOrVegetable, int _Quantity)
{
this.Name = _Name;
this.FruitOrVegetable = _FruitOrVegetable;
this.quantity = _Quantity;
}
}
For your available items it becomes:
private ObservableCollection<INamedItem> _availableItems;
public ObservableCollection<INamedItem> AvailableItems
{
get { return _availableItems; }
set { _availableItems = value; }
}
Now add normally as you have in your code so no more changes to that and check if it works :)

WPF Datagrid for IList<String> - cannot add new values

I have a JSON model and on there an IList subordinates.
Nothing special and I wanted to put them into a datagrid where users can add and remove values from that list.
So first I hit the only length column problem because apparently C# finds it funny to pick the properties and that happens to be length.
So I got around that by another stackoverflow explaining this.
<DataGrid ItemsSource="{Binding Path=Job.Subordinates, UpdateSourceTrigger=PropertyChanged}" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" Height="Auto" HorizontalAlignment="Left" Grid.Column="1" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Subordinate Id">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
However now the end result is a read only DataGrid. I can't add new rows or edit an existing row if I load in a JSON.
Why would this behavior be happening ?
edit: the json object with its field
using GalaSoft.MvvmLight;
using System;
using System.Collections.Generic;
namespace GalaxyCreator.Model.Json
{
public class Job : ObservableObject
{
private String _id;
public String Id
{
get { return _id; }
set
{
Set(ref _id, value);
}
}
private String _name;
public String Name
{
get { return _name; }
set
{
Set(ref _name, value);
}
}
private Boolean _startActive;
public Boolean StartActive
{
get { return _startActive; }
set
{
Set(ref _startActive, value);
}
}
private Boolean _disabled;
public Boolean Disabled
{
get { return _disabled; }
set
{
Set(ref _disabled, value);
}
}
private Boolean _rebuild;
public Boolean Rebuild
{
get { return _rebuild; }
set
{
Set(ref _rebuild, value);
}
}
private Boolean _comandeerable;
public Boolean Commandeerable
{
get { return _comandeerable; }
set
{
Set(ref _comandeerable, value);
}
}
private Boolean _subordinate;
public Boolean Subordinate
{
get { return _subordinate; }
set
{
Set(ref _subordinate, value);
}
}
private bool _buildatshipyard = true;
public bool Buildatshipyard
{
get { return _buildatshipyard; }
set
{
Set(ref _buildatshipyard, value);
}
}
private JobLocation _jobLocation = new JobLocation();
public JobLocation JobLocation
{
get { return _jobLocation; }
set
{
Set(ref _jobLocation, value);
}
}
private JobCategory _jobCategory = new JobCategory();
public JobCategory JobCategory
{
get { return _jobCategory; }
set
{
Set(ref _jobCategory, value);
}
}
private JobQuota _jobQuota = new JobQuota();
public JobQuota JobQuota
{
get { return _jobQuota; }
set
{
Set(ref _jobQuota, value);
}
}
private IList<JobOrder> _orders = new List<JobOrder>();
public IList<JobOrder> Orders
{
get { return _orders; }
set
{
Set(ref _orders, value);
}
}
private String _basket;
public String Basket
{
get { return _basket; }
set
{
Set(ref _basket, value);
}
}
private String _encounters;
public String Encounters
{
get { return _encounters; }
set
{
Set(ref _encounters, value);
}
}
private String _time;
public String Time
{
get { return _time; }
set
{
Set(ref _time, value);
}
}
private Ship _ship = new Ship();
public Ship Ship
{
get { return _ship; }
set
{
Set(ref _ship, value);
}
}
private IList<String> _subordinates = new List<String>();
public IList<String> Subordinates
{
get { return _subordinates; }
set
{
Set(ref _subordinates, value);
}
}
}
}
edit: I tried a new approach but still no luck, this time I tried wrapping the String
Wrapper
public class SubordinateItem : ObservableObject
{
private String _value = default(String);
public SubordinateItem(string subordinate)
{
this._value = subordinate;
}
public String Value
{
get { return _value; }
set
{
if (value != _value)
{
_value = value;
Set(ref _value, value);
}
}
}
}
ViewModel
class JobEditorDetailViewModel : DialogViewModelBase
{
public Job Job { get; set; }
private ObservableCollection<SubordinateItem> _subordinateItems = new ObservableCollection<SubordinateItem>();
public ObservableCollection<SubordinateItem> SubordinateItems
{
get { return _subordinateItems; }
set
{
this.Job.Subordinates.Clear();
foreach (SubordinateItem item in value)
{
this.Job.Subordinates.Add(item.Value);
}
Set(ref _subordinateItems, value);
}
}
public JobEditorDetailViewModel(string message, Job job) : base(message)
{
this.Job = job;
this._saveCommand = new RelayCommand<object>((parent) => OnSaveClicked(parent));
this._cancelCommand = new RelayCommand<object>((parent) => OnCancelClicked(parent));
foreach(String subordinate in Job.Subordinates)
{
_subordinateItems.Add(new SubordinateItem(subordinate));
}
}
}
XAML
<DataGrid ItemsSource="{Binding SubordinateItems, NotifyOnTargetUpdated=True}" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" Height="Auto" Grid.Row="1" AutoGenerateColumns="False" HorizontalAlignment="Left" Width="572" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Width="500" Header="Subordinate Id" IsReadOnly="false" />
</DataGrid.Columns>
</DataGrid>
So after a long session on writing all kinds of hacks to try and fix it I got a solution with the following setup
public class SubordinateItem : ObservableObject
{
private String _value = default(String);
public SubordinateItem() { }
public SubordinateItem(string subordinate)
{
this._value = subordinate;
}
public String Value
{
get { return _value; }
set
{
if (value != _value)
{
_value = value;
Set(ref _value, value);
}
}
}
}
Notice the public constructor without params, this is needed to be able to add new items to the datagrid. Without it, it cannot instantiate new objects for it. Here is the view model
class JobEditorDetailViewModel : DialogViewModelBase
{
public Job Job { get; set; }
private RelayCommand<object> _saveCommand = null;
public RelayCommand<object> SaveCommand
{
get { return _saveCommand; }
set { _saveCommand = value; }
}
private RelayCommand<object> _cancelCommand = null;
public RelayCommand<object> CancelCommand
{
get { return _cancelCommand; }
set { _cancelCommand = value; }
}
private IList<SubordinateItem> _subordinateItems = new List<SubordinateItem>();
public IList<SubordinateItem> SubordinateItems
{
get { return _subordinateItems; }
set
{
Set(ref _subordinateItems, value);
}
}
public JobEditorDetailViewModel(string message, Job job) : base(message)
{
this.Job = job;
this._saveCommand = new RelayCommand<object>((parent) => OnSaveClicked(parent));
this._cancelCommand = new RelayCommand<object>((parent) => OnCancelClicked(parent));
foreach (String subordinate in Job.Subordinates)
{
_subordinateItems.Add(new SubordinateItem(subordinate));
}
}
private void OnSaveClicked(object parameter)
{
this.Job.Subordinates.Clear();
foreach (SubordinateItem item in _subordinateItems)
{
this.Job.Subordinates.Add(item.Value);
}
this.CloseDialogWithResult(parameter as Window, DialogResult.Yes);
}
private void OnCancelClicked(object parameter)
{
this.CloseDialogWithResult(parameter as Window, DialogResult.No);
}
}
So in the constructor of the viewmodel I take the data from the json object Job.Subordinates which is an IList of String and I wrap it in SubordinateItem and store it in _subordinateItems. The datagrid is bound to this and will work with it. Then I have a save button with a command behind it where I do the reverse. I take everything the DataGrid has in _subordinateItems and put it back into the json object. There was no way whatsoever it seemed for me to bind the datagrid directly to the json object IList of String
The Xaml relevant part
<Label Content="Subordinates" />
<DataGrid ItemsSource="{Binding SubordinateItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
Height="Auto" Grid.Row="1"
AutoGenerateColumns="False"
HorizontalAlignment="Left"
Width="572" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Width="500" Header="Subordinate Id" IsReadOnly="false" />
</DataGrid.Columns>
</DataGrid>

Can delete from Observable Collection but view does not update

I am working on the ability to add and delete rows in an observable collection.
Since the original post I have created a test app that only has the ability to delete rows from the observable collection. I populate the database externally then open it in this just to test the delete function which doesn't work. It executes the RemoveAt line, deletes from the Observable Collection but the view does not update. Here is all my code:
Model:
public class TestModel : ObservableObject
{
#region Properties
private Double id;
public Double ID
{
get { return id; }
set
{
id = value;
RaisePropertyChangedEvent("ID");
}
}
private string type;
public string Type
{
get { return type; }
set
{
type = value;
RaisePropertyChangedEvent("Type");
}
}
private decimal amount;
public decimal Amount
{
get { return amount; }
set
{
amount = value;
RaisePropertyChangedEvent("Amount");
}
}
private string notes;
public string Notes
{
get { return notes; }
set
{
notes = value;
RaisePropertyChangedEvent("Notes");
}
}
#endregion
}
Viewmodel:
public class MainWindowViewModel : ObservableObject
{
#region GetData
public MainWindowViewModel()
{
Transactions = DatabaseFunctions.getTransactionData();
}
#endregion
#region ObservableCollections
private ObservableCollection<TestModel> transactions;
public ObservableCollection<TestModel> Transactions
{
get { return transactions; }
set
{
transactions = value;
RaisePropertyChangedEvent("Transactions");
}
}
#endregion
#region Properties
public static string SharedWith;
private Double id;
public Double ID
{
get { return id; }
set
{
id = value;
RaisePropertyChangedEvent("ID");
}
}
private string type;
public string Type
{
get { return type; }
set
{
type = value;
RaisePropertyChangedEvent("Type");
}
}
private decimal amount;
public decimal Amount
{
get { return amount; }
set
{
amount = value;
RaisePropertyChangedEvent("Amount");
}
}
private string notes;
public string Notes
{
get { return notes; }
set
{
notes = value;
RaisePropertyChangedEvent("Notes");
}
}
#endregion
public void DeleteTransactionRow(List<TestModel> SelectedTransaction, int SelectedIndex)
{
Transactions.RemoveAt(SelectedIndex);
}
View:
<Window x:Class="OCTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Properties="clr-namespace:OCTest.Properties"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid"
Title="Test" SizeToContent="WidthAndHeight"
xmlns:ViewModel="clr-namespace:OCTest.ViewModel">
<Window.DataContext>
<ViewModel:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<xcdg:DataGridControl x:Name="TransactionsDataGrid" Grid.Row="0" ItemsSource="{Binding Transactions, Mode=TwoWay}" AutoCreateColumns="False" SelectionMode="Single">
<xcdg:DataGridControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete Row" Click="DeleteTransactionRow_Click"/>
</ContextMenu>
</xcdg:DataGridControl.ContextMenu>
<xcdg:DataGridControl.Columns>
<xcdg:Column Title="Type" FieldName="Type" ReadOnly="True"/>
<xcdg:Column Title="Amount" FieldName="Amount">
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat={}{0:C}}"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>
<xcdg:Column Title="Notes" FieldName="Notes"/>
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
</Grid>
Code behind that deals with the delete command and passes needed information to the view model:
public partial class MainWindow : Window
{
MainWindowViewModel mainwindowviewmodel = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
}
private void DeleteTransactionRow_Click(object sender, RoutedEventArgs e)
{
List<TestModel> selectedtransaction = TransactionsDataGrid.SelectedItems.Cast<TestModel>().ToList();
mainwindowviewmodel.DeleteTransactionRow(selectedtransaction, TransactionsDataGrid.SelectedIndex);
}
private void MouseRightButtonUpHandler(object sender, RoutedEventArgs e)
{
this.TransactionsDataGrid.SelectedItem = ((DataCell)sender).ParentRow.DataContext;
}
}
So hopefully someone can see why the RemoveAt doesn't update the view.
You need to set the DataContext of the MainWindow to your view model:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
I'd also recommend looking into binding click events to commands in your view model rather than relying on the code behind.

Binding ComboBox SelectedItem using MVVM

I have a problem with the SelectedItem in my ComboBox.
<ComboBox Name="cbxSalesPeriods"
ItemsSource="{Binding SalesPeriods}"
DisplayMemberPath="displayPeriod"
SelectedItem="{Binding SelectedSalesPeriod}"
SelectedValuePath="displayPeriod"
IsSynchronizedWithCurrentItem="True"/>
If I open the ComboBox, I see the values.
If I select an item, the selected Item won't be shown.
Has anybody an idea?
In my ViewModel I have these two properties:
public ObservableCollection<SalesPeriodVM> SalesPeriods { get; private set; }
private SalesPeriodVM selectedSalesPeriod;
public SalesPeriodVM SelectedSalesPeriod
{
get { return selectedSalesPeriod; }
set
{
if (selectedSalesPeriod != value)
{
selectedSalesPeriod = value;
RaisePropertyChanged("SelectedSalesPeriod");
}
}
}
These are a few properties from the class :
public SalesPeriodVO Vo
{
get { return period; }
}
public int Year
{
get { return period.Year; }
set
{
if (period.Year != value)
{
period.Year = value;
RaisePropertyChanged("Year");
}
}
}
public int Month
{
get { return period.Month; }
set
{
if (period.Month != value)
{
period.Month = value;
RaisePropertyChanged("Month");
}
}
}
public string displayPeriod {
get
{
return this.ToString();
}
}
public override string ToString()
{
return String.Format("{0:D2}.{1}", Month, Year);
}
EDIT:
The Following happens If I remove the Property DisplayMemberPath:
You seem to be unnecessarily setting properties on your ComboBox. You can remove the DisplayMemberPath and SelectedValuePath properties which have different uses. It might be an idea for you to take a look at the Difference between SelectedItem, SelectedValue and SelectedValuePath post here for an explanation of these properties. Try this:
<ComboBox Name="cbxSalesPeriods"
ItemsSource="{Binding SalesPeriods}"
SelectedItem="{Binding SelectedSalesPeriod}"
IsSynchronizedWithCurrentItem="True"/>
Furthermore, it is pointless using your displayPeriod property, as the WPF Framework would call the ToString method automatically for objects that it needs to display that don't have a DataTemplate set up for them explicitly.
UPDATE >>>
As I can't see all of your code, I cannot tell you what you are doing wrong. Instead, all I can do is to provide you with a complete working example of how to achieve what you want. I've removed the pointless displayPeriod property and also your SalesPeriodVO property from your class as I know nothing about it... maybe that is the cause of your problem??. Try this:
public class SalesPeriodV
{
private int month, year;
public int Year
{
get { return year; }
set
{
if (year != value)
{
year = value;
NotifyPropertyChanged("Year");
}
}
}
public int Month
{
get { return month; }
set
{
if (month != value)
{
month = value;
NotifyPropertyChanged("Month");
}
}
}
public override string ToString()
{
return String.Format("{0:D2}.{1}", Month, Year);
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
PropertyChanged(this, new PropertyChangedEventArgs("HasError"));
}
}
}
Then I added two properties into the view model:
private ObservableCollection<SalesPeriodV> salesPeriods = new ObservableCollection<SalesPeriodV>();
public ObservableCollection<SalesPeriodV> SalesPeriods
{
get { return salesPeriods; }
set { salesPeriods = value; NotifyPropertyChanged("SalesPeriods"); }
}
private SalesPeriodV selectedItem = new SalesPeriodV();
public SalesPeriodV SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
}
Then initialised the collection with your values:
SalesPeriods.Add(new SalesPeriodV() { Month = 3, Year = 2013 } );
SalesPeriods.Add(new SalesPeriodV() { Month = 4, Year = 2013 } );
And then data bound only these two properties to a ComboBox:
<ComboBox ItemsSource="{Binding SalesPeriods}" SelectedItem="{Binding SelectedItem}" />
That's it... that's all you need for a perfectly working example. You should see that the display of the items comes from the ToString method without your displayPeriod property. Hopefully, you can work out your mistakes from this code example.
I had a similar problem where the SelectedItem-binding did not update when I selected something in the combobox. My problem was that I had to set UpdateSourceTrigger=PropertyChanged for the binding.
<ComboBox ItemsSource="{Binding SalesPeriods}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}" />
<!-- xaml code-->
<Grid>
<ComboBox Name="cmbData" SelectedItem="{Binding SelectedstudentInfo, Mode=OneWayToSource}" HorizontalAlignment="Left" Margin="225,150,0,0" VerticalAlignment="Top" Width="120" DisplayMemberPath="name" SelectedValuePath="id" SelectedIndex="0" />
<Button VerticalAlignment="Center" Margin="0,0,150,0" Height="40" Width="70" Click="Button_Click">OK</Button>
</Grid>
//student Class
public class Student
{
public int Id { set; get; }
public string name { set; get; }
}
//set 2 properties in MainWindow.xaml.cs Class
public ObservableCollection<Student> studentInfo { set; get; }
public Student SelectedstudentInfo { set; get; }
//MainWindow.xaml.cs Constructor
public MainWindow()
{
InitializeComponent();
bindCombo();
this.DataContext = this;
cmbData.ItemsSource = studentInfo;
}
//method to bind cobobox or you can fetch data from database in MainWindow.xaml.cs
public void bindCombo()
{
ObservableCollection<Student> studentList = new ObservableCollection<Student>();
studentList.Add(new Student { Id=0 ,name="==Select=="});
studentList.Add(new Student { Id = 1, name = "zoyeb" });
studentList.Add(new Student { Id = 2, name = "siddiq" });
studentList.Add(new Student { Id = 3, name = "James" });
studentInfo=studentList;
}
//button click to get selected student MainWindow.xaml.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Student student = SelectedstudentInfo;
if(student.Id ==0)
{
MessageBox.Show("select name from dropdown");
}
else
{
MessageBox.Show("Name :"+student.name + "Id :"+student.Id);
}
}

MVVM model - association between models

Let's say I have following models:
class Worker
{
int Id;
string firstname;
string lastname;
}
class Department
{
string title;
string description;
List<Worker> workers;
}
I want to display, on UI, department's title,description and list of workers inside listbox (in listbox I want to display only firstname and lastname).
Do I need to create ONE viewmodel that will wrap this relation or I must I create a viewmodel for every model that I have?
You can create on ViewModel Which wrap both of them like:
namespace XXXX.ViewModel
{
public class MainViewModel : ViewModelBase
{
private int _id;
private string _total;
private string _description;
private ObservableCollection<Worker> _workers;
public int Id
{
get { return _id; }
set
{
if (value == _id) return;
_id = value;
RaisePropertyChanged("Id");
}
}
public string Total
{
get { return _total; }
set
{
if (value == _total) return;
_total = value;
RaisePropertyChanged("Total");
}
}
public string Description
{
get { return _description; }
set
{
if (value == _description) return;
_description = value;
RaisePropertyChanged("Description");
}
}
public ObservableCollection<Worker> Workers
{
get { return _workers; }
set
{
if (value == _workers) return;
_workers = value;
RaisePropertyChanged("Workers");
}
}
//****************** You Logic *************************
public MainViewModel()
{
Department department = new Department();
}
//****************** You Logic *************************
}
}
You wouldn't have ViewModel for every Model, in MVVM you should have a unique ViewModel for almost every view. You would then map the Model to the ViewModel.
For example:
public class DepartmentViewModel
{
public string title { get; set; }
public string description { get; set; }
public IEnumerable<Worker> workers { get; set; }
//Additional ViewModel properties here
//These may or may not be items that exist in your Model
/// <summary>
/// Mapped to the description but truncated to 10 characters and followed by an elispe (...)
/// </summary>
public string ShortDescription
{
get
{
return description.Substring(0,10) + "...";
}
}
}
I realize at first this looks a little redundant. However, there could be other less 1:1 type of views you might create from the model.
Also check out automapper.org, this is a great tool for mapping object to object.
You have 1 view model that contains both the workers and the department.
If the view only wants to show certain attributes of the workers, then the view should do that filtering. Try using an item template:
<ListBox x:Name="_workers" ItemsSource="{Binding Workers}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding firstname}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding lastname}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The view model should contain:
private string _title;
public string Title {
get {return _title;}
set {_title = value; RaisePropertyChanged("Title");}
}
private string _description;
public string Description {
get {return _description;}
set {_description= value; RaisePropertyChanged("Description");}
}
public ObservableCollection Workers {get; private set;}
public Constructor()
{
Workers = new ObservableCollection();
}
//This method is called by the model once it has fetched data.
//This can be done as a callback or in an event handler
public CalledByTheModelAfterLoadingData(Department department)
{
Title = department.Title;
Description = department.Description;
foreach (var worker in department.Workers)
{
Workers.Add(worker);
}
}

Categories