WPF Entity framework datagrid refresh - c#

I started a new WPF project and on main Window I have a button. When I click it, I show a new window that displays as follows:
1 textBox (named tbTax)
1 Add Button
1 DataGrid (named dbGrid)
I obtained the DataGrid, by going to DataSources tab, and DRAG/DROP from there a table on the form/window.
When I run the app, it all works as planned. In the second window I see in the grid all my records from the database table.
NOW:
I did the Adding code for the "Add button". So when the user enters some text in the textBox (tbTax) and clicks on the Add button, the following code follows:
using (MyEntities context = new MyEntities())
{
TAX tax = new TAX();
tax.TAX1 = decimal.Parse(tbTax.Text);
context.TAXs.Add(tva);
context.SaveChanges();
tbTax.Text = "";
dbGrid.Items.Refresh();
}
So it should be obvious: I add an item to the database table through the entity framework.
But even if I added the refresh part at the end of the code... the grid does not refresh. So only if I exit the window and re-show it, only then I can see the new item I just Added.
EDIT
Following the drag/drop I ended up with this:
--XAML--
<DataGrid x:Name="tAXDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="10,157,10,35" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="tAXColumn1" Binding="{Binding TAX}" Header="TAX" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
--XAML.cs--
private void Window_Loaded(object sender, RoutedEventArgs e)
{
TestXXX.ArtDataSet artDataSet = ((TestXXX.ArtDataSet)(this.FindResource("artDataSet")));
// Load data into the table TAX. You can modify this code as needed.
TestXXX.ArtDataSetTableAdapters.TAXTableAdapter artDataSetTAXTableAdapter = new TestXXX.ArtDataSetTableAdapters.TAXTableAdapter();
artDataSetTAXTableAdapter.Fill(artDataSet.TAX);
System.Windows.Data.CollectionViewSource tAXViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("tAXViewSource")));
tAXViewSource.View.MoveCurrentToFirst();
}
Also, my Context is declared separately
namespace TestXXX
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class ArtEntities : DbContext
{
public ArtEntities()
: base("name=ArtEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<BOOK> BOOKs { get; set; }
public DbSet<TAX> TAXs { get; set; }
}
}
and the TAX class is
namespace TestXXX
{
using System;
using System.Collections.Generic;
public partial class TAX
{
public TAX()
{
this.BOOKs = new HashSet<BOOK>();
}
public int ID { get; set; }
public Nullable<decimal> TAX1 { get; set; }
public virtual ICollection<BOOK> BOOKs { get; set; }
}
}
What is wrong with this? How can I fix it?

First of all, I assume you are only in a learning stage, as putting all layers into UI control is not a good idea. Data layer (communication with database) should be separated from model and model should be separated from view. There is of course many other designs, but separation is a core issue in all of them.
As you are using WPF, you should know the ObservableCollection contained in System.Collection.ObjectModel. This collection notifies data user (mostly UI controls in wpf) about changes in collection as adding, removing etc.
So what you need to do is set ItemSource of DataGrid with ObservableCollection. All you need to do next is only add tax item into this collection and save it to the database. Nothing more.

The solution seems to be simple after all...
In the CS file of the Window
public partial class NewTAX : Window
{
ArtEntities db;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
db = new ArtEntities();
dbgrid.ItemsSource = db.TAXs.ToList();
}
... then after adding a new item, just set the ItemSource AGAIN like:
using (MyEntities context = new MyEntities())
{
TAX tax = new TAX();
tax.TAX1 = decimal.Parse(tbTax.Text);
context.TAXs.Add(tva);
context.SaveChanges();
tbTax.Text = "";
dbgrid.ItemsSource = db.TAXs.ToList();
}

Related

Adding new Row [at RunTime] in DataGridView replaces the previous added Row

I want (When Button Clicked) previous row still exist in datagridview while adding new row (such that user can add as many rows as he/she want at runtime in datagridview).
I found many questions on stackoverflow.com regarding rows of datagridview but i cannot figure out them according to my problem. Second, i am new in Dapper and all answers using ADO.NET.
I have an idea that there should be an easy way of such problem.
I bind my datagridview to Orders Class.
Here is my Orders class...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test
{
public class Orders
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string Brand { get; set; }
public string Category { get; set; }
public int Quantity { get; set; }
public int Price { get; set; }
}
}
Select Button Click Event is..
private void btn_select_Click(object sender, EventArgs e)
{
using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["Connection"].ConnectionString))
{
if (db.State == ConnectionState.Closed)
db.Open();
string query = "SELECT *FROM tbl_ItemDetail WHERE ItemName=#ItemName";
ordersBindingSource.DataSource = db.Query<Orders>(query, new { ItemName = txt_sell_item.Text });
}
}
When I enter ItemName as 'bread' then GUI is..
But when I enter ItemName as 'bjn' then first row replaces with newest row..
The main reason behind your problem lies within your button click event handler, You are binding DataSource every time the user clicks the button which gives you one record as expected. Instead of assigning DataSource, write a insert statement which inserts record within your grid. By doing this, your issue will be surely resolved.
I'm not giving solution with code because I don't have my pc right now.

WPF's DataGrid Sort Descriptions works only for static instances

I have a problem with sorting DataGrid items using SortDescriptions.
I had a static ObservableCollection that stored my data and after binding that collection to the DataGrid I was able to sort the items ascendingly by using SortDescription.
After a while I needed to change my code - a static instance of that collection was no longer sensible. After making those changes I've built the same DataGrid view but sorting the items was no longer working.
I need to create a DataGrid on WPF that would store my application's history records.
For that, I created some classes to be used as history records and then I bound that data to my DataGrid. Becuase those are historical records, I would like them to be shown to the user in an ascending order. Meaning the last item I've added will be shown at the very top.
This is my HistoryRecord class:
public class HistoryRecord {
public TransactionSender Sender { get; set; }
public string Description { get; set; }
public DateTime Timestamp { get; set; }
}
I've created a History class with an observable collection, as follows:
public static class History {
public static ObservableCollection<HistoryRecord> UserHistory { get; set; } =
new ObservableCollection<HistoryRecord>();
}
I chose to use an observable collection because that way I will be able to update the DataGrid more easily.
This is my xaml code for the DataGrid at my history tab:
<DataGrid x:Name="HistoryGrid" HorizontalAlignment="Left" AutoGenerateColumns="False" IsReadOnly="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Sender" Binding="{Binding Sender}"/>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Header="Timestamp" Binding="{Binding Timestamp}"/>
</DataGrid.Columns>
</DataGrid>
This is my C# code for the history tab:
public partial class HistoryTab : UserControl {
public HistoryTab() {
InitializeComponent();
HistoryGrid.ItemsSource = History.UserHistory;
HistoryGrid.Items.SortDescriptions.Add(new SortDescription("Timestamp", ListSortDirection.Ascending));
HistoryGrid.Items.Refresh();
}
}
The code above works just fine. It does sort my items ascendingly.
Last week I needed to change my code a little bit.
Up until now, it was fine to have a static History class but now I need to create multiple instances of that History class for several things.
That being said, the app as a whole still has a unified history record.
In order to save that unified history record I created a new class named HistoryUtil:
public static class HistoryUtil {
public static HistoryStorage AppHistory = new HistoryStorage();
}
The History class is now called HistoryStorage and it contains the observable collection just as before:
public class HistoryStorage {
public ObservableCollection<HistoryRecord> UserHistory { get; set; } =
new ObservableCollection<HistoryRecord>();
}
The xaml code from before stayed the same.
I updated the C# code for the tab as follows:
public partial class HistoryTab : UserControl {
public HistoryTab() {
InitializeComponent();
HistoryGrid.ItemsSource = HistoryUtil.AppHistory.UserHistory;
HistoryGrid.Items.SortDescriptions.Add(new SortDescription("Timestamp", ListSortDirection.Ascending));
HistoryGrid.Items.Refresh();
}
}
This updated code does not sort the items.
This code shows the items but without the ascending order of the timestamp.
I can't figure out how and why the view is not sorted ascendingly according to the timestamp.
This is basically the same code, there are only a few changes to the structure and the fact that I've changed the static instance to be non-static.
Here are some sample data that demonstrates the issue:
HistoryRecord action1 = new HistoryRecord() {
Sender = TransactionSender.User,
Timestamp = DateTime.Now,
Description="First record, should appear last"
};
action1.StoreRecord();
//After few seconds...
HistoryRecord action2 = new HistoryRecord() {
Sender = TransactionSender.User,
Timestamp = DateTime.Now,
Description="Second record, should appear first"
};
action2.StoreRecord();
Where the StoreRecord method is set like this:
//For the static class:
public void StoreRecord() {
History.UserHistory.Add(this);
}
//For the non-static class:
public void StoreRecord() {
HistoryUtil.AppHistory.UserHistory.Add(this);
}
This is the History DataGrid:
Please note this isn't about ascending or descending - I tried them both.
It just doesn't seem to sort the items anymore.
I added a breakpoint at HistoryGrid.Items.SortDescriptions and it doesn't seem to get there.
I would appreciate your help figuring out the problem.

