Entity Framework Field Is Required (MVVM) - c#

I have been having this issue and have been trying to figure out for the past few days, I set up a second test project as my first project is too big to try and nail this down. I'm still fairly new to mvvm so I'm still learning. Whenever I try to save a field using Entity it starts complaining the FirstName field is required, I sort of nailed down the issue to being apart of something with the button because whenever I move the button into the same UserControl as the textbox it will save. The TabControl in the MainWindow has a UserControl for each tab and each one has their own respective ViewModel and then the TabControl itself has a ViewModel.
MainWindow
Just contains the button and the tab control
<Window.Resources>
<vm:TabControlViewModel x:Key="tab"/>
</Window.Resources>
<Grid DataContext="{StaticResource tab}">
<TabControl Margin="10"
Width="500"
Height="500">
<TabItem Header="Test Tab 1">
<custom:TabOneUserControl/>
</TabItem>
<TabItem Header="Test Tab 2">
<custom:TabTwoUserControl/>
</TabItem>
</TabControl>
<Button Content="Save"
Width="120"
Height="50"
Margin="1114,604,41,38.5"
Command="{Binding SaveCommand}"/>
</Grid>
TabOneUserControl
Contains a textbox and label
<UserControl.Resources>
<vm:TabOneUserControlViewModel x:Key="vm"/>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<StackPanel VerticalAlignment="Center">
<Label Content="First Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</UserControl>
TabTwoUserControl
Contains a textbox and label
<UserControl.Resources>
<vm:TabTwoUserControlViewModel x:Key="vm"/>
</UserControl.Resources>
<Grid DataContext="{StaticResource vm}">
<StackPanel VerticalAlignment="Center">
<Label Content="Last Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>
</UserControl>
TabControlViewModel
For the tab control
public class TabControlViewModel
{
public SaveCommand SaveCommand { get; set; }
private TabOneUserControlViewModel tabOneUserControl;
private TabTwoUserControlViewModel tabTwoUserControl;
public TabControlViewModel()
{
tabOneUserControl = new TabOneUserControlViewModel();
tabTwoUserControl = new TabTwoUserControlViewModel();
SaveCommand = new SaveCommand(this);
}
public void SaveInformation()
{
using (TestDbEntities test = new TestDbEntities())
{
test.FNs.Add(new FN
{
FirstName = tabOneUserControl.FirstName
});
test.LNs.Add(new LN
{
LastName = tabTwoUserControl.LastName
});
try
{
test.SaveChanges();
Debug.Print("SAVED CHANGES!");
}
catch (DbEntityValidationException ex)
{
foreach (var validationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation(
"Class: {0}, Property: {1}, Error: {2}",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage);
}
}
}
}
}
}
TabOneUserControlViewModel
For the first tab user control
public class TabOneUserControlViewModel : INotifyPropertyChanged
{
private string firstName;
public event PropertyChangedEventHandler PropertyChanged;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
OnPropertyChanged("FirstName");
}
}
public TabOneUserControlViewModel()
{
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
TabTwoUserControlViewModel
For the second tab user control
public class TabTwoUserControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
OnPropertyChanged("LastName");
}
}
public TabTwoUserControlViewModel()
{
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}

