How to properly implement MVVM pattern using SQLite database? - c#

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.

Related

Setter not triggering as user types into TextBox

I'm trying to filter a list based on what a user types into a textbox. However, nothing is happening as the user types into the box. As I've been debugging, I've placed breakpoints on the setter for this binding, but they don't trigger.
TextBox definition:
<TextBox HorizontalAlignment="Center" Text="{Binding TESTSerialNumbSearchTerm, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ToolTip="Filter Part Number" Width="180"/>
ViewModel Binding:
public String TESTSerialNumbSearchTerm
{
get
{
return serialNumbSearchTerm;
}
set
{
if (serialNumbSearchTerm != value)
{
serialNumbSearchTerm = value;
VisibleProfiles = FilterList(VisibleProfiles, Tuple.Create("serialNumber", value));
OnPropertyChanged(nameof(VisibleProfiles));
OnPropertyChanged(nameof(TESTSerialNumbSearchTerm));
}
}
}
Grid definition, with ItemSource:
<DataGrid MaxHeight="400" Grid.Row="0" ItemsSource="{Binding VisibleProfiles}" SelectedItem="{Binding SelectedProfile}" SelectionMode="Single" IsReadOnly="True" AutoGenerateColumns="False" VerticalScrollBarVisibility="Visible">
FilterList Method:
public List<DongleProfile> FilterList(List<DongleProfile> inputList, Tuple<string, string> filter)
{
List<DongleProfile> newList = new List<DongleProfile>();
foreach (DongleProfile item in inputList)
{
switch (filter.Item1)
{
case "serialNumber":
if (item.SerialNumberPrefix.Contains(filter.Item2))
{
newList.Add(item);
}
break;
// Similar cases
}
}
return newList;
}
If the TextBox is located in a DataGrid, you could use a RelativeSource to bind to a property of the view model:
<TextBox HorizontalAlignment="Center"
Text="{Binding DataContext.TESTSerialNumbSearchTerm, UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
ToolTip="Filter Part Number" Width="180"/>
Try the following idea:
Public field for filtering textbox
public string md_FilterString
{
get { return _FilterString; }
set
{
if (_FilterString != value)
{
_FilterString = value;
mf_MakeView();
OnPropertyChanged("md_FilterString");
}
}
}
Public field for datagrid binding:
public ICollectionView md_LogEntriesStoreView { get; private set; }
XAML:
..
<TextBox Grid.Column="1"
Text="{Binding md_FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="22"
HorizontalAlignment="Stretch"
Margin="0,0,0,0"
Name="textBoxFilter"
VerticalAlignment="Center"/>
..
<DataGrid ItemsSource="{Binding md_LogEntriesStoreView, UpdateSourceTrigger=PropertyChanged}"
..
</DataGrid>
mf_MakeView func configures the composition of the collection md_LogEntriesStoreView:
private void mf_MakeView()
{
if (d_Items== null) return;
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
md_LogEntriesStoreView.Filter = mf_UserFilter;
OnPropertyChanged("md_LogEntriesStoreView");
}
Where d_Items - are directly the elements of your observable collection that will be displayed in the control datagrid
The filtering function (mf_UserFilter) is presented in a general way for an object containing text fields. You can replace it for optimization purposes with your own version, adapted to your goals:
private bool mf_UserFilter(object item)
{
string s = md_FilterString;
if (String.IsNullOrWhiteSpace(s))
return true;
else
{
var srcT = item.GetType();
foreach (var f in srcT.GetFields())
{
string str = f.GetValue(item) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
foreach (var f in srcT.GetProperties())
{
string str = f.GetValue(item, null) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
return false;
}
}
UPDATE:
Full text:
Code part:
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindow_ModelView();
}
}
public class MainWindow_ModelView : NotifyBase
{
private string _FilterString = String.Empty;
public ObservableCollection<ItemClass> d_Items { get; set; }
public ICollectionView md_LogEntriesStoreView { get; private set; }
public string md_FilterString
{
get { return _FilterString; }
set
{
if (_FilterString != value)
{
_FilterString = value;
mf_MakeView();
OnPropertyChanged("md_FilterString");
}
}
}
public MainWindow_ModelView()
{
d_Items = new ObservableCollection<ItemClass>() { new ItemClass() { d_Text1 = "Item1Text1", d_Text2 = "Item1Text2" },
new ItemClass() { d_Text1 = "Item2Text1", d_Text2 = "Item2Text2" },
new ItemClass() { d_Text1 = "Item3Text1", d_Text2 = "Item3Text2" } };
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
}
private void mf_MakeView()
{
if (d_Items == null) return;
md_LogEntriesStoreView = CollectionViewSource.GetDefaultView(d_Items);
md_LogEntriesStoreView.Filter = mf_UserFilter;
OnPropertyChanged("md_LogEntriesStoreView");
}
private bool mf_UserFilter(object item)
{
string s = _FilterString;
if (String.IsNullOrWhiteSpace(s))
return true;
else
{
var srcT = item.GetType();
foreach (var f in srcT.GetFields())
{
string str = f.GetValue(item) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
foreach (var f in srcT.GetProperties())
{
string str = f.GetValue(item, null) as string;
if (String.IsNullOrWhiteSpace(str)) continue;
bool b = str.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0;
if (b) return true;
}
return false;
}
}
}
public class ItemClass : NotifyBase
{
public string d_Text1 { get; set; }
public string d_Text2 { get; set; }
}
public class NotifyBase : INotifyPropertyChanged
{
Guid id = Guid.NewGuid();
[Browsable(false)]
[System.Xml.Serialization.XmlAttribute("ID")]
public Guid ID
{
get { return id; }
set
{
if (id != value)
{
id = value;
OnPropertyChanged("ID");
}
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
XAML part:
<Window x:Class="WpfApplication2.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">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Height="23"
Text="{Binding md_FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
Name="textBox1"
Margin="2"
VerticalAlignment="Top"/>
<DataGrid ItemsSource="{Binding md_LogEntriesStoreView}"
AutoGenerateColumns="False"
Grid.Row="1"
Margin="2"
HorizontalAlignment="Stretch"
Name="dataGrid1"
VerticalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path = d_Text1}"
Width="Auto"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path = d_Text2}"
Width="*"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Result:

Bind enum into ViewModel using C# WPF

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}}" />

How to use FileUpload in Telerik RadDataForm in MVVM WPF?

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

Parse xml into ObservableCollection using Linq. Data bind to datagrid using wpf c#

I'm new to linq, wpf and C#. I've managed to research my way to a functional component. I have attacked data binding successfully but I'm struggling with performance. I'm reading in a static external xml file (i.e. database) and want to display it to users using a wpf datagrid. The additional bit of info is that I'm using a user-controlled wpf combobox to filter how much data from the database is shown in the grid. I'd like to use linq to accomplish this task but I can't seem to get it to perform correctly.
C# file:
namespace ReadPipeXMLDB
{
public partial class ReadDB : Window, INotifyPropertyChanged
{
private XDocument xmlDoc = null;
const string ALL = "All";
// Constructor
public ReadDB()
{
InitializeComponent();
// Load xml
xmlDoc = XDocument.Load("DataBase.xml");
this.DataContext = this;
}
private ObservableCollection<CPipeData> _col;
public ObservableCollection<CPipeData> Col
{
get { return _col; }
set
{
if (_col == value)
return;
_col = value;
OnPropertyChanged(() => Col);
}
}
private ObservableCollection<CMfgData> _mfgCollection;
public ObservableCollection<CMfgData> MfgCollection
{
get { return _mfgCollection; }
set
{
if (_mfgCollection == value)
return;
_mfgCollection = value;
OnPropertyChanged(() => MfgCollection);
}
}
private ObservableCollection<string> _mfgNames;
public ObservableCollection<string> MfgNames
{
get { return this._mfgNames; }
set
{
if (this._mfgNames == value)
return;
this._mfgNames = value;
OnPropertyChanged(() => MfgNames);
}
}
#region Notify Event Declaration and Definition
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged<T>(Expression<Func<T>> property)
{
PropertyChangedEventHandler eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
var memberExpression = property.Body as MemberExpression;
eventHandler(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
#endregion
public class CMfgData : ReadDB
{
private string _mfgName;
//private ObservableCollection<CPipeData> _pipeDataCollection;
/*public CMfgData(string mfgName, CPipeData pipeData)
{
_mfgName = mfgName;
_pipeData = pipeData;
}*/
#region CMfgData Property Definitions
public string MfgName
{
get { return _mfgName; }
set
{
if (_mfgName == value)
return;
_mfgName = value;
OnPropertyChanged(() => MfgName);
}
}
/* public ObservableCollection<CPipeData> PipeDataCollection
{
get { return _pipeDataCollection; }
set
{
if (_pipeDataCollection == value)
return;
_pipeDataCollection = value;
OnPropertyChanged(() => PipeDataCollection);
}
}*/
#endregion
}
public class CPipeData : ReadDB
{
// PipeData Property Declarations
private string _nominal;
private string _sched;
private string _id;
private string _od;
private string _wt;
public CPipeData()
{
_nominal = "";
_sched = "";
_id = "";
_od = "";
_wt = "";
}
// Constructor
public CPipeData(string nominal, string sched, string id, string od, string wt)
{
_nominal = nominal;
_sched = sched;
_id = id;
_od = od;
_wt = wt;
}
#region CPipeData Property Definitions
public string Nominal
{
get { return _nominal; }
set
{
if (_nominal == value)
return;
_nominal = value;
OnPropertyChanged(() => Nominal);
}
}
public string Sched
{
get { return _sched; }
set
{
if (_sched == value)
return;
_sched = value;
OnPropertyChanged(() => Sched);
}
}
public string ID
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
OnPropertyChanged(() => ID);
}
}
public string OD
{
get { return _od; }
set
{
if (_od == value)
return;
_od = value;
OnPropertyChanged(() => OD);
}
}
public string WT
{
get { return _wt; }
set
{
if (_wt == value)
return;
_wt = value;
OnPropertyChanged(() => WT);
}
}
#endregion
}
private void mfgrComboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Update database grid
if (mfgrComboBox1.SelectedValue is string)
{
PopulateGrid(mfgrComboBox1.SelectedValue as string);
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
/*MfgCollection = new ObservableCollection<CMfgData>(
from mfg in xmlDoc.Root.Elements("Mfg")
//where mfg.Attribute("name").Value == comboValue
select new CMfgData
{
MfgName = mfg.Attribute("name").Value,
PipeDataCollection =
new ObservableCollection<CPipeData>
(from pipe in mfg.Elements("pipe")
select new CPipeData
{
Nominal = pipe.Element("Nominal").Value,
Sched = pipe.Element("Schedule").Value,
ID = pipe.Element("ID").Value,
OD = pipe.Element("OD").Value,
WT = pipe.Element("Wall_Thickness").Value
})
});*/
}
private void mfgrComboBox1_Loaded(object sender, RoutedEventArgs e)
{
// Make sure xml document has been loaded
if (xmlDoc != null)
{
ObservableCollection<string> tempCollection = new ObservableCollection<string>(
from n in xmlDoc.Root.Elements("Mfg").Attributes("name")
select n.Value);
// Add the additional "All" filter
tempCollection.Insert(0, ALL);
// Assign list to member property. This is done last so the property event gets fired only once
MfgNames = tempCollection;
PopulateGrid(ALL);
}
}
private void PopulateGrid(string comboValue)
{
if (mfgrComboBox1.Items.IndexOf(comboValue) > -1)
{
Col = new ObservableCollection<CPipeData>(
from mfg in xmlDoc.Root.Elements("Mfg")
where mfg.Attribute("name").Value == comboValue
from pipe in mfg.Elements("pipe")
select new CPipeData
{
Nominal = pipe.Element("Nominal").Value,
Sched = pipe.Element("Schedule").Value,
ID = pipe.Element("ID").Value,
OD = pipe.Element("OD").Value,
WT = pipe.Element("Wall_Thickness").Value
});
}
}
}
}
Xaml:
<Window x:Class="ReadPipeXMLDB.ReadDB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Standard Pipe Sizes" Height="849" Width="949" Loaded="Window_Loaded">
<Grid>
<!-- Manufactuer filter combobox -->
<ComboBox Name="mfgrComboBox1"
ItemsSource="{Binding Path=MfgNames}"
SelectedIndex="0"
Height="23" Width="286"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="20,20,0,0" SelectionChanged="mfgrComboBox1_SelectionChanged" Loaded="mfgrComboBox1_Loaded" />
<!-- Units combobox -->
<ComboBox Height="23" HorizontalAlignment="Left" Margin="320,20,0,0" Name="dimensionsComboBox2" VerticalAlignment="Top" Width="87" />
<!-- Pipe database display grid -->
<DataGrid Name="dataGrid1" IsReadOnly="True" AutoGenerateColumns="False" Margin="20,60,20,20" ItemsSource="{Binding Col}">
<DataGrid.Columns>
<DataGridTextColumn Header="Nominal" Binding="{Binding Path=Nominal}"/>
<DataGridTextColumn Header="Schedule" Binding="{Binding Path=Sched}"/>
<DataGridTextColumn Header="ID" Binding="{Binding Path=ID}"/>
<DataGridTextColumn Header="OD" Binding="{Binding Path=OD}"/>
<DataGridTextColumn Header="Wall Thickness" Binding="{Binding Path=WT}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
XML:
<DBRoot>
<Mfg name="A Manufac">
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
</Mfg>
<Mfg name="B Manufac">
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
<pipe>
<Nominal>testdata</Nominal>
<Schedule>testdata</Schedule>
<OD>testdata</OD>
<Wall_Thickness>testdata</Wall_Thickness>
<ID>testdata</ID>
</pipe>
</Mfg>
</DBRoot>
The PopulateGrid call is slow because I create a new ObservableCollection every time the combobox changes value. I'm not use to working with collections and linq so if someone can offer my a more robust alternative I'd appreciate it!
You can save yourself some trouble by binding directly to the XML file:
XAML
<Grid>
<Grid.DataContext>
<XmlDataProvider Source="DataBase.xml"/>
</Grid.DataContext>
<StackPanel>
<ComboBox ItemsSource="{Binding XPath=/DBRoot/Mfg}" Name="comboBox" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding XPath=#name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<DataGrid ItemsSource="{Binding ElementName=comboBox, Path=SelectedItem}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding XPath=Nominal}" Header="Nominal"/>
<DataGridTextColumn Binding="{Binding XPath=Schedule}" Header="Schedule"/>
<DataGridTextColumn Binding="{Binding XPath=OD}" Header="OD"/>
<DataGridTextColumn Binding="{Binding XPath=Wall_Thickness}" Header="Wall Thickness"/>
<DataGridTextColumn Binding="{Binding XPath=ID}" Header="ID"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
Oh my goodness. Your problem starts with ObservableCollection<string> MfgNames. String has no intelligence and you are building data from scratch every time. Use a class.
public class Mfg
{
public string Name { get; private set; }
public ObservableCollection <CPipeData> pipes { ....
Then in the detail you just bind
ItemsSounce="{binding ElementName=cbMfg Path=SelectedItem.Pipes}"
Look up Master Detail on MSDN.Microsoft.Com
If several Mfg use the same pipe then you would create a hashset with the relationship and pass the hashset to Mfg and use LINQ the filter from that single hashset. Override GetHash.

LoginWindow with Caliburn + MEF MVVM WPF

trying to use a login window with caliburn + mef
I get these two warnings as well
Warning 1 CA2000 : Microsoft.Reliability : In method 'AppBootstrapper.CreateContainer()', call System.IDisposable.Dispose on object 'container' before all references to it are out of scope. C:\adl\DotNetProjects\CaliburnTest\CaliburnTest\AppBootstrapper.cs 25 CaliburnTest
Warning 2 CA2000 : Microsoft.Reliability : In method 'AppBootstrapper.CreateContainer()', call System.IDisposable.Dispose on object 'new AggregateCatalog(Enumerable.Select<Assembly, AssemblyCatalog>(this.SelectAssemblies(), AppBootstrapper.CS$<>9__CachedAnonymousMethodDelegate1))' before all references to it are out of scope. C:\adl\DotNetProjects\CaliburnTest\CaliburnTest\AppBootstrapper.cs 25 CaliburnTest
Thanks in advance I know that caliburn is going to save me lots of time would love to get this to work!
In this project my IShell is an empty interface.
public class AppBootstrapper : Bootstrapper<IShell>
{
protected override IServiceLocator CreateContainer()
{
var container = new CompositionContainer(
new AggregateCatalog(SelectAssemblies().Select(x => new AssemblyCatalog(x)))
);
var batch = new CompositionBatch();
//batch.AddExportedValue<IWindowManager>(new WindowManager());
return new MEFAdapter(container);
}
}
[Export(typeof(IShell))]
public class MainViewModel : Conductor<IScreen>, IShell
{
readonly IWindowManager windowManager;
[ImportingConstructor]
public MainViewModel(IWindowManager windowManager)
{
this.windowManager = windowManager;
var result = windowManager.ShowDialog(new LoginWindowViewModel());
if (result != true)
{
Application.Current.Shutdown();
}
}
}
[Export(typeof(LoginWindowViewModel))]
public class LoginWindowViewModel :Screen
{
public LoginWindowViewModel()
{
}
public ObservableCollection<User> Users
{
get
{
if (_users == null)
{
_users = new ObservableCollection<User>(){new User("ADMIN","ADMIN","ADMIN")};
}
return _users;
}
}
public bool AuthenticateUser(string username, string pass)
{
Common.Authenticated.CurrentUser = Users.Where<User>(y => y.Login.Trim() == username.Trim()).FirstOrDefault(y => y.Pass.Trim() == pass.Trim());
if (Common.Authenticated.CurrentUser != null)
return true;
return false;
}
public bool Authenticated
{
get
{
if (Username == null || Username == String.Empty || Password == null || Password == String.Empty)
return false;
return AuthenticateUser(Username, Password);
}
}
public bool CheckAuthNeeded() {return true;}
private ObservableCollection<User> _users;
public void Login()
{ RequestClose(this, new SuccessEventArgs(true)); }
public string Username {get;set;}
public string Password {get;set;}
public event CloseDialogEventHandler RequestClose;
public delegate void CloseDialogEventHandler(object sender, SuccessEventArgs e);
}
LoginWindowView:
<Grid Height="Auto" Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="93*" />
<ColumnDefinition Width="185*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="10,10,0,0">
<Label Content="Username:" FontWeight="Bold" VerticalContentAlignment="Center" Margin="0,4,0,0" Padding="5,5,0,5" />
<Label Content="Password:" FontWeight="Bold" VerticalContentAlignment="Center" VerticalAlignment="Stretch" Margin="0,4,0,0" Padding="5,5,0,5" />
</StackPanel>
<StackPanel Grid.Column="1" Margin="0,10,10,0">
<dxe:ComboBoxEdit x:Name="User" ItemsSource="{Binding Users}"
Padding="0" Margin="5" DisplayMember="Login" ValueMember="Login" Validate="User_Validate" ValidateOnTextInput="True"
AutoComplete="True" ImmediatePopup="True" IncrementalFiltering="True" ShowEditorButtons="False" />
<dxe:PasswordBoxEdit x:Name="Pass" Margin="5" Validate="Pass_Validate" ValidateOnTextInput="False" InvalidValueBehavior="AllowLeaveEditor" />
</StackPanel>
<Button Content="Login" Grid.Row="1" Height="23" HorizontalAlignment="Center"
x:Name="Login" VerticalAlignment="Top" Width="75" IsDefault="True" Grid.ColumnSpan="2" />
</Grid>
LoginWindowView CodeBehind:
public partial class LoginWindowView
{
public LoginWindowView()
{
InitializeComponent();
}
private void User_Validate(object sender, ValidationEventArgs e)
{
if (e.Value == null)
{
e.IsValid = false;
return;
}
var _vm = GetDataContext();
var u = _vm.Users.FirstOrDefault<User>(x => x.Login.Trim() == ((string)e.Value).Trim().ToUpper());
if (u == null)
{
e.SetError("Invalid Login Name", DevExpress.XtraEditors.DXErrorProvider.ErrorType.Information);
_vm.Username = string.Empty;
e.IsValid = false;
}
else
_vm.Username = User.Text;
}
public LoginWindowViewModel GetDataContext()
{
return (LoginWindowViewModel)DataContext;
}
private void Pass_Validate(object sender, ValidationEventArgs e)
{
if (e.Value == null)
{
e.IsValid = false;
return;
}
var _vm = GetDataContext();
_vm.Password = ((string)e.Value).ToUpper();
if (_vm.Authenticated == false)
{
e.SetError("Wrong Password.", DevExpress.XtraEditors.DXErrorProvider.ErrorType.Critical);
e.IsValid = false;
}
}
private void Window_Closed(object sender, EventArgs ee)
{
GetDataContext().RequestClose -= (s, e) =>
{
this.DialogResult = e.Success;
};
}
private void Window_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue != null)
{
((LoginWindowViewModel)e.OldValue).RequestClose -= (s, ee) =>
{
this.DialogResult = ee.Success;
};
}
((LoginWindowViewModel)e.NewValue).RequestClose += (s, ee) =>
{
this.DialogResult = ee.Success;
};
}
}
public class User
{
public string FullName { get; private set; }
public string Login { get; private set; }
public string Pass { get; private set; }
public User(string fullName, string login, string pass) { FullName = fullName; Login = login; Pass = pass; }
public static User CreateUser(string fullName, string login,string pass)
{
return new User(fullName, login,pass);
}
}
public static class Authenticated
{
public static string AuthString { get; set; }
public static User CurrentUser { get; set; }
public static string UserId
{
get
{
if (CurrentUser != null)
return CurrentUser.Login;
return string.Empty;
}
}
}
MainView:
<Window x:Class="CaliburnTest.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainView" Height="300" Width="300">
<Grid>
<Label>Test</Label>
</Grid>
</Window>
Actually Rob Eisenberg of caliburn was very helpful and he helped me out with this issue.
The problem was that when I switched to caliburn the LoginView was the first window to be opened and it was closed before the MainView window was opened.
windows treats the first window opened as the mainwindow. and when the mainwindow is closed windows checks if other windows are open if not it closes the application.
He provided a possible solution of making the loginviewmodel the shell and closing it after opening the mainviewmodel.

Categories