Refreshing EF data binding in WPF - c#

I use Entity Framework ObjectResult coming from Execute method to bind data to a WPF control, like here:
using System;
using System.Data;
using System.Data.Objects;
using System.Windows;
using System.Linq;
namespace Microsoft.Samples.Edm
{
/// <summary>
/// Interaction logic for SalesOrders.xaml
/// </summary>
public partial class SalesOrders : Window
{
private AdventureWorksEntities context;
private int customerId = 277;
private void SalesOrdersForm_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate the ObjectContext.
context = new AdventureWorksEntities();
// Define a query that returns orders for a customer.
// Because lazy loading is on by default, SalesOrderDetails
// related to a SalesOrderHeader will be loaded when the query
// is executed.
var query = from o in context.SalesOrderHeaders
where o.CustomerID == customerId
select o;
// Execute the query and bind the result to the OrderItems control.
this.orderItemsGrid.DataContext = ((ObjectQuery)query).Execute(MergeOption.AppendOnly);
}
private void buttonClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
public SalesOrders()
{
InitializeComponent();
}
}
}
The example comes from MSDN
It work fine, but how do I refresh the binding? Either programmatically or when database changes?

The SalesOrdersForm_Loaded code should be seperated from this event.
Place this code in a function. and call it in form load. Now you can call this function on your requirement basis.
I hope it makes sense.
Edit
You can call this function on button click / Timer or any event based on your requirement to update the bindings

Related

C# I'm trying to create a web user control with custom classes, and I am getting a Object not set to an instance of an Object Error

I hope you can help me with this. I am creating an internal webforms asp.net site to display a list of internally used documents in different categories.
I decided to create a custom document class to put in a list to hold the documents, and then a custom web user control to display the documents wherever they want them on the site.
The documents class is in a general class file in my App_Code folder.
cabinet.cs
public class Document
{
private string _Url;
private string _Title;
public Document(string URL, string Title)
{
_Url = URL;
_Title = Title;
}
public string URL
{
get { return _Url; }
set { _Url = value; }
}
public string Title
{
get { return _Title; }
set { _Title = value; }
}
}
This code works just fine. Then in my user control I create a list of type document and initiate it in Page_Load(). Then I created a public method to add new documents to the list.
DocDisplay.ascx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class DocDisplay : System.Web.UI.UserControl
{
private List<Document> _DocList;
protected void Page_Load(object sender, EventArgs e)
{
_DocList = new List<Document>();
}
public void Add(string URL, string Title)
{
_DocList.Add(new Document(URL, Title));
}
public void WriteDocuments()
{
foreach (Document doc in _DocList)
{
Response.Write($"<span class='document'><a href='{doc.URL}'>{doc.Title}</a></span>");
}
}
}
I am getting the error in the add method. It says that my object is not to an instance of an object. But I do that in Page_Load.
index.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
pageDocs.Add("index.aspx", "Hello World!");
pageDocs.Add("index.aspx", "Red Rum");
pageDocs.Add("index.aspx", "Lorum Ipsum");
}
I have registered my user control in my index page.
<%# Register Src="~/DocDisplay.ascx" TagPrefix="uc" TagName="DocDisplay" %>
<uc:DocDisplay ID="pageDocs" runat="server" />
So I am not exactly sure why I am getting that error. As far as I can tell, there is nothing wrong with my code. If you could help I would greatly appreciate it.
Events get fired starting from the root of control hierarchy and end at the leaf nodes. Index.Page_Load is being called before DocDisplay.Page_Load has an opportunity to instantiate the list.
The _DocList field needs a value before it can be used by anything, so initialization needs to happen as early as possible. This is accomplished very easily with a field initializer. Declare and assign it all at once:
private List<Document> _DocList = new List<Document>();
When the Index class instantiates its child controls early in the page life cycle, _DocList will immediately have an object reference.
It's tempting to say, "Page_Init will be called sooner; I'll do it there." This may work at first, but if you do any dynamic control loading, you'll soon find out that it's a balancing act. A dynamically loaded control has to play event catch-up, so its Init event can be fired after statically loaded controls have started firing Load events. It's important to use each event for its purpose, and not for its timing, and use constructors (and field initializers) to initialize non-control class state.

WPF: Execute some code asynchronously in .NET 3.5

