How to use FileUpload in Telerik RadDataForm in MVVM WPF? - c#

I am developing a WPF application in MVVM pattern using caliburn.micro. I am stuck when I need to use a local path of my computer using fileupload or openfiledialog. I need to insert the path in the database. Here is the xaml:
<Grid Margin="20" Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="50*"></RowDefinition>
<RowDefinition Height="50*"></RowDefinition>
</Grid.RowDefinitions>
<Telerik:RadGridView x:Name="RadGridView1" GroupRenderMode="Flat"
ItemsSource="{Binding ModelList}"
ColumnWidth="*"
CanUserFreezeColumns="False"
RowIndicatorVisibility="Collapsed">
</Telerik:RadGridView>
<Telerik:RadDataForm Grid.Row="1"
x:Name="myRadDataForm"
ItemsSource="{Binding ModelList}"
Header="Model Details"
Margin="0,5,0,0" cal:Message.Attach="[Event DeletingItem] = [Action onDeleted()]">
</Telerik:RadDataForm>
</Grid>
I am using an ICollectionView as a datasource in viewmodel. Maybe I need to use a datatemplate in the dataform, but I don't know how to do that. Please let me know if you need any other info.
Code for ViewModel:
[Export(typeof(AddModelsViewModel))]
public class AddModelsViewModel : PropertyChangedBase
{
private Model _selectmodel;
List<Model> ModelsList = new List<Model>();
Model model = new Model();
private ICollectionView models = null;
public ICollectionView Models
{
get
{
if (this.models == null)
{
ObservableCollection<Model> allModels = new ObservableCollection<Model>();
ModelsList = model.GetAllModels();
foreach (Model m in ModelsList)
{
allModels.Add(m);
}
this.models = new QueryableCollectionView(allModels);
}
return this.models;
}
}
// Model model = new Model();
private List<Model> _ModelListAll;
// private ClsRole _selectedRole;
public Model selectedModel
{
get { return _selectmodel; }
set
{
_selectmodel = value;
NotifyOfPropertyChange(() => selectedModel);
}
}
public List<Model> ModelListAll
{
get { return _ModelListAll; }
set
{
_ModelListAll = value;
NotifyOfPropertyChange(() => ModelListAll);
}
}
public void onDeleted()
{
try
{
model.Delete(selectedModel);
}
catch (Exception ex)
{
Logger.GetInstance().Log(ex);
}
}
public void OnRoleViewLoad()
{
ModelListAll = model.GetAllModels();
if (ModelListAll.Count > 0)
{
selectedModel = ModelListAll[0];
}
}
private ICollectionView modelList = null;
public ICollectionView ModelList
{
get
{
if (this.modelList == null)
{
ObservableCollection<Model> Allmodel = new ObservableCollection<Model>();
ModelListAll = model.GetAllModels();
foreach (Model role in ModelListAll)
{
Allmodel.Add(role);
}
this.modelList = new QueryableCollectionView(Allmodel);
}
return this.modelList;
}
set
{
ModelList = value;
NotifyOfPropertyChange(() => ModelList);
}
}
public void OnRadGridView1SelectonChanged(Model modelClicked)
{
selectedModel = modelClicked;
}
}

Related

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.

Xamarin Forms Syncfusion - Async databinding not working with SfCombobox

I have an issue on binding a collection into my SfCombobox when the data comes from an async call ( my api ). Of course, all works correctly with a local list.
I already check the data coming from my api and the binding with my viewmodel property all is working.
Here the XAML sample :
<border:SfBorder
Grid.Row="4"
BorderColor="{Binding Source={x:Reference CategoryCombo}, Path=IsFocused, Converter={StaticResource ColorConverter}, ConverterParameter=0}"
Style="{StaticResource SfBorderStyle}">
<combobox:SfComboBox
AllowFiltering="True"
x:Name="CategoryCombo"
Style="{StaticResource ComboBoxStyle}"
DataSource="{Binding Categories}" DisplayMemberPath="Name" SelectedValuePath="Id"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"/>
</border:SfBorder>
and the full view model
[Preserve(AllMembers = true)]
[DataContract]
public class SearchPageViewModel: BaseViewModel
{
private ObservableCollection<Category> categories;
private readonly ICategoryQueriesServices categoryQueriesServices;
public ObservableCollection<Category> Categories
{
get { return this.categories; }
set
{
if (this.categories == value)
{
return;
}
this.categories = value;
this.NotifyPropertyChanged();
}
}
private Category selectedCategory;
public Category SelectedCategory
{
get
{
return selectedCategory;
}
set
{
if (selectedCategory != value)
{
selectedCategory = value;
this.NotifyPropertyChanged();
}
}
}
public SearchPageViewModel(ICategoryQueriesServices categoryQueriesServices)
{
this.categoryQueriesServices = categoryQueriesServices;
}
protected override void CurrentPageOnAppearing(object sender, EventArgs eventArgs)
{
base.CurrentPageOnAppearing(sender, eventArgs);
Task.Run(async () =>
{
var categories = await this.categoryQueriesServices.GetCategoryModelsAsync();
Device.BeginInvokeOnMainThread(() =>
{
Categories = new ObservableCollection<Category>(categories);
});
});
}
}
thanks for your help