Thanks to everyone in the comments for your suggestions, Nawed's comment nailed it perfectly for what I was doing wrong. Here is the final code changes I made:
TabControlViewModel
Removed the fields and made them into properties
public class TabControlViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SaveCommand SaveCommand { get; set; }
public TabOneUserControlViewModel TabOneUserControl { get; set; }
public TabTwoUserControlViewModel TabTwoUserControl { get; set; }
public TabControlViewModel()
{
TabOneUserControl = new TabOneUserControlViewModel();
TabTwoUserControl = new TabTwoUserControlViewModel();
SaveCommand = new SaveCommand(this);
}
public void SaveInformation()
{
using (TestDbEntities test = new TestDbEntities())
{
test.FNs.Add(new FN
{
FirstName = TabOneUserControl.FirstName
});
test.LNs.Add(new LN
{
LastName = TabTwoUserControl.LastName
});
try
{
test.SaveChanges();
Debug.Print("SAVED CHANGES!");
}
catch (DbEntityValidationException ex)
{
foreach (var validationErrors in ex.EntityValidationErrors)
{
foreach (var validationError in validationErrors.ValidationErrors)
{
Trace.TraceInformation(
"Class: {0}, Property: {1}, Error: {2}",
validationErrors.Entry.Entity.GetType().FullName,
validationError.PropertyName,
validationError.ErrorMessage);
}
}
}
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
TabOneUserControl
Removed the static resource from the grid and replaced it with a regular binding and then changed the binding in the Text element, this was the same thing I did in the second user control so I will negate posting the second UserControl:
<Grid DataContext="{Binding TabOneUserControl}">
<StackPanel VerticalAlignment="Center">
<Label Content="First Name"
HorizontalAlignment="Center"/>
<TextBox Width="250"
Height="50"
Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Grid>

Related

Binding DataContext Window to ViewModel

Ok, I tried it several ways, but none worked as it should be in my case. I have a simple Window with a single ComboBox. I am changing the code to MVVM, so now everything is still in the Code-Behind and should go to a ViewModel, etc.
But even on the first step (binding the ViewModel to the View/Window) I don't seem to be able to bind them together.
My Window XAML:
<Window x:Class="CustomerGuidance.ClientWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:CustomerGuidance.ViewModels"
Title="Stop'n'Go - Client" Height="22" Width="229"
Loaded="ClientWindow_OnLoaded" WindowStyle="None"
WindowStartupLocation="Manual" Top="0" Left="0"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True">
<Window.DataContext>
<VM:EmployeeViewModel />
</Window.DataContext>
<Canvas Background="Gainsboro">
<ComboBox Name="EmployeesComboBox"
ItemsSource="{Binding EmployeeEntries}"
Width="192" FontFamily="Arial" FontSize="14">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Lastname}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Surname}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Canvas>
The ViewModel looks like this:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace CustomerGuidance.ViewModels
{
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
}
public static ObservableCollection<ServerWindow.EmployeeEntry> EmployeeEntries { get; set; } = new ObservableCollection<ServerWindow.EmployeeEntry>();
private string _surname;
private string _lastname;
private int _id;
public string Surname
{
get { return _surname; }
set
{
if (_surname == value)
return;
_surname = value;
NotifyPropertyChanged("Surname");
}
}
public string Lastname
{
get { return _lastname; }
set
{
if (_lastname == value)
return;
_lastname = value;
NotifyPropertyChanged("Lastname");
}
}
public int Id
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
NotifyPropertyChanged("Id");
}
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I get the following error message: "The Name "EmployeeViewModel" is not available in the namespace "clr-namespace:CustomerGuidance.ViewModels".And now the question: What am I missing? How can I bind the ViewModel to my window-XAML?
You should build your code for the errors to disappear.
It's because the namespace is not yet available in the assembly the designer relies on (your program) before it has been built.

How to bind to a collection inside my ViewModel