I have a MVVM WPF app. I have a window, let's say "LvWindow", with a listview that is loaded from data comming from a database. From main window "MainWindow", I have a menu, which has some options. When I select the option to access "LvWindow", it is open. Then from ViewModel, in the constructor I have a call to a database from which I request some data that then I load into the listview.
My goal is to make the process to request data from database and then load it in the listview asynchronous. I want this in order to not block the whole app, I mean, during this window is loaded, user can go to the main window menu and select to open another type of window. Windows are open in tabs.
While the process of requesting data from database and being loaded into listview in window "LvWindow", I show a splash saying "Loading" on it(in fact this is a rectangle with zindex set to a higher number to avoid user can interact with listview until it is completely loaded). This splash will be closed when the listview is loaded with the data comming from database.
So to make the process asynchronous, I know in winforms it can be done with delegates by using beginInvoke, endInvoke and callbacks methods, see here.
Also, another possibility is to use a background worker, like posted here.
So in WPF which is the best way to do it? using delegates as winforms or background workers?
ATTEMPT #1:
I have tried XANIMAX solution as this:
public class TestViewModel : BaseViewModel
{
private static Dispatcher _dispatcher;
public ObservableCollection<UserData> lstUsers
public ObservableCollection<UserData> LstUsers
{
get
{
return this.lstUsers;
}
private set
{
this.lstUsers= value;
OnPropertyChanged("LstUsers");
}
}
public TestViewModel()
{
ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
{
var result = getDataFromDatabase();
UIThread((p) => LstUsers = result);
}));
}
ObservableCollection<UserData> getDataFromDatabase()
{
return this.RequestDataToDatabase();
}
static void UIThread(Action<object> a)
{
if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
//this is to make sure that the event is raised on the correct Thread
_dispatcher.Invoke(a); <---- HERE EXCEPTION IS THROWN
}
}
but in line _dispatcher.Invoke(a) an exception is thrown:
TargetParameterCountException: the parameter count mismatch
UserData is my data model, it is a class with some public properties. Something like:
public class UserData
{
public string ID{ get; set; }
public string Name { get; set; }
public string Surname { get; set; }
// Other properties
}
so the problem is that the call to database is returning "RequestDataToDatabase" is returning a collection of UserData (ObservableCollection) so the exception is thrown.
I do not know how to solve it. Could you help me, please?
Final solution:
As XAMIMAX said in the comments:
Change the signature from static void UIThread(Action a) to static void UIThread(Action a)
modify UIThread((p) => LstUsers = result); by UIThread(() => LstUsers
= result);
As you can't await asynchronous methods in a constructor in C# 7.0 (but async Main is coming in 7.1) you can extract your async function calls to a separate function in your ViewModel and synchronously call this within your View's code-behind constructor, after you have created your ViewModel and assigned it to the View's DataContext:
public MainWindow()
{
this.vm = new MyViewModel();
this.DataContext = this.vm;
this.InitializeComponent();
this.vm.AsychronousFunctionToCallDatabase();
}
As XAMIMAX says, you want to implement a ViewModel to handle business logic between your View and your models. Then if your ViewModel implements INotifyPropertyChanged and and you set up Binding in your XAML to your properties in the ViewModel - then the display will refresh after the database call without blocking the UI thread. Note, if you have any collections populated from the database call then they should be of type ObservableCollection.
But as Kundan says, in your AsychronousFunctionToCallDatabase() function you should include an await statement or a create a Task on the line that calls the database - this will return control to the calling function (in this case, to the MainWindow constructor).
Here is one of the possible solutions for you.
In your View Model you would have something like this:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace VM
{
public class TestViewModel : BaseViewModel
{
private static Dispatcher _dispatcher;
List<object> ListToDisplay { get; set; }//INPC omitted for brevity
public TestViewModel()
{
ThreadPool.QueueUserWorkItem(new WaitCallback((o) =>
{
var result = getDataFromDatabase();
UIThread(() => ListToDisplay = result);
}));
}
List<object> getDataFromDatabase()
{
//your logic here
return new List<object>();
}
static void UIThread(Action a)
{
if(_dispatcher == null) _dispatcher = Dispatcher.CurrentDispatcher;
//this is to make sure that the event is raised on the correct Thread
_dispatcher.Invoke(a);
}
}
}
There are couple of options to run the method asynchronously.
async await - https://msdn.microsoft.com/library/hh191443(vs.110).aspx
Task Parallel Library :https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-based-asynchronous-programming

Relationship between ComboBox & ListBox

I have a DataSet setup like the following:
As can be seen, there's a relationship between both tables.
In my form page I have the following layout:
The combobox simply select the 'platform' and what I want to get is that the ListBox only shows records that belongs to the relationship, filtered by plataforma_id.
In the TableAdaptor of the Table 'SetupUrlConditions' I have set it up like the following:
When I run the app, the ListBox always shows all records instead of being filtered by the relationship. Changing the ComboBox selected item, the result is always the same.
So, Is there something missing in my code to accomplish this?
Thanks.
EDIT:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Backoffice
{
public partial class SetupRules : Form
{
public SetupRules()
{
InitializeComponent();
}
private void plataformasBindingNavigatorSaveItem_Click(object sender, EventArgs e)
{
this.Validate();
this.plataformasBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.plataformasDataSet);
}
private void SetupRules_Load(object sender, EventArgs e)
{
this.plataformasTableAdapter.Fill(this.plataformasDataSet.plataformas);
this.setupUrlConditionsTableAdapter.Fill(this.plataformasDataSet.SetupUrlConditions);
}
}
}
private void PlatformasCBO_SelectedValueChanged(object sender, EventArgs e)
{
if (PlatformasCBO.SelectedValue != null)
{
SiteUrlLstBox.DataSource = this.platformasDataSet.SetupUrlConditions.Where( p => p.platforma_id == (int)PlatformasCBO.SelectedValue).ToList();
}
}
Ok you made me dig for that one since I haven't done it in such a long time...
Combobox = Platformas. Properties to be set are DisplayMember = Plaformas, ValueMember = Id
ListBox just displaymember = condicion?... up to you from there.
Keep in mind that something is always selected with this setup it would need to be modified to have nothing selected initially but out of scope for the question

Close C# form but not close calling application?

What I have is a project that is called from our ERP. The ERP calls functions that are exposed in the C# project and info is passed back and forth. This works great, but I am having an issue with the "Cancel" button. When the cancel button is clicked I want it to jsut close the C# form and not return anything.... Pretty much just terminate any action in C# and sever the connection to the ERP. I have tried many forms of the environment and application commands but they also close the ERP as well as the C# form. Are there any recommendations of the best approach for this? I can send the dialog from the button back to the ERP and just have the ERP not do anything with a true value but I am wondering if there isn't a more efficient way to accomplish this. Thank in advance.
Edit: Here is the current code on the form that contains the buttons. The cancel button is and has been set with DialogResult = Cancel as well...
The problem is that certain functions will be called from the ERP when this form is instantiated. Using this.close() does close the form but it also returns values back to the ERP, which I do not want it to do.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace NavAutomation
{
public partial class NAVForm : Form
{
public NAVForm(string frmName, int maxLen)
{
InitializeComponent();
}
private void NAVForm_Load(object sender, EventArgs e)
{
}
public string RetVal1 { get; set; }
public string txtB1 { get; set; }
public int txtB1Len { get; set; }
private void button2_Click(object sender, EventArgs e)
{
this.RetVal1 = textBox1.Text;
this.Close();
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
Application.Exit();
}
}
}
What I ended up doing, which works great is default a bool to true, and pass back false on the cancel button/close button and then simply exited the calling code in our ERP and it works like a dream. Thanks for the comments.

linq to sql dynamic data modify object before insert and update

I'm using Dynamic Data and LINQ to SQL for some admin pages on a .NET 3.5 web app. All my admin tables have a CreatedBy, CreatedDate, UpdatedBy, and UpdatedDate.
I'm looking for a way to inject the setting of these properties before the objects are inserted and updated.
I've seen an object_inserting hook if you have a linq to sql datasource in the web form, but I'm using dynamic data...is there an easy way to generically set that? And I've also looked at modifying each of the partial classes for my admin objects, but the closest hook I see is to implement the OnValidate method with the Insert action. Any suggestions? TIA.
David Turvey has published a great example of adding in an OnSaving and OnSaved methods for your entities, check here: Adding OnSaving an OnSaved Events to LINQ to SQL Entities
By implementing the above on your entities, you can extend them with a partial class, e.g.
partial class MyAdminEntity : EntityBase
{
internal override OnSaving(ChangeAction changeAction)
{
if (changeAction == ChangeAction.Insert)
{
CreatedBy = "<username>";
CreatedDate = DateTime.Now;
}
else if (changeAction == ChangeAction.Update)
{
CreatedBy = "<username>";
CreatedDate = DateTime.Now;
}
}
}
I got tried, add your entity class to app_code, change the class to partial class, it works for me! Hope this help! Reference here.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.Objects;
using System.Data.Linq;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace NorthwindModel
{
public partial class NorthwindEntities
{
partial void OnContextCreated()
{
// Register the handler for the SavingChanges event.
this.SavingChanges += new EventHandler(context_SavingChanges);
}
// SavingChanges event handler.
private static void context_SavingChanges(object sender, EventArgs e)
{
var objects = ((ObjectContext)sender).ObjectStateManager;
// Get new objects
foreach (ObjectStateEntry entry in objects.GetObjectStateEntries(EntityState.Added))
{
// Find an object state entry for a SalesOrderHeader object.
if (entry.Entity.GetType() == typeof(Employee))
{
var usr = entry.Entity as Employee;
// Do your Business Logic here.
}
}
}
}
}
I know this is an old post, but this could help others in solving their problem.
There are some other ways to do that.
You can use this:
public partial class BasicModelDataContext : DataContext
{
partial void InsertEmployee(Employee instance)
{
instance.MyValue = "NEW VALUE";
Employee.Insert(instance);
}
partial void UpdateEmployee(Employee instance)
{
instance.MyValue = "NEW Update VALUE";
Employee.Update(instance);
}
}

Categories

Resources