Get SelectedItem or SelectedItems in a ListBox that is updated to bind to ViewModel using MVVM

I'm creating a WPF Network Scanning application using .NET Framework 4.7.2 with Caliburn Micro and a strict MVVM pattern. I have been going through all the similar questions to this one but yet to find a solution. I don't use code-behind and so I am trying to do everything MVVM style.
My ListBox is initially empty and items are added to it until my scan is complete. After this I try to select 1 OR multiple items in the ListBox even though visually the items can be multiply selected, the problem I am finding is that I am unable to bind SelectedItem in my ListBox View with the SelectedItem property. I.e. SelectedItem property in my ViewModel will not update and remains at null.
I want my SelectedItems collection to increment as more items are selected.
I have tried the following:
Binding the IsSelected property of ListBoxItem to DataContext of Itemssource
Get ListBox SelectedItems
These have not worked so far.
Example Model:
public class ExampleModel
{
protected int _id;
protected string _name;
public ExampleModel()
{
}
public ExampleModel(int _id, string _name)
{
this.Id = _id;
this.Name = _name;
}
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Name == Name && this.Id == Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Example ViewModel:
public class ExampleViewModel : Screen
{
private IEventAggregator _events;
public ExampleViewModel(IEventAggregator events)
{
// Initialize collection
BindableCollection<ExampleModel> SelectedItems = new BindableCollection<ExampleModel>();
_events = events;
}
private BindableCollection<ExampleModel> _selectedItems;
public BindableCollection<ExampleModel> SelectedItems
{
get { return _selectedItems; }
set
{
// SelectedItems remains NULL
_selectedItems = value;
NotifyOfPropertyChange(() => Items);
}
}
private ExampleModel _selectedItem;
public ExampleModel SelectedItem
{
get { return _selectedItem; }
set
{
// CODE NOT UPDATING SELECTED ITEM VALUE!
_selectedItem = value;
NotifyOfPropertyChange(() => SelectedItem);
NotifyOfPropertyChange(() => SelectedItems);
}
}
// Initialize list
private HashSet<ExampleModel> _exampleList = new HashSet<ExampleModel>();
public HashSet<ExampleModel> ExampleList
{
get { return _exampleList; }
set { _exampleList = value; }
}
private async Task AddItem(ExampleModel item)
{
// A task to asyncrhonously add unique items to list
await Task.Run(() =>
_exampleList.Add(item));
Items = new BindableCollection<ExampleModel>(_exampleList);
NotifyOfPropertyChange(() => Items);
}
private BindableCollection<ExampleModel> _items;
public BindableCollection<ExampleModel> Items
{
get { return _items; }
set
{
_items = value;
NotifyOfPropertyChange(() => Items);
}
}
// Begin adding items to list
public async void Add()
{
for (int i = 0; i<10; i++)
{
// do something
var item = new ExampleModel
{
Id = i + 1,
Name = i.ToString()
};
await AddItem(item);
}
}
// Button to start process
public void Start()
{
Add();
}
}
Example View:
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.ScrollUnit="Item"
HorizontalContentAlignment="Stretch"
ScrollViewer.IsDeferredScrollingEnabled="True"
Margin="8 0 0 0"
Grid.Column="1"
ItemsSource="{Binding Path= Items}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Multiple"
b:SelectedItemsBehavior.SelectedItems="{Binding SelectedItems, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!-- Id -->
<TextBlock
Text="{Binding Id}"
Grid.Row="0"
Grid.Column="0"
/>
<!-- Name -->
<TextBlock
Text="{Binding Name}"
Grid.Row="0"
Grid.Column="1"
Margin="10"
/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
Selected Items Behavior class:
public class SelectedItemsBehavior
{
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems",
typeof(INotifyCollectionChanged), typeof(SelectedItemsBehavior),
new PropertyMetadata(default(IList), OnSelectedItemsChanged));
public static void SetSelectedItems(DependencyObject d, INotifyCollectionChanged value)
{
d.SetValue(SelectedItemsProperty, value);
}
public static IList GetSelectedItems(DependencyObject element)
{
return (IList)element.GetValue(SelectedItemsProperty);
}
private static void OnSelectedItemsChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
IList selectedItems = null;
void CollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs args)
{
if (args.OldItems != null)
foreach (var item in args.OldItems)
if (selectedItems.Contains(item))
selectedItems.Remove(item);
if (args.NewItems != null)
foreach (var item in args.NewItems)
if (!selectedItems.Contains(item))
selectedItems.Add(item);
};
if (d is MultiSelector multiSelector)
{
selectedItems = multiSelector.SelectedItems;
multiSelector.SelectionChanged += OnSelectionChanged;
}
if (d is ListBox listBox)
{
selectedItems = listBox.SelectedItems;
listBox.SelectionMode = SelectionMode.Multiple;
listBox.SelectionChanged += OnSelectionChanged;
}
if (selectedItems == null) return;
if (e.OldValue is INotifyCollectionChanged)
(e.OldValue as INotifyCollectionChanged).CollectionChanged
-= CollectionChangedEventHandler;
if (e.NewValue is INotifyCollectionChanged)
(e.NewValue as INotifyCollectionChanged).CollectionChanged
+= CollectionChangedEventHandler;
}
private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var s = sender as DependencyObject;
if (!GetIsBusy(s))
{
SetIsBusy(s, true);
var list = GetSelectedItems((DependencyObject)sender);
foreach (var item in e.RemovedItems)
if (list.Contains(item)) list.Remove(item);
foreach (var item in e.AddedItems)
if (!list.Contains(item)) list.Add(item);
SetIsBusy(s, false);
}
}
private static readonly DependencyProperty IsBusyProperty =
DependencyProperty.RegisterAttached("IsBusy", typeof(bool),
typeof(SelectedItemsBehavior), new PropertyMetadata(default(bool)));
private static void SetIsBusy(DependencyObject element, bool value)
{
element.SetValue(IsBusyProperty, value);
}
private static bool GetIsBusy(DependencyObject element)
{
return (bool)element.GetValue(IsBusyProperty);
}
}
I expect SelectedItems collection to populate, but the output remains null.
I expect when any item is selected the value of SelectedItem is updated but get null.

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.