how to pass data from an application page class to a ViewModel class WP8

In my WP8 application page,i am generating the data for my ViewModel that i am using to plot a chart.The generated data is passed to the ViewModel as a List.That is where i have a
bit of a problem because they are different classes and the list's visibility scope doesn't extend to the ViewModel's collection.
This is the data in the application page(sort of an example of what i'm doing):
namespace M
{
public partial class SampleRecord : PhoneApplicationPage
{
List<Record> r_List = new List<Record>();
public SampleRecord()
{
InitializeComponent();
}
private void PhoneApplicationPage_Loaded_1(object sender, RoutedEventArgs e)
{
//after proceesing the data
r_List.Add(1, 100);
r_List.Add(2, 200);
r_List.Add(3, 300);
}
}
}
The ViewModel class:
public class ViewModel
{
public ObservableCollection<Record> Collection { get; set; }
public ViewModel()
{
Collection = new ObservableCollection<Record>();
GenerateData();
}
private void GenerateData()
{
for(int i<0; i<r_List.Count; i++){\
this.Collection.Add(add elements of the list to the collection);
}
}
}
}
Tried declaring the List in the App.xaml.cs so that it is accessible eveywhere but it isn't recognized in the application page class.
Having your data source in a view is a huge no no if you are using MVVM. If you insist on having it there then you are not using MVVM.
However, if you just create a DependencyProperty in your view's code behind to hold the data collection, then you can just data bind to it from there. Let's assume your property is called Collection...
Collection = new ObservableCollection<Record>();
GenerateData();
... you could data bind to it from the view like this:
<ListBox ItemsSource="{Binding Collection, RelativeSource={RelativeSource
AncestorType={x:Type YourLocalPrefix:YourView}}}" ... />
However, I can't stress enough how important it is to separate your views from your data access.

WPF controls are not populated from custom method

In my WPF C# project I have two frames in the MainWindow. The first frame has a page with a DataGrid (bound to an XML file) in which I select an object of interest.
<Grid.Resources>
<XmlDataProvider x:Key="XmlData" Source="/DB.xml"/>
</Grid.Resources>
<DataGrid Name="dg"
SelectionChanged="dg_SelectionChanged"
AutoGenerateColumns="False"
ItemsSource="{Binding Source={StaticResource XmlData}, XPath=Data/Object}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding XPath=Type}"></DataGridTextColumn>
<DataGridTextColumn Binding="{Binding XPath=Number}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
In the second frame I open different pages (one at a time) according to the calculations I am going to perform with the selected object. At every SelectionChanged event a custom method MySub() is called, that initiates all the necessary calculations on the loaded page.
public partial class pg_DB : Page
{
public pg_DB()
{
InitializeComponent();
}
public void dg_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
switch (Var._loadedPage) // This variable holds the name of the loaded page.
{
case "pg_SCT":
pg_SCT c1 = new pg_SCT();
c1.MySub(); // Initiates the calculation process on pg_SCT page.
break;
case "pg_OCT":
pg_OCT c2 = new pg_OCT();
c2.MySub(); // Initiates the calculation process on pg_OCT page.
break;
}
}
}
The problem is that everything works well except the data visualization. Thus, for instance, every time the MySub() is called the List<> is being updated and the ItemsSource has the necessary items, yet they are not displayed in the DataGrid. Moreover, even simple TextBox1.Text = "Test" is not working. At the same time the same code works perfectly from the Button_Click method.
public partial class pg_SCT : Page
{
public pg_SCT()
{
InitializeComponent();
//grid.ItemsSource = myList (); (This works).
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//grid.ItemsSource = myList (); (This works).
//TextBox1.Text = "Test"; (This works).
}
public void MySub()
{
grid.ItemsSource = myList(); // Nothing happens (although debugging shows that List is updated and ItemsSource has necessary items).
TextBox1.Text = "Test"; // Textbox remains empty.
}
public class Author
{
public int ID { get; set; }
public string Name { get; set; }
}
private List<Author> myList()
{
List<Author> authors = new List<Author>();
authors.Add(new Author()
{
ID = Var._ID,
Name = Var._Name,
});
return authors;
}
}
I can’t find what is missing to populate DataGrid and TextBox from my custom method MySub().
Thank you for your time and consideration.
In your dg_SelectionChanged method, you're creating instances of your Page controls as local variables but not using them anywhere so they are just going out of scope. I'm guessing you probably have other instances you've created elsewhere that are the ones being displayed but based on the code here the ones calling MySub are never going to show up.

WPF DataBinding ObservableCollection<T> to DataGrid

I'm trying to create DataGrid in a separate UserControl whose DataContext is a List of T.
In the code behind, I create a List, populate the list, then send it to the constructor for the UserControl on which I have the DataGrid I am trying to populate.
The UserControl class is as follows.
public partial class QuotePreview : UserControl
{
private static SelectionList previewList = new SelectionList();
public SelectionList PreviewList
{
get { return previewList; }
}
public QuotePreview()
{
InitializeComponent();
}
public QuotePreview(SelectionList selectedOptions)
{
InitializeComponent();
previewList = selectedOptions;
QuotePreviewDataGrid.DataContext = previewList;
}
}
And the Xaml looks like:
<DataGrid Name="QuotePreviewDataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTextColumn Header="Model Number" Binding="{Binding ModelNumber}"/>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"/>
<DataGridTextColumn Header="List Price per Unit" Binding="{Binding Price}"/>
</DataGrid.Columns>
</DataGrid>
I've tried setting the ItemSource as well using
QuotePreviewDataGrid.ItemsSource = PreviewList;
I've also tried setting both the data context and the itemsource as well as refreshing:
QuotePreviewDataGrid.Items.Refresh();
The databinding I have set to listboxes in the rest of my application works perfectly. In the list boxes I have the itemsource set to {Binding} and the ListItems binding set to {Binding Property}. The datacontext for the listboxes set in the code behind.
My datagrid here is setup in the same manner, yet for some reason nothing is being displayed inside the grid.
When I go through the debugger and watch the flow of information, I can see the List of T, SelectionsList being created and passed to the constructor for the user control where the data grid lies. I can see that the DataContext is indeed being set and shows the items in the list, but when I go back to my appication and try to view the data grid, it's blank.
Any help would be greatly appreciated. I've been trying to wrap my mind around this problem for the last day and a half. Thanks!
UPDATE
The SelectionList is setup like:
public class SelectionList : List<Selection>
{
public List<Selection> availableSelections = new List<Selection>();
public List<Selection> AvailableSelections
{
get { return availableSelections; }
}
}
and a Selection is then defined by:
public class Selection : DependencyObject
{
public bool IsChecked { get; set; }
public string ModelNumber { get; set; }
public string Description { get; set; }
public string Price { get; set; }
}
When the application starts, I build a catalog of existing products (Selections). On different tabs, one for each product family, the datacontext for the products list box is initialized with with available products that it grabs from the catalog. Then pending which product a user selects, the available options or child selections associated with that product are populated into the appropriate list boxes, accessories and warranties.
Once a user selects the options they want, a button is clicked to preview the selected items which is supposed to populate the data grid explained above.
I can build the list of selected options, however when I try to set the data context of the data grid, nothing appears. The Lists for available selections are built and set to the appropriate data context the same way I am trying to do it for the data grid, however the data grid doesn't want to display my information.
UPDATE
So after some more debugging, I've narrowed the problem down a bit. The data binding works as it should. I have no real problems there, I don't think. However, the issue I'm running into now is what I believe to be 2 different instances of my User Control, but only the original is being displayed, not the updated copy.
Here's a copy of the class from about with a couple lines I added to help debug the problem.
public partial class QuotePreview : UserControl
{
private SelectionList _selectionList;
private SelectionList temp;
public QuotePreview()
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
}
private void QuotePreview_Loaded(object sender, RoutedEventArgs e)
{
_selectionList.SelectedOptions.Add(
new Selection
{
ModelNumber = "this",
Description = "really",
Price = "sucks"
});
}
public QuotePreview(SelectionList selectedOptions)
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
temp = selectedOptions;
_selectionList.AddRange(selectedOptions);
QuotePreview_Loaded();
}
private void QuotePreview_Loaded()
{
foreach (var options in temp.SelectedOptions)
{
_selectionList.SelectedOptions.Add(options);
}
QuotePreviewDataGrid.ItemsSource = _selectionList.SelectedOptions;
}
}
The implementation of the default constructor, is called every time the user control / tab, is clicked on. When that happens, _selectionList is set to the data context of the user control, followed by the Loaded Event which adds a line to my data grid.
In another user control where I select the options I want to add to my data grid user control, I click a button that creates a list of the options I want to be added and calls the custom constructor I wrote. Once the constructor finishes, it calls a custom Loaded Event method that I created for shits and giggles, that adds the selected options to my _selectionList.
Now once I click on the data grid user control again, it goes through the whole default process, and adds another default line.
If I go back a tab and say I want these options again and go back to the data grid, it again goes through the default process and adds another default line.
Whats most intriguing though is that I can see both of the selectionLists build since I dont clear the in between processes. I see a list build of the options i want to display and a list build of the default options build...
Oh, also, SelectionList does implement ObservableCollection.
I finally came up with a solution to the problem.
public static class QuotePreview
{
public static ObservableCollection<PurchasableItem> LineItems { get; private set; }
static QuotePreview()
{
LineItems = new ObservableCollection<PurchasableItem>();
}
public static void Add(List<PurchasableItems> selections)
{
foreach (var selection in selections)
{
LineItems.Add(selection);
}
}
public static void Clear()
{
LineItems.Clear();
}
}
public class QuoteTab : TabItem
{
public ObservableCollection<PurchasableItem> PreviewItems { get; private set; }
public QuoteTab()
{
Initialize()
PreviewItems = QuotePreview.LineItems;
DataGrid.ItemSource = PreviewItems
}
}
Try changing:
QuotePreviewDataGrid.DataContext = previewList;
to
this.DataContext = previewList;
My suspicion is that the ItemsSource="{Binding}" in your xaml is overriding the DataContext code in your constructor.
By changing the previewList to be DataContext of the entire UserControl, then the binding of the DataGrid's ItemsSource can correctly evaluate.
On a side note, I would start looking into the use of ObservableCollection<T> and the MVVM design pattern. An issue you might end up with is that your DataGrid doesn't update when the underlying list changes, using the ObservableCollection<T> will fix this.
Using the MVVM design pattern will give you a good separation of your logic and data (in this case your list and how it's loaded) from the physical display (the DataGrid)

Categories