ComboBox SelectedItem Binding does not work - c#

I have a datagrid with four columns.
<DataGrid ItemSource="{Binding MyDataSet,Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock ...../>
</DataTemplate>
</DataGridColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock ...../>
</DataTemplate>
</DataGridColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
.............
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemSource ="{Binding FirstComboBoxData }" SelectedItem="{Binding FirstComboBoxSelectedVal}"/>
</DataTemplate>
</DataGridColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataTemplate>
<ComboBox ItemSource ="{Binding SecondComboBoxData }" SelectedItem="{Binding SecondComboBoxSelectedVal}"/>
</DataTemplate>
</DataGridColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The model class MyData is like in the following.
class MyData : ObservableObject
{
private string _code;
public string Code
{
set { _code = value ; OnPropertyChanged("Code");}
get { return _code ;}
}
private string _name;
public string Name
{
set { _name = value ; OnPropertyChanged("Name");}
get { return _name ;}
}
private List<SomeOtherClass> _firstComboBoxData;
public List<SomeOtherClass> FirstComboBoxData
{
set { _firstComboBoxData = value ; OnPropertyChanged("FirstComboBoxData");}
get { return _firstComboBoxData ;}
}
private List<SomeOtherClass> _secondComboBoxData;
public List<SomeOtherClass> SecondComboBoxData
{
set { _secondComboBoxData = value ; OnPropertyChanged("SecondComboBoxData");}
get { return _secondComboBoxData ;}
}
private SomeOtherClass _firstComboBoxSelectedVal;
public SomeOtherClass Name
{
set { _firstComboBoxSelectedVal= value ; OnPropertyChanged("FirstComboBoxSelectedVal");}
get { return _firstComboBoxSelectedVal;}
}
private SomeOtherClass _secondComboBoxSelectedVal;
public SomeOtherClass SecondComboBoxSelectedVal
{
set { _secondComboBoxSelectedVal= value ; OnPropertyChanged("SecondComboBoxSelectedVal");}
get { return _secondComboBoxSelectedVal;}
}
}
class SomeOtherClass
{
private int id;
public int ID
{
set { id = value ; OnPropertyChanged("ID");}
get { return id ;}
}
}
Then I have the following list in my ViewModel.
private ObservableCollection<MyData> _myDataSet;
public ObservableCollection<MyData> MyDataSet
{
set { _myDataSet=value; OnPropertyChanged ("MyDataSet"); }
get { return _myDataSet ;}
}
I see everything is fine. But when I change the selected item of said comboboxes, MyDataSet's relevant FirstComboBoxSelectedVal or SecondComboBoxSelectedVal is not updated
Have I done something wrong somewhere?

Related

GridView ComboBox - Bind selected element property

