I get Null Object in MVVM Model with silverlight two way binding - silverlight

I'm new to silverlight and trying to save a form to the database via RIA Services using MVVM Pattern.
I get a textbox value in ViewModel when I bind a textbox to a string in twoway binding mode.
But When I bind a Object.Property to the textbox (Twoway binding) I get a null object in the ViewModel after I click on the save button.
Here is my code, please help me figure out where I am going wrong.
private tblSchool _school;
public tblSchool thisschool
{
get
{
return _school;
}
set
{
if (_school != value)
{
_school = value;
OnPropertyChanged("thisschool");
}
}
}
private void SaveSchool()
{
DomainServiceForDatabaseData service = new DomainServiceForDatabaseData();
service.tblSchools.Add(thisschool); //HERE I GET NULL VALUE
service.SubmitChanges();
}
Here is my XAML:
<Grid x:Name="LayoutRoot"
DataContext="{Binding Source={StaticResource SignUpViewModel}}">
<TextBox Height="23"
HorizontalAlignment="Right"
Margin="0,55,160,0"
Name="textBox1"
VerticalAlignment="Top"
Width="213"
Text="{Binding Path= thisschool.School_Name, Mode=TwoWay}" />

The backing field _school doesn't get initialized in your code sample.
Somewhere you will need to do _school = new tblSchool() or it will stay null forever.

Related

WPF MVVM getting the textbox data to the ViewModel

I have read many questions on this and so far I've not been able to find the answer on this apparently simple issue.
I have a view model, in which is a property. In my XAML I have a TextBox with a binding to that property.
But the property never seems to change.
Here's the textbox:
<TextBox Grid.Row="1"
Grid.Column="0"
Margin="4"
Text="{Binding CharNameFromTB}" />
And the relevant code behind for the ViewModel:
private String _charNameFromTB;
String CharNameFromTB
{
get { return _charNameFromTB; }
set
{
if (!string.Equals(this._charNameFromTB, value))
{
this._charNameFromTB = value;
RaisePropertyChanged("CharNameFromTB");
}
}
}
I have put a break point on the if statement in the setter, but it never triggers. Have I missed something obvious out? I tried setting the binding mode to twoway but that didn't change anything.
It's driving me a little mad. Any help would be appreciated!
You should make the property public in order to be able to bind to it:
private String _charNameFromTB;
public String CharNameFromTB
{
get { return _charNameFromTB; }
set
{
this._charNameFromTB = value;
RaisePropertyChanged("CharNameFromTB");
}
}
Also make sure that you have set the DataContext of the TextBox or any of its parent elements to an instance of your view model class where the CharNameFromTB property is defined.
Also note that by default, the source property is set when the TextBox loses focus.
If you want to update the source property on each keystroke you should set the UpdateSourceTrigger property of the Binding to PropertyChanged:
<TextBox Grid.Row="1"
Grid.Column="0"
Margin="4"
Text="{Binding CharNameFromTB, UpdateSourceTrigger=PropertyChanged}" />

Binding Single Item of a Collection