Binding to model that contains collections in MVVM

I am getting started with MVVM (using Caliburn.Micro) and have come across an issue which I'm not sure if I'm doing this correctly. I have a model MediaCacherConfig which represents a textfile that stores the data in json format. The model contains 2 lists of strings and one string by itself.
What I am struggling with is how to correctly set up the viewmodel and in particular the AddNewFolder() method. I'm not sure if I am raising the correct event and whether the viewmodel's representation is correct. I can see how to bind to a simple property, but binding to a collection seems a bit more of a head spinner as I am creating a whole new collection everytime an item (string) is added.
Furthermore, when I load an entirely new model I have to run the NotifyPropertyChanged() method on all the properties which doesn't make sense to me.
Any guidance is much appreciated.
public class MediaCacherConfig : IConfig
{
public string DatabaseFileName { get; set; }
public ICollection<string> FoldersToScan { get; set; }
public ICollection<string> ExtensionsToIgnore { get; set; }
}
I have a viewmodel MediaCacherConfigViewModel:
public class MediaCacherConfigViewModel : PropertyChangedBase
{
private MediaCacherConfig Model { get; set; }
public string DatabaseFileName
{
get { return Model.DatabaseFileName; }
set
{
Model.DatabaseFileName = value;
NotifyOfPropertyChange(() => DatabaseFileName);
}
}
public BindableCollection<string> FoldersToScan
{
get
{
return new BindableCollection<string>(Model.FoldersToScan);
}
set
{
Model.FoldersToScan = value;
NotifyOfPropertyChange(() => FoldersToScan);
}
}
public BindableCollection<string> ExtensionsToIgnore
{
get
{
return new BindableCollection<string>(Model.ExtensionsToIgnore);
}
set
{
Model.ExtensionsToIgnore = value;
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
}
/* Constructor */
public MediaCacherConfigViewModel()
{
LoadSampleConfig();
}
/* Methods */
public void LoadSampleConfig()
{
MediaCacherConfig c = new MediaCacherConfig();
string sampleDatabaseFileName = "testing.config";
List<string> sampleFoldersToScan = new List<string>();
sampleFoldersToScan.Add("A");
sampleFoldersToScan.Add("B");
sampleFoldersToScan.Add("C");
List<string> sampleExtensionsToIgnore = new List<string>();
sampleExtensionsToIgnore.Add("txt");
sampleExtensionsToIgnore.Add("mov");
sampleExtensionsToIgnore.Add("db");
sampleExtensionsToIgnore.Add("dat");
c.DatabaseFileName = sampleDatabaseFileName;
c.FoldersToScan = sampleFoldersToScan;
c.ExtensionsToIgnore = sampleExtensionsToIgnore;
Model = c;
NotifyOfPropertyChange(() => DatabaseFileName);
NotifyOfPropertyChange(() => FoldersToScan);
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
public void AddNewFolder()
{
Model.FoldersToScan.Add("new one added");
NotifyOfPropertyChange(() => FoldersToScan);
}
public void SaveConfig()
{
ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));
ConfigTools.SaveConfig(Model,"sampleconfig.txt");
}
public void LoadConfig()
{
ConfigTools.Configure(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Cacher", "Config"));
MediaCacherConfig m = ConfigTools.LoadConfig<MediaCacherConfig>("sampleconfig.txt") as MediaCacherConfig;
Model = m;
NotifyOfPropertyChange(() => DatabaseFileName);
NotifyOfPropertyChange(() => FoldersToScan);
NotifyOfPropertyChange(() => ExtensionsToIgnore);
}
}
And here is my view:
<UserControl x:Class="MediaCacher.Views.MediaCacherConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="413" Width="300">
<Grid MinWidth="300" MinHeight="300" Background="LightBlue" Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="409*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<TextBox x:Name="DatabaseFileName" TextWrapping="Wrap" Margin="10,64,10,0" HorizontalAlignment="Center" Width="280" Height="42" VerticalAlignment="Top"/>
<ListBox x:Name="FoldersToScan" HorizontalAlignment="Left" Height="145" Margin="10,111,0,0" VerticalAlignment="Top" Width="280"/>
<ListBox x:Name="ExtensionsToIgnore" HorizontalAlignment="Left" Height="145" Margin="10,261,0,0" VerticalAlignment="Top" Width="280"/>
<Button x:Name="AddNewFolder" Content="Add" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
<Button x:Name="LoadConfig" Content="Load" HorizontalAlignment="Left" Margin="102,10,0,0" VerticalAlignment="Top" Width="96" Height="49"/>
<Button x:Name="SaveConfig" Content="Save" HorizontalAlignment="Left" Margin="203,10,0,0" VerticalAlignment="Top" Width="87" Height="49"/>
</Grid>
First, here you are returning a brand new collection every time, so obviously nothing gets persisted.
public BindableCollection<string> FoldersToScan
{
get
{
return new BindableCollection<string>(Model.FoldersToScan);
}
set
{
Model.FoldersToScan = value;
NotifyOfPropertyChange(() => FoldersToScan);
}
}
Secondly, your AddFolder method should belong in your ViewModel. When you Add a string to your already existing collection the fact that it is a BindingCollection should fire off an event to your View automatically that a new Item was added.
This is how I would do it. This is obviously an example for demonstration purposes, please add everything else you need. Youd ideall want to pass EventArgs and note I am not implementing INotifyPorpertyChanged because I don't have time to write it all out. Also I am using ObservableCollection but you can use your BindableCollection.
The point of this example is to show you how to manage your ViewModel - > Model communcation. Technically speaking your View -> ViewModel should talk through a CommandPattern.
public class YourViewModel
{
private readonly YourModel model;
private ObservableCollection<string> foldersToScan = new ObservableCollection<string>();
public ObservableCollection<string> FoldersToScan
{
get { return this.foldersToScan; }
}
public YourViewModel(YourModel model)
{
this.model = model;
this.model.OnItemAdded += item => this.foldersToScan.Add(item);
}
public void AddFolder(string addFolder) //gets called from view
{
this.model.AddFolder(addFolder); //could be ICommand using Command Pattern
}
}
public class YourModel
{
private readonly List<string> foldersToScan;
public IEnumerable<string> FoldersToScan
{
get { return this.foldersToScan; }
}
public event Action<string> OnItemAdded;
public YourModel()
{
this.foldersToScan = new List<string>();
}
public void AddFolder(string folder)
{
this.foldersToScan.Add(folder);
this.RaiseItemAdded(folder);
}
void RaiseItemAdded(string folder)
{
Action<string> handler = OnItemAdded;
if (handler != null) handler(folder);
}
}

Categories