EDIT : Working better, but still not perfect :
SelectedFace has been set as ComboElement (still couldn't manage to do the job without this additional parameter).
Still not working : When I create a new Tool, SelectedFace is updated correctly, but if I want to edit an existing one, no Update is made(no binding).
End Edit
I have a GridView, in which there is a ComboBox to select on which face each tool located.
I cannot manage to bind the selected value to my object property.
Here are the properties as declared in my Tool object :
private long face = 0;
public long Face
{
get { return face; }
set
{
this.face = value;
this.NotifyPropertyChanged("Face");
}
}
private ComboElement selectedFace;
public ComboElement SelectedFace
{
get { return selectedFace; }
set
{
this.selectedFace = value;
this.Face = value.MyInt;
this.NotifyPropertyChanged("SelectedFace");
}
}
private ObservableCollection<ComboElement> comboFaceSlot=new ObservableCollection<ComboElement> { new ComboElement { MyInt = 0, MyString = "Web" }, new ComboElement { MyInt = 1, MyString = "Top" }, new ComboElement { MyInt = 2, MyString = "Bottom" }, new ComboElement { MyInt = 3, MyString = "Back" } };
public ObservableCollection<ComboElement> ComboFaceSlot
{
get { return comboFaceSlot; }
set
{
this.comboFaceSlot = value;
this.NotifyPropertyChanged("ComboFaceSlot");
}
}
Nota : At the beginning I had not the SelectedFace property, I added it thinking this would solve my problem, but it didn't.
And XAML is as following :
<DataGrid Grid.Row="1" Margin="5" BorderBrush="{StaticResource WindowBorderColor}" BorderThickness="3" Width="Auto" ItemsSource="{Binding Path=ListTools}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" HorizontalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static p:Resources.Length}" Binding="{Binding Path=Longueur}" Width="100" />
<DataGridTextColumn Header="{x:Static p:Resources.Diameter}" Binding="{Binding Path=Diameter}" Width="100" />
<DataGridTemplateColumn Header="{x:Static p:Resources.Face}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ComboFaceSlot}" DisplayMemberPath="MyString" SelectedItem="{Binding SelectedFace}" Width="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static p:Resources.Angle}" Binding="{Binding Path=Angle}" Width="100" />
</DataGrid.Columns>
</DataGrid>
Also forgot, my ComboElement I use for all Comboboxes of my project :
public class ComboElement
{
private long myInt=-1;
public long MyInt
{
get { return myInt; }
set { myInt = value; }
}
private string myString="";
public string MyString
{
get { return myString; }
set { myString = value; }
}
public ComboElement()
{
}
public ComboElement(long id, string name)
{
this.myInt = id;
this.MyString = name;
}
}
Edit : My ViewModel
private ObservableCollection<Tool> listTool = new ObservableCollection<Tool>();
public ObservableCollection<Tool> ListTool
{
get { return listTool; }
set
{
this.listTool = value;
NotifyPropertyChanged("ListTool");
}
}
private Machine cnc = new Machine();
public Machine CNC
{
get { return cnc; }
set { this.cnc = value;NotifyPropertyChanged("CNC"); }
}
public TableTools(Machine cnc)
{
InitializeComponent();
this.CNC = cnc;
foreach(Tool slot in CNC.ListTool)
{
this.ListTool.Add(slot);
}
DataContext = this;
}
It lists a list of Tools, and I want to say on which face each element is located.
But for now something is going wrong, I tried to use several properties of my ComboBox as SelectedItem, SelectedValue, SelectedValuePath, but until now didn't manage to solve it.
Ideally, I would like to delete "SelectedFace" element, and use only "Face" property.
You need to specify the Tool class like such in your question.
The root cause is not specifying the correct SelectedValuePath of your ComboElement object to be mapped to your Tool object's Face property as the SelectedValue.
Tool Class
public class Tool
{
private ObservableCollection<ComboElement> comboFaceSlot = new ObservableCollection<ComboElement> { new ComboElement { MyInt = 0, MyString = "Web" }, new ComboElement { MyInt = 1, MyString = "Top" }, new ComboElement { MyInt = 2, MyString = "Bottom" }, new ComboElement { MyInt = 3, MyString = "Back" } };
public ObservableCollection<ComboElement> ComboFaceSlot
{
get { return comboFaceSlot; }
set
{
this.comboFaceSlot = value;
this.NotifyPropertyChanged("ComboFaceSlot");
}
}
private long face = 0;
public long Face
{
get { return face; }
set
{
this.face = value;
this.NotifyPropertyChanged("Face");
}
}
}
ToolWindow View
<DataGrid Grid.Row="1" Margin="5" BorderBrush="{StaticResource WindowBorderColor}" BorderThickness="3" Width="Auto" ItemsSource="{Binding Path=ListTools}" AutoGenerateColumns="False" CanUserAddRows="True" CanUserDeleteRows="True" HorizontalAlignment="Center">
<DataGrid.Columns>
<DataGridTextColumn Header="{x:Static p:Resources.Length}" Binding="{Binding Path=Longueur}" Width="100" />
<DataGridTextColumn Header="{x:Static p:Resources.Diameter}" Binding="{Binding Path=Diameter}" Width="100" />
<DataGridTemplateColumn Header="{x:Static p:Resources.Face}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ComboFaceSlot}"
DisplayMemberPath="MyString"
SelectedValuePath="MyInt"
SelectedValue="{Binding Face, UpdateSourceTrigger=PropertyChanged}" Width="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static p:Resources.Angle}" Binding="{Binding Path=Angle}" Width="100" />
</DataGrid.Columns>

WPF Binding to three level ObservableCollection<T> property