I'm just learning the basic concepts of WPF and XAML coming from a C++ background, so some of it is a bit alien to me. I am using Expression Blend to help me get to grips with XAML.
I am making a basic app that displays records in a simple XML data source:
<photos>
<photo>
<image>Assets\Item01.png</image>
<description>Strawberry</description>
</photo>
<photo>
<image>Assets\Item02.png</image>
<description>Orange</description>
</photo>
<photo>
<image>Assets\Item03.png</image>
<description>Pineapple</description>
</photo>
...
</photos>
I have bound this data 'photoDataSource' to a grid and stuck some textboxes and image fields that display the first record. In XAML:
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource photoDataSource}}" Margin="0,0,0,1" Background="#FF1D1D1D">
<Image Height="104" Width="104" Source="{Binding XPath=/photos/photo/image}" Margin="8,62,0,0" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox Height="23" Margin="8,8,6,0" TextWrapping="Wrap" Text="{Binding XPath=/photos/photo/description}" VerticalAlignment="Top"/>
<TextBox Height="23" Margin="8,35,6,0" TextWrapping="Wrap" Text="{Binding XPath=/photos/photo/image}" VerticalAlignment="Top"/>
<Button Content="Next Product" Margin="213,97,297,0" Height="44" VerticalAlignment="Top"/>
</Grid>
This displays two textboxes containing "Strawberry" and "Assets\Item01.png" respectively, along with the image and a Button Containing the text "Next Product". As you can see I have bound the collection "photoDataSource" to the parent Grid. When run it displays the first item in the collection.
How can I trigger the button to display the next item in the collection (and loop) at runtime?
I am not intending to do this with any code-behind as I am not changing any of the data itself, just which item is displayed. But perhaps I am going about this in the wrong way?
Ideally after this example I will want to remove the button completely and change records automatically after a storyboard animation has completed (using the trigger 'StoryboardCompletedTrigger').
Quite right not wanting to use code behind. However I would recommend implementing a ViewModel against your Window to get what you want achieved.
In your view model you should have an ObservableCollection of your Photo object and another property to specify a single Photo being called SelectedPhoto as shown below:
public ObservableCollection<Photo> MyPhotos {
get { return _photos; }
set { _photos = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Photos"));
}
}
public Photo SelectedPhoto {
get { return _photo; }
set { _photo = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SelectedPhoto"));
}
}
Then use XmlSerialization to load your Xml into the ObservableCollection. Then create your buttons to move next and previous to bind to an ICommand (also in your ViewModel) to cycle up or down the MyPhotos collection setting SelectedPhoto each time.
Then you can bind and Image in your Xaml as follows.
<Image Source="{Binding Source={StaticResource myViewModel}, Path=SelectedPhoto.Image}"/>
I hope this makes some sense for you and has been of some help.

WPF Binding DataTable Row Column to textbox

