Bind enum into ViewModel using C# WPF - c#

I have an enum class where it has few statuses. I want to bind my enum to the combobox and once the save button is click it should save into the database by using mvvm pattern. For now, i am able to populate the enum statuses into the combobox, but can i bind it to the view model? And how can i save into the database from the enum.
Here is the xaml code:
xmlns:enum="clr-namespace:application.Enum"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Home" Height="450" Width="700">
<Window.DataContext>
<vm:ProductionLineConfigViewModel/>
</Window.DataContext>
<Window.Resources>
<ObjectDataProvider x:Key="dataFromEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="enum:Status"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<ComboBox x:Name="combobox_status" Grid.Column="2" Grid.Row="3" Margin="5.8,41.8,43.8,0" VerticalAlignment="Top" SelectionChanged="combobox_status_SelectionChanged"
ItemsSource="{Binding Source={StaticResource dataFromEnum}}" SelectedItem="{Binding ProductionLineStatus}" SelectedValue="{Binding ProductionLineStatus, Mode=TwoWay}" SelectedValuePath="ProductionLineStatus"/>
<Button Grid.Column="1" Grid.Row="5" Content="Back" Margin="24.8,7,24.8,42.6" x:Name="btnBack" Click="btnBack_Click"/>
<Button Grid.Column="2" Grid.Row="5" Content="Create" Margin="24.8,7,24.8,42.6" x:Name="btnCreate" Click="btnCreate_Click" Command="{Binding NewProductionLineConfigCommand}"/>
</Grid>
And this is the error message I am getting now:
System.Windows.Data Error: 40 : BindingExpression path error:
'ProductionLineStatus' property not found on 'object' ''Status'
(HashCode=0)'. BindingExpression:Path=ProductionLineStatus;
DataItem='Status' (HashCode=0); target element is 'ComboBox'
(Name='combobox_status'); target property is 'NoTarget' (type
'Object')
Here is the viewmode:
public class ProductionLineConfigViewModel : INotifyPropertyChanged
{
Database db = new Database();
MySqlDataReader reader;
MySqlDataAdapter da;
DataTable dt = new DataTable();
private ProductionLineConfig productionlineconfig;
public ProductionLineConfig ProductionLineConfigs
{
get { return productionlineconfig; }
set
{
productionlineconfig = value;
OnPropertyChanged("ProductionLineConfigs");
}
}
// TODO - List all productionline configs; Implement observablecollections
public List<ProductionLineConfig> listAllProductionLineConfigs
{
get
{
var plc = new List<ProductionLineConfig>();
string query;
query = "select * from productionlineconfig";
da = new MySqlDataAdapter(query, db.GetConnection());
da.Fill(dt);
reader = db.QueryCommand(query);
while (reader.Read())
{
plc.Add(new ProductionLineConfig()
{
ProductionLineId = Int32.Parse(reader[0].ToString()),
ProductLineCode = reader[1].ToString(),
ProductionLineName = reader[2].ToString(),
ProductionLineStatus = Convert.ToBoolean(reader[3].ToString()),
ProductionLineCreatedDate = Convert.ToDateTime(reader[4].ToString())
});
}
reader.Close();
return plc;
}
}
// TODO - Create new productionline config;
public void createNewProductionLineConfig()
{
string query;
try
{
query = "Insert into productionlineconfig (PRODUCTION_LINE_CODE, PRODUCTION_LINE_NAME, PRODUCTION_LINE_STATUS) Values ('" + ProductionLineConfigs.ProductLineCode + "' , '" + ProductionLineConfigs.ProductionLineName + "' , '" + ProductionLineConfigs.ProductionLineStatus + "')";
db.QueryCommand(query);
Console.WriteLine("User created successfully");
production_line_config plcWindow = new production_line_config();
plcWindow.Hide();
npi_home npiWindow = new npi_home();
npiWindow.Show();
}
catch (MySqlException ex)
{
Console.WriteLine(ex.ToString());
}
}
public NewProductionLineConfigCommand newProductionLineConfigCommand { get; set; }
public ProductionLineConfigViewModel()
{
ProductionLineConfigs = new ProductionLineConfig();
newProductionLineConfigCommand = new NewProductionLineConfigCommand(this);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the model code:
public class ProductionLineConfig : INotifyPropertyChanged
{
private int id;
public int ProductionLineId
{
get { return id; }
set
{
id = value;
OnPropertyChanged("ProductionLineId");
}
}
private string linecode;
public string ProductLineCode
{
get { return linecode; }
set
{
linecode = value;
OnPropertyChanged("ProductLineCode");
}
}
private string linename;
public string ProductionLineName
{
get { return linename; }
set
{
linename = value;
OnPropertyChanged("ProductionLineName");
}
}
private bool status;
public bool ProductionLineStatus
{
get { return status; }
set
{
status = value;
OnPropertyChanged("ProductionLineStatus");
}
}
private DateTime createddate;
public DateTime ProductionLineCreatedDate
{
get { return createddate; }
set
{
createddate = value;
OnPropertyChanged("ProductionLineCreatedDate");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

This helped to solve my problem
<ComboBox x:Name="combobox_status" Grid.Column="2" Grid.Row="3" Margin="5.8,41.8,43.8,0" VerticalAlignment="Top" SelectionChanged="combobox_status_SelectionChanged"
ItemsSource="{Binding Source={StaticResource dataFromEnum}}" SelectedItem="{Binding ProductionLineConfigs.ProductionLineStatus, Converter={StaticResource statusToBooleanConverter}}" />

Related

Prevent WPF ViewModel from creating new instance when navigating to other views

I am attempting to prevent my application from deleting a view and then creating a new one each time it's navigated around. I have a dashboard that will run a test program, if I select the settings view, then back to the dashboard, it has deleted the running test and initialized a new view. I need to keep the same view instance alive so that the test can continue to run while the user navigates to the settings view and back again but I cant exactly figure out how to successfully do that. I have attempted making the instance static but that doesn't seem to make a difference.
MainViewModel
class MainVM : ViewModelBase
{
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set { _currentView = value; OnPropertyChanged(); }
}
public ICommand DashboardCommand { get; set; }
public ICommand SettingsCommand { get; set; }
public static DashboardVM DashboardInstance { get; } = new DashboardVM();
public static SettingsVM SettingsInstance { get; } = new SettingsVM();
private void Dashboard(object obj) => CurrentView = DashboardInstance;
private void Settings(object obj) => CurrentView = SettingsInstance;
public MainVM()
{
DashboardCommand = new RelayCommand(Dashboard);
SettingsCommand = new RelayCommand(Settings);
// Startup Page
CurrentView = DashboardInstance;
}
}
ViewModelBase
public partial class ViewModelBase : ObservableObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
MainView - Navigation
<!-- Navigation Panel -->
<Grid HorizontalAlignment="Left" Width="76">
<Border Background="#3D5A8A" CornerRadius="10,0,0,10" />
<StackPanel Height="1200" Width="76">
<!-- Dashboard Button -->
<nav:Button Style="{StaticResource NavButton_Style}"
Command="{Binding DashboardCommand}"
IsChecked="True">
<Grid>
<Image Source="Images/dash_black_50.png"
Style="{StaticResource NavImage_Style}" />
<TextBlock Text="Dashboard"
Style="{StaticResource NavText_Style}" />
</Grid>
</nav:Button>
<!-- Settings Button -->
<nav:Button Style="{StaticResource NavButton_Style}"
Command="{Binding SettingsCommand}">
<Grid>
<Image Source="Images/gear_black_50.png"
Style="{StaticResource NavImage_Style}" />
<TextBlock Text="Settings"
Style="{StaticResource NavText_Style}" />
</Grid>
</nav:Button>
</StackPanel>
</Grid>
DashboardVM
class DashboardVM : ViewModelBase
{
enum TestItemStatus
{
Reset,
Queued,
InProgress,
Pass,
Fail
}
private readonly PageModel _pageModel;
private string _StartButtonText,
_WaveRelayEthernetText;
private bool isTestRunning;
public DashboardVM()
{
_pageModel = new PageModel();
_StartButtonText = "Start Test";
_WaveRelayEthernetText = string.Empty;
StartButtonCommand = new RelayCommand(o => StartButtonClick("StartButton"));
}
#region Text Handlers
public string StartButtonText
{
get { return _StartButtonText; }
set { _StartButtonText = value; NotifyPropertyChanged("StartButtonText"); }
}
public string WaveRelayEthernetText
{
get { return _WaveRelayEthernetText; }
set { _WaveRelayEthernetText = value; NotifyPropertyChanged("WaveRelayEthernetText"); }
}
#endregion
private bool TestRunning
{
get { return isTestRunning; }
set { isTestRunning = value;
if (isTestRunning) { StartButtonText = "Stop Test"; }
else { StartButtonText = "Start Test";
ResetTestItems();
}
NotifyPropertyChanged("TestRunning");
}
}
public ICommand StartButtonCommand { get; set; }
private void StartButtonClick(object sender)
{
if(TestRunning)
{
TestRunning = false;
}
else
{
SetTestItemsToQueued();
MessageBox.Show("Please plug in Tube 1");
// Start program.
TestRunning = true;
WaveRelayEthernetText = TestItemStatusEnumToString(TestItemStatus.InProgress);
}
}
private string TestItemStatusEnumToString(TestItemStatus temp)
{
if (temp == TestItemStatus.Reset) { return string.Empty; }
else if (temp == TestItemStatus.Queued) { return "Queued"; }
else if (temp == TestItemStatus.InProgress) { return "In Progress"; }
else if (temp == TestItemStatus.Pass) { return "Pass"; }
else if (temp == TestItemStatus.Fail) { return "Fail"; }
else { return string.Empty; }
}
private void SetTestItemsToQueued()
{
WaveRelayEthernetText = TestItemStatusEnumToString(TestItemStatus.Queued);
}
private void ResetTestItems()
{
WaveRelayEthernetText = TestItemStatusEnumToString(TestItemStatus.Reset);
}
}
Image for reference:
My Issue was in the App.xaml, I link a DataTemplate file like this:
<ResourceDictionary Source="Utilities/DataTemplate.xaml" />
Inside the data template, I had this code that linked the views to the view models.
<ResourceDictionary [...]">
<DataTemplate DataType="{x:Type vm:DashboardVM}">
<view:Dashboard />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SettingsVM}">
<view:Settings />
</DataTemplate>
</ResourceDictionary>
I changed that code to link the two to this:
<ResourceDictionary [...]>
<view:Dashboard x:Key="DashboardViewKey"/>
<view:Settings x:Key="SettingsViewKey"/>
<DataTemplate DataType="{x:Type vm:DashboardVM}">
<ContentControl Content="{StaticResource DashboardViewKey}" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SettingsVM}">
<ContentControl Content="{StaticResource SettingsViewKey}" />
</DataTemplate>
</ResourceDictionary>
I am now receiveing the expected behavior of being able to navigate without the Dashboard constructor being called, thus the view does not destory and recreate.
I hope someone else finds this useful.

How to properly implement MVVM pattern using SQLite database?

I'm totally new to MVVM and I've been going through a lot of online posts regarding how do I implement CRUD operations and properly bind SQLite database data to WPF controls like Datagrid, Combobox, Textbox, Button etc. but struggling to find what I need. Also, for someone who is new to MVVM pattern it seems pretty intimidating to say the least.
Anyways, coming to the point, I've found this post online and tried my best to make it work for SQLite databases but struggling at the moment.
My project structure looks like this:
and the relevant files as follows:
StudentRepository.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Linq;
using TestWpfMVVM.Model;
namespace TestWpfMVVM.DataAccess
{
public class StudentRepository
{
private StudentEntities studentContext = null;
public StudentRepository()
{
studentContext = new StudentEntities();
}
public Student Get(int id)
{
return studentContext.Students.Find(id);
}
public List<Student> GetAll()
{
return studentContext.Students.ToList();
}
public void AddStudent(Student student)
{
if (student != null)
{
studentContext.Students.Add(student);
studentContext.SaveChanges();
}
}
public void UpdateStudent(Student student)
{
var studentFind = this.Get(student.stdId);
if (studentFind != null)
{
studentFind.stdName = student.stdName;
studentFind.stdContact = student.stdContact;
studentFind.stdAge = student.stdAge;
studentFind.stdAddress = student.stdAddress;
studentContext.SaveChanges();
}
}
public void RemoveStudent(int id)
{
var studObj = studentContext.Students.Find(id);
if (studObj != null)
{
studentContext.Students.Remove(studObj);
studentContext.SaveChanges();
}
}
}
}
Student.cs
using System;
namespace TestWpfMVVM.Model
{
public class Student
{
public int stdId { get; set; }
public string stdName { get; set; }
public int stdAge { get; set; }
public string stdAddress { get; set; }
public string stdContact { get; set; }
}
}
StudentRecord.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TestWpfMVVM.ViewModel;
namespace TestWpfMVVM.Model
{
public class StudentRecord : ViewModelBase
{
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
OnPropertyChanged("Id");
}
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private int _age;
public int Age
{
get
{
return _age;
}
set
{
_age = value;
OnPropertyChanged("Age");
}
}
private string _address;
public string Address
{
get
{
return _address;
}
set
{
_address = value;
OnPropertyChanged("Address");
}
}
private string _contact;
public string Contact
{
get
{
return _contact;
}
set
{
_contact = value;
OnPropertyChanged("Contact");
}
}
private ObservableCollection<StudentRecord> _studentRecords;
public ObservableCollection<StudentRecord> StudentRecords
{
get
{
return _studentRecords;
}
set
{
_studentRecords = value;
OnPropertyChanged("StudentRecords");
}
}
}
}
RelayCommand.cs
using System;
using System.Windows.Input;
namespace TestWpfMVVM.ViewModel
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
StudentViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using TestWpfMVVM.DataAccess;
using TestWpfMVVM.Model;
namespace TestWpfMVVM.ViewModel
{
public class StudentViewModel
{
private ICommand _saveCommand;
private ICommand _resetCommand;
private ICommand _editCommand;
private ICommand _deleteCommand;
private StudentRepository _repository;
private Student _studentEntity = null;
public StudentRecord StudentRecord { get; set; }
public ICommand ResetCommand
{
get
{
if (_resetCommand == null)
_resetCommand = new RelayCommand(param => ResetData(), null);
return _resetCommand;
}
}
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
_saveCommand = new RelayCommand(param => SaveData(), null);
return _saveCommand;
}
}
public ICommand EditCommand
{
get
{
if (_editCommand == null)
_editCommand = new RelayCommand(param => EditData((int)param), null);
return _editCommand;
}
}
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
_deleteCommand = new RelayCommand(param => DeleteStudent((int)param), null);
return _deleteCommand;
}
}
public StudentViewModel()
{
_studentEntity = new Student();
_repository = new StudentRepository();
StudentRecord = new StudentRecord();
GetAll();
}
public void ResetData()
{
StudentRecord.Name = string.Empty;
StudentRecord.Id = 0;
StudentRecord.Address = string.Empty;
StudentRecord.Contact = string.Empty;
StudentRecord.Age = 0;
}
public void DeleteStudent(int id)
{
if (MessageBox.Show("Confirm delete of this record?", "Student", MessageBoxButton.YesNo)
== MessageBoxResult.Yes)
{
try
{
_repository.RemoveStudent(id);
MessageBox.Show("Record successfully deleted.");
}
catch (Exception ex)
{
MessageBox.Show("Error occured while saving. " + ex.InnerException);
}
finally
{
GetAll();
}
}
}
public void SaveData()
{
if (StudentRecord != null)
{
_studentEntity.stdName = StudentRecord.Name;
_studentEntity.stdAge = StudentRecord.Age;
_studentEntity.stdAddress = StudentRecord.Address;
_studentEntity.stdContact = StudentRecord.Contact;
try
{
if (StudentRecord.Id <= 0)
{
_repository.AddStudent(_studentEntity);
MessageBox.Show("New record successfully saved.");
}
else
{
_studentEntity.stdId = StudentRecord.Id;
_repository.UpdateStudent(_studentEntity);
MessageBox.Show("Record successfully updated.");
}
}
catch (Exception ex)
{
MessageBox.Show("Error occured while saving. " + ex.InnerException);
}
finally
{
GetAll();
ResetData();
}
}
}
public void EditData(int id)
{
var model = _repository.Get(id);
StudentRecord.Id = model.stdId;
StudentRecord.Name = model.stdName;
StudentRecord.Age = (int)model.stdAge;
StudentRecord.Address = model.stdAddress;
StudentRecord.Contact = model.stdContact;
}
public void GetAll()
{
StudentRecord.StudentRecords = new ObservableCollection<StudentRecord>();
_repository.GetAll().ForEach(data => StudentRecord.StudentRecords.Add(new StudentRecord()
{
Id = data.stdId,
Name = data.stdName,
Address = data.stdAddress,
Age = Convert.ToInt32(data.stdAge),
Contact = data.stdContact
}));
}
}
}
ViewModelBase.cs
using System.ComponentModel;
namespace TestWpfMVVM.ViewModel
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
MainWindow.xaml.cs
using System.Windows;
using TestWpfMVVM.ViewModel;
namespace TestWpfMVVM.View
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new StudentViewModel();
}
}
}
MainWindow.xaml
<Window x:Class="TestWpfMVVM.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWpfMVVM.View"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Orientation="Vertical">
<GroupBox Header="Student Form" Margin="10">
<Grid Height="150">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="Name" HorizontalAlignment="Left"
VerticalContentAlignment="Center" Grid.Column="0" Grid.Row="0"/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="TextBoxName" Height="27"
Text="{Binding Path=StudentRecord.Name, Mode=TwoWay}" Margin="5" Width="300" HorizontalAlignment="Left"/>
<Label Content="Age" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="TextBoxAge" Height="27"
Text="{Binding Path=StudentRecord.Age, Mode=TwoWay}" Margin="5" Width="70" HorizontalAlignment="Left"/>
<TextBlock Grid.Row="1" Grid.Column="1" x:Name="TextBlockId"
Visibility="Hidden" Text="{Binding Path=StudentRecord.Id, Mode=TwoWay}"/>
<Label Content="Address" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Grid.Row="2" Grid.Column="0" />
<TextBox Grid.Row="2" Grid.Column="1" x:Name="TextBoxAddress" Height="27"
Text="{Binding Path=StudentRecord.Address, Mode=TwoWay}" Margin="5" Width="300" HorizontalAlignment="Left"/>
<Label Content="Contact" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Grid.Row="3" Grid.Column="0" />
<TextBox Grid.Row="3" Grid.Column="1" x:Name="TextBoxContact" Height="27"
Text="{Binding Path=StudentRecord.Contact, Mode=TwoWay}" Margin="5" Width="300" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<StackPanel Height="40" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="ButtonSave" Content="Save" Height="30" Width="80"
Command="{Binding SaveCommand}"/>
<Button x:Name="ButtonCancel" Content="Cancel" Height="30" Width="80"
Command="{Binding ResetCommand}" Margin="5,0,10,0"/>
</StackPanel>
<StackPanel Height="210">
<DataGrid x:Name="DataGridStudents" AutoGenerateColumns="False"
ItemsSource="{Binding StudentRecord.StudentRecords}" CanUserAddRows="False" Height="200" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Id}" Visibility="Hidden"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" Width="100" IsReadOnly="True"/>
<DataGridTextColumn Header="Age" Binding="{Binding Path=Age}" Width="50" IsReadOnly="True"/>
<DataGridTextColumn Header="Address" Binding="{Binding Path=Address}" Width="180" IsReadOnly="True"/>
<DataGridTextColumn Header="Contact" Binding="{Binding Path=Contact}" Width="125" IsReadOnly="True"/>
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Select" x:Name="ButtonEdit" CommandParameter="{Binding Path=Id}"
Command="{Binding Path=DataContext.EditCommand,RelativeSource={RelativeSource FindAncestor,
AncestorType=Window}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Delete" x:Name="ButtonDelete" CommandParameter="{Binding Path=Id}"
Command="{Binding Path=DataContext.DeleteCommand, RelativeSource={RelativeSource FindAncestor,
AncestorType=Window}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</StackPanel>
</Window>
Now in the post, inside the Model folder, they added an ADO.NET Entity Data Model that connects to the Students table in the database and named it StudentModel while changing connectionstring name to StudentEntities.
But, I have a local SQLite database, how do I change that and what other things I need to change to make this app work. Currently I have only the error Error CS0246 The type or namespace name 'StudentEntities' could not be found (are you missing a using directive or an assembly reference?) which is understandable.
I know this may be a lot to ask but any help will be highly appreciated as I'm trying to make this work for quite some time now!
EDIT
After further checking I've updated StudentRepository as below
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Linq;
using TestWpfMVVM.Model;
namespace TestWpfMVVM.DataAccess
{
public class StudentRepository
{
public void ExecuteWrite(string query, Dictionary<string, object> args)
{
//setup the connection to the database
using (var con = new SQLiteConnection(#"Data Source=./Students.db;"))
{
con.Open();
//open a new command
using (var cmd = new SQLiteCommand(query, con))
{
//set the arguments given in the query
foreach (var pair in args)
{
cmd.Parameters.AddWithValue(pair.Key, pair.Value);
}
cmd.ExecuteNonQuery();
}
}
}
public DataTable Execute(string query, Dictionary<string, object> args)
{
if (string.IsNullOrEmpty(query.Trim()))
return null;
using (var con = new SQLiteConnection(#"Data Source=./Students.db;"))
{
con.Open();
using (var cmd = new SQLiteCommand(query, con))
{
foreach (KeyValuePair<string, object> entry in args)
{
cmd.Parameters.AddWithValue(entry.Key, entry.Value);
}
var da = new SQLiteDataAdapter(cmd);
var dt = new DataTable();
da.Fill(dt);
da.Dispose();
return dt;
}
}
}
public void AddStudent(Student student)
{
const string query = "INSERT INTO tabStud(Name, Age, Address, Contact) VALUES(#stdName, #stdAge, #stdAddress, #stdContact)";
var args = new Dictionary<string, object>
{
{"#stdName", student.stdName},
{"#stdAge", student.stdAge},
{"#stdAddress", student.stdAddress},
{"#stdContact", student.stdContact}
};
ExecuteWrite(query, args);
}
public void UpdateStudent(Student student)
{
const string query = "UPDATE tabStud SET Name = #stdName, Age = #stdAge, Address = #stdAddress, Contact = #stdContact WHERE Id = #stdId";
var args = new Dictionary<string, object>
{
{"#stdId", student.stdId},
{"#stdName", student.stdName},
{"#stdAge", student.stdAge},
{"#stdAddress", student.stdAddress},
{"#stdContact", student.stdContact}
};
ExecuteWrite(query, args);
}
public void RemoveStudent(Student student)
{
const string query = "Delete from tabStud WHERE Id = #stdId";
var args = new Dictionary<string, object>
{
{"#stdId", student.stdId}
};
ExecuteWrite(query, args);
}
public Student Get(int id)
{
var student = new Student();
var query = "SELECT * FROM tabStud WHERE Id = #stdId";
var args = new Dictionary<string, object>
{
{"#stdId", student.stdId}
};
DataTable dt = Execute(query, args);
if (dt == null || dt.Rows.Count == 0)
{
return null;
}
student = new Student
{
stdId = Convert.ToInt32(dt.Rows[0]["Id"]),
stdName = Convert.ToString(dt.Rows[0]["Name"]),
stdAge = Convert.ToInt32(dt.Rows[0]["Age"]),
stdAddress = Convert.ToString(dt.Rows[0]["Address"]),
stdContact = Convert.ToString(dt.Rows[0]["Contact"])
};
return student;
}
public List<Student> GetAll()
{
List<Student> students = new List<Student>();
var student = new Student();
var query = "SELECT * FROM tabStud";
var args = new Dictionary<string, object>
{
{"#stdId", student.stdId}
};
DataTable dt = Execute(query, args);
if (dt == null || dt.Rows.Count == 0)
{
return null;
}
student = new Student
{
stdId = Convert.ToInt32(dt.Rows[0]["Id"]),
stdName = Convert.ToString(dt.Rows[0]["Name"]),
stdAge = Convert.ToInt32(dt.Rows[0]["Age"]),
stdAddress = Convert.ToString(dt.Rows[0]["Address"]),
stdContact = Convert.ToString(dt.Rows[0]["Contact"])
};
students.Add(student);
return students;
}
}
}
and StudentViewModel as under
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using TestWpfMVVM.DataAccess;
using TestWpfMVVM.Model;
namespace TestWpfMVVM.ViewModel
{
public class StudentViewModel
{
private ICommand _saveCommand;
private ICommand _resetCommand;
private ICommand _editCommand;
private ICommand _deleteCommand;
private StudentRepository _repository;
private Student _studentEntity = null;
public StudentRecord StudentRecord { get; set; }
public ICommand ResetCommand
{
get
{
if (_resetCommand == null)
_resetCommand = new RelayCommand(param => ResetData(), null);
return _resetCommand;
}
}
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
_saveCommand = new RelayCommand(param => SaveData(), null);
return _saveCommand;
}
}
public ICommand EditCommand
{
get
{
if (_editCommand == null)
_editCommand = new RelayCommand(param => EditData((int)param), null);
return _editCommand;
}
}
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
_deleteCommand = new RelayCommand(param => DeleteStudent((int)param), null);
return _deleteCommand;
}
}
public StudentViewModel()
{
_studentEntity = new Student();
_repository = new StudentRepository();
StudentRecord = new StudentRecord();
GetAll();
}
public void ResetData()
{
StudentRecord.Name = string.Empty;
StudentRecord.Id = 0;
StudentRecord.Address = string.Empty;
StudentRecord.Contact = string.Empty;
StudentRecord.Age = 0;
}
public void DeleteStudent(int id)
{
if (MessageBox.Show("Confirm delete of this record?", "Student", MessageBoxButton.YesNo)
== MessageBoxResult.Yes)
{
try
{
var model = _repository.Get(id);
_repository.RemoveStudent(model);
MessageBox.Show("Record successfully deleted."); ;
}
catch (Exception ex)
{
MessageBox.Show("Error occured while saving. " + ex.InnerException);
}
finally
{
GetAll();
}
}
}
public void SaveData()
{
if (StudentRecord != null)
{
_studentEntity.stdName = StudentRecord.Name;
_studentEntity.stdAge = StudentRecord.Age;
_studentEntity.stdAddress = StudentRecord.Address;
_studentEntity.stdContact = StudentRecord.Contact;
try
{
if (StudentRecord.Id <= 0)
{
_repository.AddStudent(_studentEntity);
MessageBox.Show("New record successfully saved.");
}
else
{
_studentEntity.stdId = StudentRecord.Id;
_repository.UpdateStudent(_studentEntity);
MessageBox.Show("Record successfully updated.");
}
}
catch (Exception ex)
{
MessageBox.Show("Error occured while saving. " + ex.InnerException);
}
finally
{
GetAll();
ResetData();
}
}
}
public void EditData(int id)
{
var model = _repository.Get(id);
StudentRecord.Id = model.stdId;
StudentRecord.Name = model.stdName;
StudentRecord.Age = (int)model.stdAge;
StudentRecord.Address = model.stdAddress;
StudentRecord.Contact = model.stdContact;
}
public void GetAll()
{
StudentRecord.StudentRecords = new ObservableCollection<StudentRecord>();
_repository.GetAll().ForEach(data => StudentRecord.StudentRecords.Add(new StudentRecord()
{
Id = data.stdId,
Name = data.stdName,
Address = data.stdAddress,
Age = Convert.ToInt32(data.stdAge),
Contact = data.stdContact
}));
}
}
}
Now I'm able to run the project and also can add data to the table using the Save button. But after window loading I can only see the first row in the datagrid and not all of the rows. Furthermore, when I click on the Select and Delete button I get the errors respectively
I believe there is some problem in the Get(id) method but not sure how to fix...
Also, this is what my database table looks like
You should reimplement your own version of StudentRepository and delete the StudentEntities and use SqLite instead.
start by experimenting with some code to connect to the database, then select a full table and map it to a student object. SqLite has the ability to use generic and can fill you Student object if the Properties match with the name of the column in your database.
Ok Jayanta,
below is extract of my own code that you can get inspiration of and report it to your student repo:
public IEnumerable<Equity> GetListOfStocksOfMarket(string exchange)
{
string query = string.Format("SELECT * from " + Tables.ASSETS_TABLE + " WHERE ExchangeMic='{0}'", exchange);
Log(query);
var command = _connection.CreateCommand(query);
var equities = command.ExecuteQuery<Equity>();
IEnumerable<Equity> ret = equities.OrderBy(x=>x.InstrumentName).ToList();
return ret;
}
private SQLiteConnection _connection;
public bool Connect()
{
_connection = new SQLiteConnection(DATABASE_FILE_NAME);
_isConnected = true;
RepoYield = new RepoYield(_connection);
return true;
}
I offered some code that utilizes the SQLite namespaces / context here
SQL statement for creating new data into three tables at once c#
Like you creating some sort of wrapper repository, adjust as you need. I have example context of ensuring data types / sizes, etc.
You could even apply GENERICS based on each table structure you wanted to apply CRUD to. You can see the querying to the connected database context also simplifies pulling data using PARAMETERIZED QUERIES vs string manipulated which could allow SQL-Injection.
Data types as bound per your example would not need to be "converted" because the binding to the underlying structure that may be numeric based would only allow the value to be stored if it WERE numeric to begin with. I could expand more if you had any questions, but hopefully with the view model, and view context you have already, creating your own wrapper/repository using SQLite directly might help a bit more.