I have 3 level subclasses with ObservableCollection<T> properties of each other. In MainViewModel I created ObservableCollection<Group> property which elements of Group class will be in first level in TreeView. In every Group class I created child ObservableCollection<Parameter> property. And in the end in Parameter class I created ObservableCollection<ParameterValue> for store values. Note: every class based on INotifyPropertyChanged interface. Let's to go the code.
Models.cs:
//BaseModel implement INotifyPropertyChanged
public class ParameterValue: BaseModel
{
private DateTime dateTimeValue;
public DateTime DateTimeValue
{
get { return dateTimeValue; }
set
{
dateTimeValue = value;
NotifyPropertyChanged("DateTimeValue");
}
}
private double value;
public double Value
{
get { return value; }
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
}
//BaseModel implement INotifyPropertyChanged
public class Parameter: BaseModel
{
public Parameter()
{
values = new ObservableCollection<ParameterValue>();
}
private ObservableCollection<ParameterValue> values;
public ObservableCollection<ParameterValue> Values
{
get { return values; }
set
{
values = value;
NotifyPropertyChanged("Values");
}
}
private int parameterId;
public int ParameterId
{
get { return parameterId; }
set
{
parameterId = value;
NotifyPropertyChanged("ParameterId");
}
}
private string parameterName;
public string ParameterName
{
get { return parameterName; }
set
{
parameterName = value;
NotifyPropertyChanged("ParameterName");
}
}
}
//BaseModel implement INotifyPropertyChanged
public class Group: BaseModel
{
public Group()
{
parameters = new ObservableCollection<Parameter>();
}
private ObservableCollection<Parameter> parameters;
public ObservableCollection<Parameter> Parameters
{
get { return parameters; }
set
{
parameters = value;
NotifyPropertyChanged("Parameters");
}
}
private int groupId;
public int GroupId
{
get { return groupId; }
set
{
groupId = value;
NotifyPropertyChanged("Id");
}
}
private string groupName;
public string GroupName
{
get { return groupName; }
set
{
groupName = value;
NotifyPropertyChanged("GroupName");
}
}
}
//Implementing INotifyPropertyChanged
public class BaseModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModels.cs:
//BaseModel implement INotifyPropertyChanged
public class MainViewModel: BaseModel
{
public MainViewModel()
{
groups = new ObservableCollection<Group>();
//fill sample data instead of recieving from DB
for (int i = 1; i < 11; i++)
{
Group group = new Group { GroupId = i, GroupName = "Group " + i.ToString()};
groups.Add(group);
for (int j = 1; j < 11; j++)
{
Parameter param = new Parameter { ParameterId = j, ParameterName = "Parameter "+j.ToString()};
for (int k = 1; k < 51; k++)
{
ParameterValue val = new ParameterValue { DateTimeValue = DateTime.Now.AddSeconds(i*j-k), Value = (1000-k*5)/((i+j)+1)};
param.Values.Add(val);
}
group.Parameters.Add(param);
}
}
int l = 0;
}
private ObservableCollection<Group> groups;
public ObservableCollection<Group> Groups
{
get { return groups; }
set
{
groups = value;
NotifyPropertyChanged("Groups");
}
}
}
And MainWindow.xaml in View role:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="75*" />
</Grid.ColumnDefinitions>
<TreeView x:Name="trv" Grid.Column="0" ItemsSource="{Binding Groups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Parameters}">
<TextBlock Text="{Binding GroupName}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ParameterName}"></TextBlock>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ListView Grid.Column="1" Background="Bisque" ItemsSource="{Binding Path=Groups.Parameters}">
<ListView.View>
<GridView>
<GridViewColumn Header="Date Time" DisplayMemberBinding="{Binding DateTimeValue}"/>
<GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
In MainViewModel I simplify data receiving from DB replacing by nested for loops with test data.
I try to make showing selected in TreeView Parameter data in ListView in MVVM way.
In DataGroup necessary to create SelectedItem property of Parameter class for more accurate data recieving from DB? Of course in MVVM way.
In your ListView ItemSource you have to bind to the Values like so...
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="75*" />
</Grid.ColumnDefinitions>
<TreeView x:Name="trv" Grid.Column="0" ItemsSource="{Binding Groups}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Parameters}">
<TextBlock Text="{Binding GroupName}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ParameterName}"></TextBlock>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ListView Grid.Column="1"
Background="Bisque"
ItemsSource="{Binding SelectedItem.Values, ElementName=trv}">
<ListView.View>
<GridView>
<GridViewColumn Header="Date Time" DisplayMemberBinding="{Binding DateTimeValue}"/>
<GridViewColumn Header="Value" DisplayMemberBinding="{Binding Value}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
Observer how I binded to the SelectedItem of your TreeView. That selected item is expected to be the parameter which has property Values.
If you select Group than nothing will be displayed since Group does not have Values collection. Only Parameter has that.

How to Display and select items in a Datagrid ComboBox with WPF C#, using MVVM

I want to be able to choose either "true" or "false"(boolean) from a ComboBox that is within a wpf Datagrid and be able to save that choice to my database.
I want to be able to indicate inside of a column if the row is "Active" or not through a boolean variable saved to my database as a bit(1 = true; 0 = false).
Here is my create table statement:
Create Table Statement(AccountType)
I populate the datagrid using an ObservableCollection as follows:
protected override void Get()
{
using (var dbContext = new IEMASEntitiesDataContext())
{
var accountType = from s in dbContext.AccountTypes select s;
var observable = new ObservableCollection<AccountType>(accountType);
Collection = observable;
}
}
My XAML code is as follows:
<DockPanel DataContext="{StaticResource ResourceKey=AccountTypeViewModel}" LastChildFill="True">
<ToolBar DockPanel.Dock="Top">
<Button Content="Display" Command="{Binding GetCommand}" Width="78"/>
<Button Content="Save" Command="{Binding SaveCommand}" Width="78"/>
</ToolBar>
<Grid>
<GroupBox x:Name="AccountTypeGroupBox">
<DataGrid x:Name="DataGridAccountType" ItemsSource="{Binding Collection}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="AccountType" Width="150" Binding="{Binding Path=AccountTypeName, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridComboBoxColumn Header="Active" Width="100" SelectedValueBinding="{Binding StatusList, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="AccountTypeId" DisplayMemberPath="Active"/>
<DataGridTemplateColumn Width="50" Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnDelete" Content="Delete" Command="{Binding DeleteCommand}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
</Grid>
</DockPanel>
It doesn't work. Nothing displays within the comboboxes, and I don't know how to save the selected item to the SQL Server database when it does display. I'd appreciate some help, please. I am using LINQ TO SQL. I am a newbie :-(.
As I can understand the problem is how to bind some XAML resource as a combo ItemsSource and in addtion how to binnd the selected value of a combo to the model behind the DataGrid row.
1. List item:
<Window x:Class="SoDataGridProjectsHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:soDataGridProjectsHelpAttempt="clr-namespace:SoDataGridProjectsHelpAttempt"
xmlns:system="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="CountriesArray" Type="soDataGridProjectsHelpAttempt:Country">
<soDataGridProjectsHelpAttempt:Country CountryName="Germany" CountryPop="150k"/>
<soDataGridProjectsHelpAttempt:Country CountryName="France" CountryPop="125k"/>
<soDataGridProjectsHelpAttempt:Country CountryName="Belarus" CountryPop="165k"/>
</x:Array>
<x:Array x:Key="StatusArray" Type="soDataGridProjectsHelpAttempt:ActivityStatus">
<soDataGridProjectsHelpAttempt:ActivityStatus VerbalStatus="Yes" BoolStatus="True"/>
<soDataGridProjectsHelpAttempt:ActivityStatus VerbalStatus="No" BoolStatus="False"/>
</x:Array>
</Window.Resources>
<Window.DataContext>
<soDataGridProjectsHelpAttempt:DataGridMainDataContext/>
</Window.DataContext>
<Grid>
<DataGrid ItemsSource="{Binding Collection}" AutoGenerateColumns="False" CanUserAddRows="True">
<DataGrid.Columns>
<DataGridTextColumn Width="Auto" Binding="{Binding UName}"/>
<DataGridComboBoxColumn Header="Country" DisplayMemberPath="CountryName"
ItemsSource="{StaticResource CountriesArray}" Width="Auto"
SelectedItemBinding="{Binding CountryData}"/>
<!--<DataGridComboBoxColumn Header="ActivityStatus" Width="Auto" ItemsSource="{StaticResource StatusArray}"
SelectedValueBinding="{Binding IsActive}" SelectedValuePath="BoolStatus" DisplayMemberPath="VerbalStatus"/>-->
<DataGridComboBoxColumn Header="ActivityStatus" SelectedItemBinding="{Binding IsActive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ItemsSource>
<x:Array Type="system:Boolean">
<system:Boolean>True</system:Boolean>
<system:Boolean>False</system:Boolean>
</x:Array>
</DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
DataGrid viewmodel:
public class DataGridMainDataContext
{
public DataGridMainDataContext()
{
Collection = new ObservableCollection(new List
{
new UserData
{
UName = "Greg",
IsActive = false,
},
new UserData
{
UName = "Joe",
IsActive = false,
},
new UserData
{
UName = "Iv",
IsActive = false,
}
});
}
public ObservableCollection<UserData> Collection { get; set; }
}
Models:
public class UserData : BaseObservableObject
{
private string _uName;
private object _countryData;
private bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
OnPropertyChanged();
}
}
public string UName
{
get { return _uName; }
set
{
_uName = value;
OnPropertyChanged();
}
}
public object CountryData
{
get { return _countryData; }
set
{
_countryData = value;
OnPropertyChanged();
}
}
}
public class ActivityStatus:BaseObservableObject
{
private bool _boolStatus;
private string _verbalStatus;
public bool BoolStatus
{
get { return _boolStatus; }
set
{
_boolStatus = value;
OnPropertyChanged();
}
}
public string VerbalStatus
{
get { return _verbalStatus; }
set
{
_verbalStatus = value;
OnPropertyChanged();
}
}
}
public class Country : BaseObservableObject
{
private string _countryName;
private string _countryPop;
public string CountryName
{
get { return _countryName; }
set
{
_countryName = value;
OnPropertyChanged();
}
}
public string CountryPop
{
get { return _countryPop; }
set
{
_countryPop = value;
OnPropertyChanged();
}
}
public Country() { }
public Country(string n, string d)
{
this.CountryName = n;
this.CountryPop = d;
}
}
Hope it will help you.
regards,