One of the proprties in my ViewModel is a collection of objects. I cant figure it out how to bind my inner DataTemplate to the properties of these objects inside the collection: The problematic binding is in TestDataTemplate1, it just cannot find N and STR.. Here is my XAML
<Page.Resources>
<DataTemplate x:Key="TestDataTemplate1">
<Grid Background="Cornsilk" HorizontalAlignment="Stretch" Height="Auto">
<TextBlock x:Name="LeftTB" Text="{Binding N}" Foreground="Red" HorizontalAlignment="Left" VerticalAlignment="Stretch"/>
<TextBlock x:Name="RightTB" Text="{Binding STR}" Foreground="Green" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TestDataTemplate">
<Grid Background="Yellow" HorizontalAlignment="Stretch" Height="Auto">
<TextBlock x:Name="LeftTB" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<ListBox x:Name="MyListBox" x:FieldModifier="Public"
ItemsSource="{Binding SampleCollection}"
ItemTemplate="{StaticResource TestDataTemplate1}"
Background="Blue"
HorizontalAlignment="Center"
VerticalAlignment="Stretch" Width="200">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<TextBlock x:Name="RightTB" Text="{Binding Age}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Page.Resources>
This is my Type that contains these properties:
class AlaBalaVM : INotifyPropertyChanged
{
AlaBala _ab = null;
public AlaBalaVM(AlaBala ab)
{
_ab = ab;
}
public string N
{
get
{
return "num: " + _ab._n.ToString();
}
set { RaisePropertyChanged("N"); }
}
public string STR
{
get
{
return _ab._str.ToString() + " string";
}
set { RaisePropertyChanged("STR"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And this is my ViewModel in which I fill the DataSource collection:
public class PersonVMWrapper : INotifyPropertyChanged
{
PersonModel _pm = null;
public PersonVMWrapper(PersonModel pm)
{
_pm = pm;
}
public string Name
{
get
{
return "mr." + _pm.Name;
}
set { RaisePropertyChanged("Name"); }
}
public string Age
{
get
{
return _pm.Age.ToString() + " years";
}
set { RaisePropertyChanged("Age"); }
}
public ObservableCollection<AlaBala> SampleCollection
{
get
{
return _pm.SampleCollection;
}
set { RaisePropertyChanged("SampleCollection"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class PersonVM : INotifyPropertyChanged
{
DispatcherTimer _refreshTimer = new DispatcherTimer();
private ObservableCollection<PersonVMWrapper> personDataSource;
public PersonModel PM { get; set; }
public PersonVM()
{
AddItemToDataSource(null);
}
private void AddItemToDataSource(object o)
{
DataSource.Add(new PersonVMWrapper(new PersonModel() { Name = "asdasd", Age = 23, SampleCollection = new ObservableCollection<AlaBala> { new AlaBala(3, "e1"), new AlaBala(4, "e2") } }));
}
public ObservableCollection<PersonVMWrapper> DataSource
{
get
{
if (this.personDataSource == null)
{
this.personDataSource = new ObservableCollection<PersonVMWrapper>();
}
return this.personDataSource;
}
set
{
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You haven't posted the definition of the "Alabala" class -- does it contain the "N" and "STR" properties? As #KooKiz pointed out, it looks like you meant to make "SampleCollection" an ObservableCollection<AlaBalaVM>, ie:
SampleCollection = new ObservableCollection<AlaBalaVM>
{
new AlaBalaVM(new AlaBala(3, "e1")),
new AlaBalaVM(new AlaBala(4, "e2")),
}

Label Binding does not work

I am trying to bind a label to an Object.Object.Property and I don't get it run.
Here is my code:
XAML
<Window x:Class="MyApp.MyWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyWindow" Height="1120" Width="800">
<Grid Name="MyGrid">
<StackPanel Orientation="Horizontal">
<Label FontWeight="Bold" FontSize="40" Content="{Binding MyDataObject/AnotherSubObject/MyProperty}"/>
</StackPanel>
</Grid>
</Window>
And the Code:
public partial class MyWindow : Window
{
public MySubObject MyDataObject { get; set; }
public MyWindow(MySubObject object)
{
InitializeComponent();
this.MyDataObject = object; // Contains MyDataObject.AnotherObject.MyProperty
DataContext = this;
}
}
And the code for MySubObject object looks like this:
public class MySubObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
AnotherObject _AnotherObject;
public MySubObject()
{
this._AnotherObject = new AnotherObject();
this._AnotherObject.Property = "Some Value";
}
public AnotherObject AnotherObject
{
get { return _AnotherObject; }
set { _AnotherObject = value; OnPropertyChanged("AnotherObject"); }
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I would be glad to get dome support for this case.
Use Dot(.) as binding property path separator not Forward slash(/)
<Label FontWeight="Bold" FontSize="40"
Content="{Binding MyDataObject.AnotherSubObject.MyProperty}"/>

WPF Binding 2 Columns to ListView

XAML:
<ListBox x:Name="MyTitleBox" ItemsSource="{Binding}"/>
<ListBox x:Name="MyInfoBox" ItemsSource="{Binding}"/>
In C#:
MyTitleBox.ItemsSource = new List<string>(new string[] {"ID:", "Info:", "More Info:"});
MyInfoBox.ItemsSource = new ObservableCollection<string>(MyMainInfo.ToString().Split(',').ToList<string>());
I currently have 2 list boxes next to each other because I need to handle their ItemsSource programmatically.
I know there must be a better way to merge the two. Essentially the list box "on the left" is the titles like ID: and the list box "on the right" is the information.
I thought I could do something like MyTitleBox.Columns.Add like I've seen but it won't let me do .Columns. I'm using .NET 4.
Here is an example with a more MVVM approach:
Domain (The type of items you want to put in your list):
public class Movie : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title;
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
RaisePropertyChanged("Title");
}
}
}
private string _info;
public string Info
{
get { return _info; }
set
{
if (_info != value)
{
_info = value;
RaisePropertyChanged("Info");
}
}
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Movie> _movies;
/// <summary>
/// Collection of movies.
/// </summary>
public ObservableCollection<Movie> Movies
{
get { return _movies; }
set
{
if (_movies != value)
{
_movies = value;
RaisePropertyChanged("Movies");
}
}
}
/// <summary>
/// Constructor
/// </summary>
public MyViewModel()
{
Movies = new ObservableCollection<Movie>();
Movies.Add(new Movie() { Title = "Gravity", Info = "Gravity is about..." });
Movies.Add(new Movie() { Title = "Avatar", Info = "Avatar is about..." });
}
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:StackOverflow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Movies}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Title}" /><Run Text=" - " /><Run Text="{Binding Info}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Grid.Row="1" Content="Click To Change Info" Margin="5" Click="Button_Click" />
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MyViewModel ViewModel { get; private set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MyViewModel();
DataContext = ViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Movie movie = ViewModel.Movies.FirstOrDefault();
if (movie != null)
{
movie.Info = "This is the new information";
}
}
}
Implementing INotifyPropertyChanged allows the code to notify the UI when something changes.
If you test this code out you will see that clicking the button updates the info for the first movie, and this changed is immediately reflected in the UI. (Normally you would use a convention like Commands for handling the button click, but for simplicity I did it this way)
Let me know if you have any questions.

windows 8 app, obserablecollection viewmodel gives xaml error: no such table

Hi I have a problem with binding my data from a collection view model in xaml.
But databinding fails, VisualStudio reports error "no such table: Recipe"
Can any one help me with this problem?
The XAML:
<Page.DataContext>
<vm:RecipeListViewModel />
</Page.DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView Header="Recipes" x:Name="RecipeList" HorizontalAlignment="Left" Height="512" Margin="156,118,0,0" VerticalAlignment="Top" Width="430" DataContext="{Binding Recipes[0].Recipe_Name}" ItemsSource="{Binding RecipeCollection.Recipe}" />
<Image x:Name="Img" HorizontalAlignment="Left" Height="260" Margin="698,116,0,0" VerticalAlignment="Top" Width="462"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="698,420,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
</Grid>
The RecipeListViewModel
class RecipeListViewModel : INotifyPropertyChanged
{
ObservableCollection<Recipe> _Recipes;
public ObservableCollection<Recipe> Recipes
{
get { return _Recipes; }
set
{
if (_Recipes != value)
{
PropertyChanged(this, new PropertyChangedEventArgs("Recipes"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public RecipeListViewModel()
{
_Recipes = new ObservableCollection<Recipe>();
var dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "LCHFData.db");
using (var db = new SQLite.SQLiteConnection(dbPath))
{
var tb = db.Table<Recipe>().ToList<Recipe>();
foreach (Recipe recipe in tb)
{
_Recipes.Add(recipe);
}
}
}
}
The RecipeViewModel:
class RecipeViewModel : INotifyPropertyChanged{
/*
* Class to Display whole recipe object
*/
Recipe _Recipe;
public Recipe Recipe
{
set
{
if (_Recipe != value)
{
_Recipe = value;
PropertyChanged(this, new PropertyChangedEventArgs("Recipe"));
}
}
get { return new Recipe() { Recipe_Name = "boo "}; }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}

Categories