Show properties of selected items in ListView in DataGrid in WPF application

Screenshot:
Selecting items from above ListView shows their properties in the datagrid below.
XAML:
<Window x:Class="EmployeeSystem.Run_with_XML"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EmployeeSystem"
mc:Ignorable="d"
Title="Run with XML" Height="398.299" Width="305.91" WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
<Grid Height="325" VerticalAlignment="Top">
<StackPanel>
<ListView Height="100" DisplayMemberPath="Name" ItemsSource="{Binding employees}" SelectedItem="{Binding SelectedEmployee}"/>
<DataGrid Height="100" ItemsSource="{Binding EmployeesView}"/>
</StackPanel>
<Button x:Name="button" Content="Create New" Height="24" Margin="112,0,111,-30" VerticalAlignment="Bottom"/>
</Grid>
</Window>
VeiwModel:
class ViewModel
{
private Employee selectedEmployee;
public ICollectionView EmployeesView { get; set; }
EmployeeManagerXML emx = new EmployeeManagerXML();
public ViewModel()
{
EmployeesView = new ListCollectionView(emx.getEmployeesList()) //this is an ObservableCollection
{
Filter = obj =>
{
var Employee = (Employee)obj;
return SelectedEmployee != null && Employee.Name == SelectedEmployee.Name;
}
};
}
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (selectedEmployee != value)
{
selectedEmployee = value;
EmployeesView.Refresh();
}
}
}
}
I have been desperately trying to achieve this for days now...i searched google for hours...tried all the related pages on stackoverflow...This post is my last hope...someone please just show me where am going wrong with this. I already know this is a duplicate..This scenario is exactly the same as mine and I did everything exactly the same as that answer says...still mine doesn't work.
You should impliment INotifyPropertyChanged interface to reflect changes in View
for example below,
public class ViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
EmployeeManagerXML emx = new EmployeeManagerXML();
public ViewModel()
{
}
private Void RefreshEmployeeList()
{
EmployeesView = new ListCollectionView(emx.getEmployeesList()) //this is an ObservableCollection
{
Filter = obj =>
{
var Employee = (Employee)obj;
return SelectedEmployee != null && Employee.Name == SelectedEmployee.Name;
}
};
}
private Employee selectedEmployee;
private ICollectionView _EmployeesView;
public ICollectionView EmployeesView
{
get { return _EmployeesView; }
set
{
_EmployeesView = value;
NotifyPropertyChanged();
}
}
public Employee SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (selectedEmployee != value)
{
selectedEmployee = value;
NotifyPropertyChanged();
RefreshEmployeeList();
}
}
}
MSDN
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

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 { };
}