mvvm observablecollection and binding

I have a model:
public class Table : ViewModelBase
{
private int _id;
private string _north;
private string _east;
private string _south;
private string _west;
public int Id
{
get
{
return _id;
}
set
{
_id = value; OnPropertyChanged();
}
}
public string North
{
get { return _north; }
set { _north = value; OnPropertyChanged();}
}
public string East
{
get { return _east; }
set { _east = value; OnPropertyChanged();}
}
public string South
{
get { return _south; }
set { _south = value; OnPropertyChanged();}
}
public string West
{
get { return _west; }
set { _west = value; OnPropertyChanged();}
}
}
Also ViewModel where tables list declared:
Tables = new ObservableCollection<Table>();
And in XAML:
<ScrollViewer.Resources>
<ItemsPanelTemplate x:Name="MyWrapPanel">
<toolkit:WrapPanel MinWidth="250" Width="{Binding ViewportWidth, ElementName=MyScrollViewer}" />
</ItemsPanelTemplate>
</ScrollViewer.Resources>
<ItemsControl ItemsSource="{Binding Tables}" ItemsPanel="{StaticResource MyWrapPanel}" Grid.RowSpan="2" Grid.Row="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:TableControl
TableNumber="{Binding Id}"
North="{Binding North}"
East="{Binding East}"
South="{Binding South}"
West="{Binding West}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
But when I add elements to Tables list, there is all OK.
But
TableNumber="{Binding Id}"
North="{Binding North}"
East="{Binding East}"
South="{Binding South}"
West="{Binding West}"
are not bound indeed.
In the Xaml file of your user control named TableControl, add DataContext={Binding} in the root element and I think it should then work (if it's not present).
It could also be not working if you have DataContext set inside the code behind of your user control's (e.g.TableControl.xaml.cs) file to something like this.DataContext = this; which makes user control not use the DataContext of Tables collection.
You can use this SO Answer to check what exactly is in the DataContext of your user control TableControl.
Edit:
Try below also, because as per your comment, DataContext for TableControl is conflicting:
<DataTemplate>
<controls:TableControl DataContext="{Binding}"
TableNumber="{Binding Id}"
North="{Binding North}"
East="{Binding East}"
South="{Binding South}"
West="{Binding West}"
/>
</DataTemplate>

How to set the ItemSource of a Datagrid to a collection inside an object using MVVM

I have a ComboBox that holds a list of StrategyViewModels. The StrategyViewModel has an ObservableCollection of StrategyParameterViewModels inside of it. I have a StrategyViewModel called SelectedStrategy that I bound to the SelectedItem property on the combobox. When the user selects a Strategy from the ComboBox I would like to set the itemsource of a datagrid to the StrategyParameters inside that Strategy. I've tried all different ways, but nothing seems to work.
Here's the XAML:
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" ItemsSource="{Binding Strategies}" DisplayMemberPath="Name" SelectedItem="{Binding Path=SelectedStrategy,Mode=TwoWay}" />
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings" ItemsSource="{Binding SelectedStrategy.Parameters, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
Here is the Strategy ViewModel:
public class StrategyViewModel : ViewModelBase
{
public StrategyObject Strategy { get; set; }
public int Id
{
get { return Strategy.Id; }
set
{
if (value == Strategy.Id)
return;
Strategy.Id = value;
OnPropertyChanged("Id");
}
}
public string Name
{
get { return Strategy.Name; }
set
{
if (value == Strategy.Name)
return;
Strategy.Name = value;
OnPropertyChanged("Name");
}
}
public ObservableCollection<StrategyParameterViewModel> Parameters { get { return _parameters; } }
public ObservableCollection<StrategyParameterViewModel> _parameters;
public StrategyViewModel()
{
Strategy = new StrategyObject();
_parameters = new ObservableCollection<StrategyParameterViewModel>();
}
public StrategyViewModel(StrategyObject o, IEnumerable<StrategyParameterObject> pms)
{
Strategy = o;
_parameters = new ObservableCollection<StrategyParameterViewModel>();
foreach (StrategyParameterObject s in pms)
{
_parameters.Add(new StrategyParameterViewModel(s));
}
}
}
And here is the StrategyParameter ViewModel:
public class StrategyParameterViewModel : ViewModelBase
{
public StrategyParameterObject StrategyParameter { get; set; }
public int Id
{
get { return StrategyParameter.Id; }
set
{
if (value == StrategyParameter.Id)
return;
StrategyParameter.Id = value;
OnPropertyChanged("Id");
}
}
public int StrategyId
{
get { return StrategyParameter.StrategyId; }
set
{
if (value == StrategyParameter.StrategyId)
return;
StrategyParameter.StrategyId = value;
OnPropertyChanged("StrategyId");
}
}
public string Name
{
get { return StrategyParameter.Name; }
set
{
if (value == StrategyParameter.Name)
return;
StrategyParameter.Name = value;
OnPropertyChanged("Name");
}
}
public string Value
{
get { return StrategyParameter.Value; }
set
{
if (value == StrategyParameter.Value)
return;
StrategyParameter.Value = value;
OnPropertyChanged("Value");
}
}
public StrategyParameterViewModel()
{
StrategyParameter = new StrategyParameterObject();
}
public StrategyParameterViewModel(StrategyParameterObject o)
{
StrategyParameter = o;
}
}
The problem is that you are trying to set up a two way binding with a read-only property. You need to either add a setter for SelectedStrategy.Parameters, or use a OneWay binding on the datagrid itemssource.
Off the top of my head, I don't think you're doing anything wrong with regards to your XAML.
Data Binding is notoriously tricky. I suggest adding PresentationTraceSources to get more debug information as to what is being found. This will ship several lines of data about binding results to your Output window.
<ComboBox Height="23" Margin="0,12,0,0" Name="cbxStrats" VerticalAlignment="Top" DisplayMemberPath="Name">
<ComboBox.ItemsSource>
<Binding Path="Strategies" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.ItemsSource>
<ComboBox.SelectedItem>
<Binding Path="SelectedStrategy" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</ComboBox.SelectedItem>
</ComboBox>
<DataGrid AutoGenerateColumns="False" Margin="12,97,14,35" Name="dgSettings">
<DataGrid.ItemsSource>
<Binding Path="SelectedStrategy.Parameters" Mode="TwoWay" PresentationTraceSources.TraceLevel="High"/>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridTextColumn Header="Value" Binding="{Binding Path=Value}" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
In addition, WPF Inspector can help, as you can tweak Data Binding expressions in real time when running your actual app. http://wpfinspector.codeplex.com/

Categories

Resources