NARROWED DOWN SOLUTION
I'm much closer, but don't know how to apply XAML to change datacontext value. Please review context of original question below as may be needed.
My issue is that I have a ViewModel class as the datacontext to a window. On this view model, I have a "DataTable" object (with columns and just a single row for testing). When I try to set a Textbox "TEXT" binding to the column of the datatable, it doesn't work. What I've ultimately found is that no matter what "source" or "path" I give it, it just won't cooperate. HOWEVER, just by playing around with scenarios, I said the heck with it. Lets look. The Textbox control has its own "DataContext" property. So, in code, I just FORCED the textbox.DataContext = "MyViewModel.MyDataTableObject" and left the path to just the column it should represent "MyDataColumn", and it worked.
So, that said, how would I write the XAML for the textbox control so it's "DataContext" property is set to that of the datatable object of the view model the window but can't get that correct. Ex:
<TextBox Name="myTextBox"
Width="120"
DataContext="THIS IS WHAT I NEED" --- to represent
Text="{Binding Path=DataName,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
DataContext for this textbox should reflect XAML details below and get
(ActualWindow) ( DDT = View Model) (oPerson = DataTable that exists ON the view model)
CurrentWindow.DDT.oPerson
I'm stuck on something with binding. I want to bind a column of a datatable to a textbox control. Sounds simple, but I'm missing something. Simple scenario first. If I have my window and set the data context to that of "MyDataTable", and have the textbox PATH=MyDataColumn, all works fine, no problems, including data validation (red border on errors).
Now, the problem. If I this have a same "MyDataTable" as a public on my Window Class directly (but same thing if I had it on an actual ViewModel object, but the window to simplify the level referencing), I can't get it to work from direct XAML source. I knew I had to set the "SOURCE=MyDataTable", but the path of just the column didn't work.
<TextBox Name="myTextBox"
Text="{Binding Source=DDT, Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
However, from other testing, if I set the path (in code-behind) to
object txt = FindName("myTextBox");
Binding oBind = new Binding("DataName");
oBind.Source = DDT;
oBind.Mode = BindingMode.TwoWay;
oBind.ValidatesOnDataErrors = true;
oBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
((TextBox)txt).SetBinding(TextBox.TextProperty, oBind);
It DOES work (when the datatable is available as public in the window (or view model))
What am I missing otherwise.
UPDATE: HERE IS A FULL POST of the sample code I'm applying here.
using System.ComponentModel;
using System.Data;
namespace WPFSample1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public DerivedDataTable DDT;
public MainWindow()
{
InitializeComponent();
// hook up to a Data Table
DDT = new DerivedDataTable();
DataContext = this;
// with THIS part enabled, the binding works.
// DISABLE this IF test, and binding does NOT.
// but also note, I tried these same settings manually via XAML.
object txt = FindName("myTextBox");
if( txt is TextBox)
{
Binding oBind = new Binding("DataName");
oBind.Source = DDT;
oBind.Mode = BindingMode.TwoWay;
oBind.ValidatesOnDataErrors = true;
oBind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
((TextBox)txt).SetBinding(TextBox.TextProperty, oBind);
}
}
}
// Generic class with hooks to enable error trapping at the data table
// level via ColumnChanged event vs IDataErrorInfo of individual properties
public class MyDataTable : DataTable
{
public MyDataTable()
{
// hook to column changing
ColumnChanged += MyDataColumnChanged;
}
protected void MyDataColumnChanged(object sender, DataColumnChangeEventArgs e)
{ ValidationTest( e.Row, e.Column.ColumnName); }
// For any derived datatable to just need to define the validation method
protected virtual string ValidationTest(DataRow oDR, string ColumnName)
{ return ""; }
}
public class DerivedDataTable : MyDataTable
{
public DerivedDataTable()
{
// simple data table, one column, one row and defaulting the value to "X"
// so when the window starts, I KNOW its properly bound when the form shows
// "X" initial value when form starts
Columns.Add( new DataColumn("DataName", typeof(System.String)) );
Columns["DataName"].DefaultValue = "X";
// Add a new row to the table
Rows.Add(NewRow());
}
protected override string ValidationTest(DataRow oDR, string ColumnName)
{
string error = "";
switch (ColumnName.ToLower())
{
case "dataname" :
if ( string.IsNullOrEmpty(oDR[ColumnName].ToString() )
|| oDR[ColumnName].ToString().Length < 4 )
error = "Name Minimum 4 characters";
break;
}
// the datarow "SetColumnError" is what hooks the "HasErrors" validation
// in similar fashion as IDataErrorInfo.
oDR.SetColumnError(Columns[ColumnName], error);
return error;
}
}
}
AND here's the XAML. Any brand new form and this is the only control in the default "grid" of the window.
Tried following versions, just defining the Rows[0][Column]
<TextBox Name="myTextBox"
Width="120"
Text="{Binding Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
Including the source of "DDT" since it is public to the window
<TextBox Name="myTextBox"
Width="120"
Text="{Binding Source=DDT, Path=Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
And even suggestions offered by grantnz
I think your xaml is setting the source to the string "DDT" when you're expecting it to be the property DDT on the current window.
Do you see an error in the output window of Visual Studio like:
System.Windows.Data Error: 40 : BindingExpression path error:
'Rows' property not found on 'object' ''String' (HashCode=1130459074)'.
BindingExpression:Path=Rows[0][DataName]; DataItem='String' (HashCode=1130459074);
target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
If you set the window DataContext to this (from code DataContext = this; or xaml), you can use:
Text="{Binding Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
or you can leave the DataContext as null and use:
<TextBox Name="myTextBox"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}},Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
The above assumes that you are setting the DDT property before the binding is set-up. If DDT is set after the binding is configured, you'll need to implement INotifyPropertyChanged.
Here's the source of a working version (with DataContext set from XAML and INotifyPropertyChanged implemented). It doesn't work if you comment out the line
OnPropertyChanged(new PropertyChangedEventArgs("DDT"));
and the second TextBox is bound if you leave out the following out of the XAML
DataContext="{Binding RelativeSource={RelativeSource Self}}"
CODE
public partial class MainWindow : Window, INotifyPropertyChanged
{
public DataTable DDT { get; set; }
public String SP { get; set; }
public MainWindow()
{
InitializeComponent();
DDT = new DerivedDataTable();
OnPropertyChanged(new PropertyChangedEventArgs("DDT"));
SP = "String prop";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
XAML
<Window x:Class="BindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<TextBox
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
<TextBox
Text="{Binding Path=DDT.Rows[0][DataName],
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }" />
<TextBox
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=SP}" />
</StackPanel>
</Window>
SOLVED, but what a PITA... Most things within the samples of doing MVVM patterns will have properties on the view model exposing whatever you want to hook into. When dealing with binding to a DATATABLE (or similar view, etc), you are binding to COLUMNs of said table (or view).
When a table is queried from whatever back-end, the schema populating the data columns will always force the column names to UPPER CASE.
So, if you have a column "InvoiceTotal" in your table, when queried, the column name will have it as "INVOICETOTAL".
If you try to bind to the
Path="InvoiceTotal" ... it will fail
Path="INVOICETOTAL" ... it WILL WORK
However, if you are working directly in .Net (I use C#), the following will BOTH get a value back from the row
double SomeValue = (double)MyTable.Rows[0]["InvoiceTotal"];
or
double SomeValue = (double)MyTable.Rows[0]["INVOICETotal"];
or
double SomeValue = (double)MyTable.Rows[0]["invoicetotal"];
all regardless of the case-sensitivity of the column name.
So, now the rest of the bindings, Error triggers available at the table, row or column levels can properly be reflected in the GUI to the user.
I SURE HOPE this saves someone else the headaches and research I have gone through on this....

MVVM - ListBox SelectedItem Binding Property Going Null

So i have a listbox:
<ListBox x:Name="listbox" HorizontalAlignment="Left" Margin="8,8,0,8" Width="272" BorderBrush="{x:Null}" Background="{x:Null}" Foreground="{x:Null}" ItemsSource="{Binding MenuItems}" ItemTemplate="{DynamicResource MenuItemsTemplate}" SelectionChanged="ListBox_SelectionChanged" SelectedItem="{Binding SelectedItem}">
</ListBox>
and i have this included in my viewmodel:
public ObservableCollection<MenuItem> MenuItems
{
get
{
return menuitems;
}
set
{
menuitems = value;
NotifyPropertyChanged("MenuItems");
}
}
public MenuItem SelectedItem
{
get
{
return selecteditem;
}
set
{
selecteditem = value;
NotifyPropertyChanged("SelectedItem");
}
}
and also in my viewmodel:
public void UpdateStyle()
{
ActiveHighlight = SelectedItem.HighlightColor;
ActiveShadow = SelectedItem.ShadowColor;
}
So, the objective is to call UpdateStyle() whenever selectedchanged event is fired. So in the .CS file, i call UpdateStyle().
The problem is, whenever I get into the selectionchanged event method, my ViewModel.SelectedItem is always null.
I tried debugging this to see if the binding was working correctly, and it is. When I click on an item in the listbox, the SelectedItem Set is triggered, setting the value... but somewhere inbetween that and the selected changed (In the CS File) It gets reset to Null.
Can anyone help out?
Thanks
Edit:
I thought I might shed a little more light.
1. Click on an item in the list
2. SelectedItem.Set gets triggered, ViewModel.SeletedItem gets set correctly.
3. Enter the OnSelectionChanged Event in the .CS file.
4. Enter ViewModel.UpdateStyle()
5. SelectedItem Throws a Null Exception.
Wow, found a strange issue:
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MainViewModelDataSource}}" d:DataContext="{d:DesignData /SampleData/MainViewModelSampleData.xaml}">
That code is generated by Expression Blend - and it was causing the issue. I erased all generated binding and just made a this.datacontext a new VM in the constructor of the XAML... now its working.
Thanks anyway, guys.
Look to see if your backing property (selecteditem) is getting set to NULL somewhere in your code.

Silverlight bind collection to Combobox in DataForm using MVVM

I have this problem, I've got Silverlight app written using MVVM. I need to create DataForm which is binded to property on ViewModel and I want to add ComboBox and fill it with values from other collection in the same ViewModel.
Code:
<dataFormToolkit:DataForm CurrentItem="{Binding NewUser, Mode=TwoWay}" AutoGenerateFields="False" Height="298">
<dataFormToolkit:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dataFormToolkit:DataField Label="Email">
<TextBox Text="{Binding Email, Mode=TwoWay}"/>
</dataFormToolkit:DataField>
<dataFormToolkit:DataField Label="Język">
<ComboBox ItemsSource="{Binding Path=Languages, Mode=TwoWay}"/>
</dataFormToolkit:DataField>
</StackPanel>
</DataTemplate>
</dataFormToolkit:DataForm.EditTemplate>
</dataFormToolkit:DataForm>
All this is handled by NewAccountVM which has these properties:
private User newUser;
public User NewUser {
get
{
return newUser;
}
set
{
if (value != newUser)
{
newUser = value;
RaisePropertyChanged("NewUser");
}
}
}
private ObservableCollection<Language> languages;
public ObservableCollection<Language> Languages
{
get { return languages; }
set
{
if (languages != value)
{
languages = value;
RaisePropertyChanged("Languages");
}
}
}
Now, all this works besides adding ItemsSource to ComboBox. I've found many examples showing how fill CB in CodeBehind, but like I said I want to do this in MVVM-Style :)
I understand that, ComboBox inherited DataContext from DataForm, and this ItemsSource="{Binding Path=Languages, Mode=TwoWay}" will not work, but I have no idea how to achieve my goal.
Can somebody help me?
1) Declare the viewmodel to the view in the resources section.
<UserControl.Resources>
<local:MyViewModel x:Key="myViewModel" />
</UserControl.Resources>
2) Bind the ComboBox to the collection property on the viewmodel.
<ComboBox ItemsSource="{Binding Path=Languages,
Source={StaticResource myViewModel},
Mode=TwoWay}"/>
you can set the Data Context in XAML to your static resource like so:
<UserControl.DataContext>
<Binding Source="{StaticResource myViewModel}" />
</UserControl.DataContext>
Scenario A:
1. Assume you wish to populate a combo with all the membership Roles, and allow the client to select the role and assign to the User :
i.e. ObjectA : Aspnet_Role
i.e. ObjectB : User
Let us say User.MembershipRoleId is to be bound to Aspnet_Role.RoleId
Dataform is bound to ObjectB
Combobox in dataform is populated with List
In XAML write the following:
<Combobox DisplayMemberPath="RoleName"
SelectedValue="{Binding MembershipRoleId,Mode=TwoWay}" SelectedValuePath="RoleId" />
here the mapping is, ObjectB.MembershipRoleId=ObjectA.RoleId
Scenario B:
1. If you do not want to explicitly define by the way in ScenarioA, then in that case, define a ForeignKey-PrimaryKey relationship between the tables in the database like
ForeignKey -> User.MembershipId
PrimaryKey -> Aspnet_Roles.RoleId
2. From the ADO.NET (.edmx) file, update the model from the database, you will observe that in the User entity there is an association made upon entity Aspnet_Roles
3. In XAML write the code as below to bind the combobox, to the desired field of the Dataform
<Combobox DisplayMemberPath="RoleName" SelectedItem="{Binding MembershipRoleId,Mode=TwoWay}" .... />

Resources