EASY way to refresh ListBox in WPF?

I have created a simple form that inserts/updates/deletes a values for Northwind Customers.
Everything works fine, except in order to see a results, I have to close it, and reopen again.
My form looks like this :
I've searched tens of articles on how to refresh ListBox, but all of those use interface implementing, or using DataSets, and stuff I have never heard of and cannot implement. It's a very simple project, using simple procedures. Is there an easy way to refresh the list of customers without adding many lines of code?
The simple answer is: myListBox.Items.Refresh();
Are you using ObservableCollection and does your model implement INotifyPropertyChanged these two things will automaticly update the ListBox on any change. no need to explicitly refresh the list.
Here is a small example of using ObservableCollection and INotifyPropertyChanged, obviously you will populate your ObservableCollection from your SQL database.
Window:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<MyModel> _list = new ObservableCollection<MyModel>();
private MyModel _selectedModel;
public MainWindow()
{
InitializeComponent();
List.Add(new MyModel { Name = "James", CompanyName = "StackOverflow"});
List.Add(new MyModel { Name = "Adam", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Chris", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Steve", CompanyName = "StackOverflow" });
List.Add(new MyModel { Name = "Brent", CompanyName = "StackOverflow" });
}
public ObservableCollection<MyModel> List
{
get { return _list; }
set { _list = value; }
}
public MyModel SelectedModel
{
get { return _selectedModel; }
set { _selectedModel = value; NotifyPropertyChanged("SelectedModel"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Xaml
<Window x:Class="WpfApplication11.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" Name="UI">
<Grid>
<ListBox ItemsSource="{Binding ElementName=UI, Path=List}" SelectedItem="{Binding ElementName=UI, Path=SelectedModel}" Margin="0,0,200,0" DisplayMemberPath="DisplayMember" SelectedIndex="0" />
<StackPanel HorizontalAlignment="Left" Height="100" Margin="322,10,0,0" VerticalAlignment="Top" Width="185">
<TextBlock Text="Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Company Name" />
<TextBox Height="23" TextWrapping="Wrap" Text="{Binding ElementName=UI, Path=SelectedModel.CompanyName, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
</Window>
Model
public class MyModel : INotifyPropertyChanged
{
private string _name;
private string _companyName;
public string Name
{
get { return _name; }
set { _name = value; NotifyPropertyChanged("Name"); }
}
public string CompanyName
{
get { return _companyName; }
set { _companyName = value; NotifyPropertyChanged("CompanyName"); }
}
public string DisplayMember
{
get { return string.Format("{0} ({1})", Name, CompanyName); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
PropertyChanged(this, new PropertyChangedEventArgs("DisplayMember"));
}
}
}
In this case any edit to properties will Update your list instantly, also will update when new Items are added/removed.
How about calling ListBox.UpdateLayout?
Of course you also need to update the particular item(s) so that it returns the updated string from the ToString method.
UPDATE: I think you also need to call ListBox.InvalidateArrange before you call ListBox.UpdateLayout.
Use INotifyPropertyChanged is the best way, refresh the entire list is not a good idea.
Main entrance:
public partial class MainWindow : Window
{
private BindingList<FoodModel> foodList = new BindingList<FoodModel>();
public MainWindow()
{
InitializeComponent();
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
foodList.Add(new FoodModel { foodName = "apple1" });
foodList.Add(new FoodModel { foodName = "apple2" });
foodList.Add(new FoodModel { foodName = "apple3" });
FoodListBox.ItemsSource = foodList;
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
foodList[0].foodName = "orange";
}
private void RefreshButton_Click(object sender, RoutedEventArgs e)
{
FoodListBox.Items.Refresh();
}
}
Model:
public class FoodModel: INotifyPropertyChanged
{
private string _foodName;
public string foodName
{
get { return _foodName; }
set
{
if (_foodName != value)
{
_foodName = value;
PropertyChanged(this, new PropertyChangedEventArgs("foodName"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
XAML:
<ListBox HorizontalAlignment="Center" Name="FoodListBox" VerticalAlignment="Top" Width="194" Height="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding foodName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories