MVVM and DBContext - how to put it together? - wpf

I'm trying to follow the MVVM pattern, however I spent some good time on this issue, googled a lot and checked stackoverflow as well... No working example found so far.
Basically, I've a simple application and want to retrieve and write data to SQL server. Here's my code:
//Model
public class Visitor
{
public string ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
//ViewModel
public class VisitorViewModel : ViewModelBase
{
public ObservableCollection<Visitor> _visitorDataCollection = new ObservableCollection<Visitor>();
public ObservableCollection<Visitor> VisitorDataCollection
{
get { return _visitorDataCollection; }
set { _visitorDataCollection = value; }
}
private string _firstName = "";
private string _lastName = "";
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
public VisitorViewModel()
{
}
}
}
//VisitorContext class that represents a database context
public partial class VisitorContext : DbContext
{
public VisitorContext()
: base()
{
}
public DbSet<VISITOR> Visitors { get; set; }
}
}
Nothing really fancy. However, I cannot put it "together". How to complete that to retrieve all visitors and add a new one?
Could someone point me to the right direction?

Just a simple example how make it all to life.
Add some commands to VM:
public ICommand Add {get; private set;}
In constructor:
public VisitorViewModel()
{
using(var context = new VisitorContext())
{
//fill collection with initial data from DbContext
context.Visitors.ToList().ForEach(_visitorDataCollection.Add);
}
//setup add command, here I'm using MVVM Light like you
Add = new RelayCommand(()=> {
using(var context = new VisitorContext())
{
_visitorDataCollection.Add(context.Visitors.Add(new Visitor {
FirstName = this.FirstName,
LastName = this.LastName //read values from model properties
});
}
});
}
That's it, all you need to do is bind this ViewModel to appropriate View.

Related

Execute RaiseCanExecuteChanged from 'subclass'

So I have the following setup:
PLANNING:
public class Planning : ViewModelBase
{
public Planning()
{
AddNewActivityCommand = new RelayCommand(AddActivity, CanAddActivity);
}
public ObservableCollection<PlanningItem> PlanningItems { get; set; }
public PlanningItem SelectedPlan { get; set; }
#region AddNewActivity
public RelayCommand AddNewActivityCommand { get; private set; }
private bool CanAddActivity()
{
if (!PlanningItems.Any())
{
return true;
}
if (string.IsNullOrEmpty(PlanningItems[PlanningItems.Count - 1].Activities) != true ||
PlanningItems[PlanningItems.Count - 1].DhpRepresentativeSelected != null)
{
return true;
}
return false;
}
private void AddActivity()
{
PlanningItems.Add(new PlanningItem());
AddNewActivityCommand.RaiseCanExecuteChanged();
}
#endregion
}
PLANNING ITEM:
public class PlanningItem : ViewModelBase
{
private string _activity;
public ObservableCollection<OutreachUser> DhpRepresentativeSource
{
get
{
var userSource = new ObservableCollection<OutreachUser>();
using (var context = new Outreach_Entities())
{
var query = from a in context.UserInfoes
join b in context.PersonalInfoes on a.UserIdentity equals b.PersonIdentity
join c in context.PersonalTitles on b.TitleLink equals c.TitleIdentity into cGroup
from c in cGroup.DefaultIfEmpty()
select new OutreachUser
{
PersonLink = a.UserIdentity,
Username = a.Username,
FirstName = b.FirstName,
MiddleInitial = b.MiddleInitial,
LastName = b.LastName
};
foreach (var result in query)
{
userSource.Add(result);
}
return userSource;
}
}
}
public OutreachUser DhpRepresentativeSelected { get; set; }
public DateTime PlanningDate { get; set; }
public TimeSpan PlanningStart { get; set; }
public TimeSpan PlanningEnd { get; set; }
public int PlanningTotalHours { get; set; }
public string Activities
{
get
{
return _activity;
}
set
{
_activity = value;
RaisePropertyChanged(nameof(Activities), "", _activity, true);
}
}
}
I have a ListBox bound to the PlanningItems Observable Collection.
I want to be able to add a new item to the list if the following criteria are met:
The Planning Items Collection is empty.
The last item in the Planning Items Collection has a DhpRepresentativeSelected that is not null.
The last item in the Planning Items Collection has some text in the Activities string.
The first item is easy enough because I call AddNewActivityCommand.RaiseCanExecuteChanged(); after I add a new item from an empty list.
Now I need to call the AddNewActivityCommand.RaiseCanExecuteChanged(); from within the PlanningItem ViewModel, but it does not have access rights to the command.
Clueless pointed me to the answer.
What I did was inside of my Planning ViewModel I created an internal Method that called the AddNewActivityCommand.RaiseCanExecuteChanged() method. I think called that method from within the PlanningItems ViewModel.

How to use an object as a data source c#

I'm trying to figure out how to make an object data source where I can select which columns to display in Visual Studio and all that. Here's what I have so far, but I'm not sure what else I'm supposed to do?
public class ItemData
{
public string ItemName { get; set; }
public string Description { get; set; }
public string Quantity { get; set; }
public string ManuPartNumber { get; set; }
public string ListID { get; set; }
public string VendorRef { get; set; }
public string VendorName { get; set; }
public string EditSequence { get; set; }
public string UPC { get; set; }
}
public class ItemDataSource : IEnumerable<ItemData>
{
private ICollection<ItemData> list;
public ItemDataSource()
{
try
{
list = QBCom.GetItemList();
}
catch (Exception e)
{
list = new List<ItemData>();
}
}
public ItemDataSource(IEnumerable<ItemData> data)
{
list = data.ToList();
}
public IEnumerator<ItemData> GetEnumerator()
{
foreach (var item in list)
{
yield return item;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable<ItemData>)this).GetEnumerator();
}
}
I am not talking about ASP.net, I'm talking about stuff like DataTables and so forth. Just a regular winforms program.
I assume you want to bind to a DataGridView if so I would inherit from BindingSource and set the list to the DataSource. You can then set the datagridview DataSource to the object to view the columns in the visual studio properties window.
public class ItemDataSource : BindingSource
{
private ICollection<ItemData> list;
public ItemDataSource()
{
try
{
list = QBCom.GetItemList();
}
catch (Exception e)
{
list = new List<ItemData>();
}
this.DataSource = list;
}
public ItemDataSource(IEnumerable<ItemData> data)
{
list = data.ToList();
this.DataSource = list;
}
}

What is the right way to save and restore a disconnected entity using code first?

So that I can store the user's screen preferences, I have ScreenSettings entity that I want to retrieve when the program starts and save when the program ends.
For this reason I don't want to keep the context open.
I am wondering about the best way to do this.
I have tried the following
however I am not comfortable with the SaveSettings function because it deletes and re-adds the object.
How do I save changes to the object without actually replacing it?
namespace ClassLibrary1
{
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
//Domain Class
public class ScreenSetting
{
#region Properties
public int Id { get; set; }
[Required]
public int WindowLeft { get; set; }
[Required]
public int WindowTop { get; set; }
#endregion
}
// Context
public class Context : DbContext
{
#region Properties
public DbSet<ScreenSetting> ScreenSettings { get; set; }
#endregion
}
// UI
public class UI
{
#region Public Methods
// Get the settings object
public ScreenSetting GetSettings(int SettingsId)
{
var Db = new Context();
ScreenSetting settings = Db.ScreenSettings.Find(SettingsId);
if (settings == null)
{
settings = new ScreenSetting { Id = SettingsId, WindowTop = 100, WindowLeft = 100 };
Db.ScreenSettings.Add(settings);
}
Db.Dispose();
return settings;
}
// Save the settings object
public void SaveSettings(ScreenSetting settings)
{
var Db = new Context();
ScreenSetting oldSettings = Db.ScreenSettings.Find(settings.Id);
if (oldSettings == null)
{
Db.ScreenSettings.Add(settings);
}
else
{
Db.ScreenSettings.Remove(oldSettings);
Db.ScreenSettings.Add(settings);
}
Db.Dispose();
}
public void test()
{
ScreenSetting setting = this.GetSettings(1);
setting.WindowLeft = 500;
setting.WindowTop = 500;
this.SaveSettings(setting);
}
#endregion
#region Methods
private static void Main()
{
var o = new UI();
o.test();
}
#endregion
}
}
You ran into a common pattern, update or insert, which is so common that it's got a name: upsert. When a pattern is common, usually there also is a common solution.
In System.Data.Entity.Migrations there is an extension method AddOrUpdate that does exactly what you want:
public void SaveSettings(ScreenSetting settings)
{
using (var db = new Context())
{
db.ScreenSettings.AddOrUpdate(settings);
db.SaveChanges();
}
}

How to solve a HasMany relation in MVVM to show up in one row in a datagrid

i have a class Auswahl with some plain properties and a property RefFilters of type
List<RefAuswahlFilter>
What i want to achieve is: Display all Auswahl Properties in a datagrid with all RefFilter items in ONE row. the Problem is, the count of RefFilter is different from auswahl to auswahl object. in the past i use a datatable as the collection source. there i just added the MAX reffilters count as columns.
now i want to achieve this without a datatable, with something like "dynamic" Properties or anything.
public class Auswahl
{
public Auswahl()
{
this.RefFilters = new List<RefAuswahlFilter>();
}
public virtual string Beschreibung {get; set; }
public virtual long Id { get; set; }
public virtual string Programm { get; set; }
public virtual string Returnkey { get; set; }
public virtual string Variante { get; set; }
//RefFilters contains a Rank and a Filter Property
public virtual IList<RefAuswahlFilter> RefFilters { get; set; }
public class AuswahlVM
{
...
public ObservableCollection<Auswahl> Auswahlliste { get; private set; }
public void FillList()
{
try
{
var l = session.CreateCriteria(typeof(Auswahl)).List<Auswahl>().Where(x =>!String.IsNullOrEmpty(x.Returnkey));
this.Auswahlliste = new ObservableCollection<Auswahl>(l);
}
catch (Exception ex)
{
}
}
well i ended up in creating a helper class with an indexer and map my original list in that helper class
public class RefAuswahlFilterListe
{
private IList<RefAuswahlFilter> refFilters;
private Auswahl auswahl;
public RefAuswahlFilterListe(Auswahl refauswahl, IList<RefAuswahlFilter> filter)
{
this.refFilters = filter;
this.auswahl = refauswahl;
}
public string this[string rank]
{
get
{
long index;
if(Int64.TryParse(rank, out index))
{
var result = this.refFilters.FirstOrDefault(x => x.Filterrank == index);
return result != null ? result.Filter : String.Empty;
}
return String.Empty;
}
set
{
long index;
if (Int64.TryParse(rank, out index))
{
var result = this.refFilters.FirstOrDefault(x => x.Filterrank == index);
if(result == null)
this.refFilters.Add(new RefAuswahlFilter(){Auswahl = auswahl,Filter = value, Filterrank = index});
else
result.Filter = value;
}
}
}
}
<DataGridTextColumn Header="Filter1"
ToolTipService.ToolTip="Filter Spalte"
Binding="{Binding Path=Filter[1]}">
</DataGridTextColumn>
<DataGridTextColumn Header="Filter2"
ToolTipService.ToolTip="Filter Spalte"
Binding="{Binding Path=Filter[2]}"/>
i really dont know if this is the way to go. and i still has the problem to create wpf DataGridTextColumns dynamic (maybe in code behind?) cause it must be at least so much Columns like the highest count of RefFilters.

property names are different from original Object in the silverlight

Following is part of service layer which is provided by WCF service :
[Serializable]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
public class Service1 : IService1
{
public List<WaitInfo> GetWaitingList()
{
MyDBDataContext db = new MyDBDataContext();
var query = from w in db.WAIT_INFOs
select new WaitInfo
{
TagNo = w.PATIENT_INFO.TAG_NO,
RoomName= w.ROOM_INFO.ROOM_NAME,
PName= w.PATIENT_INFO.P_NAME
};
List<WaitInfo> result = query.ToList();
return result;
}
And following is codebehind part of UI layer which is provided by Silverlight
public MainPage()
{
InitializeComponent();
Service1Client s = new Service1Client();
s.GetWaitingListCompleted +=
new EventHandler<GetWaitingListByCompletedEventArgs>( s_GetWaitingListCompleted);
s.GetWaitingListAsync();
}
void s_GetWaitingListCompleted(object sender,
RadControlsSilverlightApplication1.ServiceReference2.GetWaitingListByCompletedEventArgs e)
{
GridDataGrid.ItemsSource = e.Result;
}
And following is xaml code in Silverlight page
<Grid x:Name="LayoutRoot">
<data:DataGrid x:Name="GridDataGrid"></data:DataGrid>
</Grid>
It is very simple code, however what I am thinking weird is property name of object at "e.Result" in the code behind page.
In the service layer, although properties' names are surely "RoomName, PName, TagNo", in the silverlight properties' names are "roomName, pName, tagNo" which are private variable name of the WaitingList Object.
Did I something wrong?
Thanks in advance.
Unless you specifically decorate your class with the DataContract attribute (which you should, instead of Serializable) then a default DataContract will be inferred. For normal Serializable types, this means the fields will be serialized as opposed to the properties.
You can markup your class in either of the following two ways. The latter will use the property accessors when serializing/deserializing your object which may be very useful or be a hassle depending on your circumstances.
[DataContract]
public class WaitInfo
{
[DataMember(Name="RoomName")]
private string roomName;
[DataMember(Name="PName")]
private string pName;
[DataMember(Name="TagNo")]
private string tagNo;
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
public string PName
{ get { return pName; } set { this.pName = value; } }
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}
The method I prefer:
[DataContract]
public class WaitInfo
{
private string roomName;
private string pName;
private string tagNo;
[DataMember]
public string RoomName
{ get { return roomName; } set { this.roomName = value; } }
[DataMember]
public string PName
{ get { return pName; } set { this.pName = value; } }
[DataMember]
public string TagNo
{ get { return tagNo; } set { this.tagNo = value; } }
}

Resources