How to trigger redraw of Xamarin BindableLayout - c#

I have a BindableLayout that uses a List<PricingLevel> as the datasource. One of the columns in the resulting layout uses a converter to derive a calculated result based on a unit price in the view model and the PricingLevel property of the datasource.
public bool IsTaxInclusive { get; set; }
public decimal UnitPrice { get; set; }
public List<PricingLevel> PricingLevels { get; set; }
If the IsTaxInclusive flag is switched I need to update the calculated results (note PropertyChanged events are raised). But because the PricingLevels don't change, I'm not sure how to trigger the update. At the moment I just refresh the PricingLevels list which works but is not ideal. Is there a simple way to force the update?

So the fix was simple. Rather than refresh the entire list, I used a FastObservableCollection and called its RaiseCollectionChanged() method.
public FastObservableCollection<PricingLevel> PricingLevels { get; set; }
...
PricingLevels.RaiseCollectionChanged();

Related

how to ensure that the editing (IsEdited or whatever) state of an EFCore entity is notified

I've got a class Invoice that has InvoiceRows.
public class Invoice
{
[Key]
public int? Id { get; set; }
public DateTime InvoiceDate {
get => invoiceDate;
set => PropertySet<DateTime>(value, ref invoiceDate);
}
private DateTime invoiceDate;
public List<InvoiceRow> Rows { get; } = new List<InvoiceRow>();
[NotMapped]
public bool IsEdited { get; set; } = false;
public void PropertySet<T>(T value, ref T field)
{
if (value.Equals(field)) return;
field = value;
IsEdited = true;
}
}
The Invoice is edited in a WPF graphical interface that need notifications when the invoice has been edited in order to activate the save button, for example.
(In a first implementation i thought that IsEdited was a viewmodel thing and I omitted it from the model and included in the viewmodel instead. Turns out to be quite complex when handling sub-records. However I've not yet fully realized which option the model or the viewmodel implementation is best, and this is possibly a second question.)
The IsEdited is easily managed at record level, but how to handle it at the InvoiceRow level?
The first idea that comes to mind is to notify back to the main record.
This in turn requires additional code to wire up notifications between Invoice and Rows.
Another idea is to leverage the DbContext state that holds in one place the required information instead of gathering it around sub-records.
Are there any other options left? One of the challages of such decision is to fully evaluate the consequences of each approach before hand. What are the pros/cons of different ways of handling this?
In my little experience of WPF I've read something about hirearchical viewmodels. Maybe they are suitable for this, handling the wrap-up at the viewmodel level?

windows wpf MVVC pattern, how to fit code logic in that pattern

I'm working now with converting existing winform app to WPF. I've never coded anything using MVVC pattern before and I have a hard time trying to figure out how to correctly set models, viewmodels and views in my specific case.
App that I want to convert is used for communicate with physical devices using serial port.
Let's first start with existing winform app. Almost any of the logic is in separate .dll file that expose interface which winform consumes. There are really no "code behind" while displaying data because dll already expose everything that is needed. There are only some additional functions in GUI for manipulation of the data, saving current data etc.
The thing is that I really don't understand how to fit this nicely in MVVM. As model as I understand I will need to create some let's say device that will have all the properties that are changeable and readable. And first question, if model needs to be concrete class or it may be some interface (which I already have) or maybe abstract device class (that implements this interface)?
Second question about ViewModels. I understand that ViewModel is somehow used for "glue" model data to view. And from that perspective in ViewModel I can put all the code that as a result format data from device to output it in some nice formats that will be easily databinded to View. But, here is a quirk. I think that this inteface that I'm now using in that .dll file fits nicely to be databinded in gui (or maybe not?).
Another question is about View itself. I understand that View wouldn't be aware of model etc. But could I put in view that code that I already have in winform GUI? (for saving data to csv files, for doing some measurements etc) or maybe I will need to create another model (I have no idea what it may be looks like).
And the last question is where to put all the logic for using serial port, for concrete implementation of all functions etc. I believe that models should be as easy as possible (like in mvc pattern) without any logic and so on (but again, if it should be in that way, maybe model should be only interface?). And ViewModels should have only code for data manipulation to expose data in friendly format to view and convert it back to model. So where rest of the logic should exist? Thank you all in advance for explaining this thing. And in order to put some code to my post, below is interface that I'm using in that .dll file
public interface IScope
{
event EventHandler NewDataInBuffer;
bool Destroy();
bool Connect();
bool Disconnect();
bool StartCapture();
bool StopCapture();
string ScopeName { get; }
IParameter<float> CurrentVoltageLimit { get; }
IParameter<int> DataSamplesPerDiv { get; }
List<IParameter<Config.Timebase>> AvailableTimebaseSettings { get; }
List<IParameter<Config.Coupling>> AvailableCoupleSettings { get; }
List<IParameter<Config.Slope>> AvailableTriggerSlopeSettings { get; }
List<IParameter<Config.VerticalSensitivity>> AvailableSenitivitySettings { get; }
List<IParameter<Config.TriggerMode>> AvailableTriggerModeSettings { get; }
List<IParameter<Config.RecordLength>> AvailableRecordLength { get; }
IParameter<Config.Timebase> TimeBase { get; set; }
IParameter<Config.Coupling> Couple { get; set; }
IParameter<Config.Slope> TriggerSlope { get; set; }
IParameter<Config.VerticalSensitivity> Sensitivity { get; set; }
IParameter<Config.TriggerMode> TriggerMode { get; set; }
IParameter<Config.RecordLength> RecordLength { get; set; }
int TriggerPosition { get; set; }
float TriggerLevel { get; set; }
}
public interface IParameter<T>
{
string ParameterName { get; }
string ParameterValue { get; }
string ParameterUnit { get; }
bool IsReadOnly { get; }
T GetParameter { get; }
}

Issue with lambda expressions in c# data retrieval

i'm writing a system to track observation values from sensors (e.g. temperature, wind direction and speed) at different sites. I'm writing it in C# (within VS2015) using a code-first approach. Although i've a reasonable amount of programming experience, I'm relatively new to C# and the code-first approach.
I've defined my classes as below. I've built a REST api to accept observation reading through Post, which has driven my desire to have Sensor keyed by a string rather than an integer - Some sensors have their own unique identifier built in. Otherwise, i'm trying to follow the Microsoft Contoso university example (instructors - courses- enrolments).
What I am trying to achieve is a page for a specific site with a list of the sensors at the site, and their readings. Eventually this page will present the data in graphical form. But for now, i'm just after the raw data.
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Sensor> Sensors { get; set; }
}
public class Sensor
{
[Key]
public string SensorName { get; set; }
public int SensorTypeId { get; set; }
public int SiteId { get; set; }
public ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int Id { get; set; }
public string SensorName { get; set; }
public float ObsValue { get; set; }
public DateTime ObsDateTime { get; set; }
}
and I've created a View Model for the page I'm going to use...
public class SiteDataViewModel
{
public Site Site { get; set; }
public IEnumerable<Sensor> Sensors { get; set;}
public IEnumerable<Observation> Observations { get; set; }
}
and then i try to join up the 3 classes into that View Model in the SiteController.cs...
public actionresult Details()
var viewModel.Site = _context.Sites
.Include(i => i.Sensors.select(c => c.Observations));
i used to get an error about "cannot convert lambda expression to type string", but then I included "using System.Data.Entity;" and the error has changed to two errors... on the 'include', I get "cannot resolve method 'include(lambda expression)'...". And on the 'select' i get "Icollection does not include a definition for select..."
There's probably all sorts of nastiness going on, but if someone could explain where the errors are (and more importantly why they are errors), then I'd be extremely grateful.
Simply you can you use like
viewModel.Site = _context.Sites
.Include("Sensors).Include("Sensors.Observations");
Hope this helps.
The way your ViewModel is setup, you're going to have 3 unrelated sets of data. Sites, sensors, and observations. Sites will have no inherent relation to sensors -- you'll have to manually match them on the foreign key. Realistically, your ViewModel should just be a list of Sites. You want to do
#Model.Sites[0].Sensors[0].Observations[0]
not something convoluted like
var site = #Model.Sites[0]; var sensor = #Model.Sensors.Where(s => SiteId == site.Id).Single(); etc...
Try doing
viewModel.Site = _context.Sites.Include("Sensors.Observations").ToList();
Eager-loading multiple levels of EF Relations can be accomplished in just one line.
One of the errors you reported receiving, by the way, is because you're using 'select' instead of 'Select'
And lastly, be aware that eager-loading like this can produce a huge amount of in-memory data. Consider splitting up your calls for each relation, such that you display a list of Sensors, and clicking, say, a dropdown will call an API that retrieves a list of Sites, etc. This is a bit more streamlined, and it prevents you from getting held up because your page is loading so much information.
Update
I've created a sample application for you that you can browse and look through. Data is populated in the Startup.Configure method, and retrieved in the About.cshtml.cs file and the About.cshtml page.. This produces this page, which is what you're looking for I believe.

Easy binding of dbcontext with or without WCF

Dear Programmers,
I have a question that I can't succeed to figure out, it is more related to how should I design the application, and until now I have always overcome this issue by writing a lot of code.
I have to design a silverlight application connected to a database, and I want to use a WCF service between these 2 for many reasons.
If I use the WCF service I will loose the dbcontext structure which is very great when we want to use CollectionViewSource with related fields to bind the xaml controls.
By example, lets say we have simple entities like that, generated from the edmx file: Resistor and ResistorCategories
public partial class ResistorCategories
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorCategories()
{
this.Name = "New";
this.ResistorsSet = new HashSet<ResistorsSet>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorsSet> ResistorsSet { get; set; }
}
public partial class ResistorsSet
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public ResistorsSet()
{
this.ResistorStockEntriesSet = new HashSet<ResistorStockEntriesSet>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Resistance { get; set; }
public Nullable<decimal> PowerRating { get; set; }
public Nullable<decimal> Price { get; set; }
public Nullable<decimal> Tolerance { get; set; }
public string SupplierCode { get; set; }
public string ManufacturerCode { get; set; }
public int ResistorCategories_Id { get; set; }
public Nullable<int> Suppliers_Id { get; set; }
public Nullable<int> Manufacturers_Id { get; set; }
public virtual ResistorCategories ResistorCategories { get; set; }
public virtual Suppliers Suppliers { get; set; }
public virtual Manufacturers Manufacturers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<ResistorStockEntriesSet> ResistorStockEntriesSet { get; set; }
}
In the xaml, it is very easy to place 2 datagrid and see the list of resistors which belong to a categorie:
In the xaml:
Then I bind each datagrid to its corresponding CollectionViewSource:
ItemsSource="{Binding Source={StaticResource resistorCategoriesViewSource}, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource resistorCategoriesResistorsViewSource}, UpdateSourceTrigger=PropertyChanged}"
And in the code behind file, with lazyloading enabled:
System.Windows.Data.CollectionViewSource resistorCategoriesViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("resistorCategoriesViewSource")));
//
_context.ResistorCategoriesSet.Load();
resistorCategoriesViewSource.Source = _context.ResistorCategoriesSet.Local;
So it can't be easier to display records with their related fields, but in this case the xaml client is querying the database directly and this is what I want to avoid with a WCF service, + adding some standardization and security.
With a WCF, first I have to generate the POCO objects with EF Power Tool, because it can't return IEnumerable by example because of its virtual property (even if I deactivate lazyloading and proxy).
Then I have to query and store in separated collections the POCO objects:
ObservableCollection && ObservableCollection
Then I bind each datagrid to its collection and monitor the selecteditem event to manually change the collection displayed in the datagrid for the resistors!
So I guess there is a solution to use a WCF together with the simplicity of the dbcontext!
Please help me! Thanks in advance
May be, you can implement your own "data provider", based on sample data provider: Entity Framework 5 Sample Provider. But you need to "tunnel" and serialize data to send/receive throught wcf.
Thx for your message, I think yes finally I will have to do sth like that, I am struggling trying to find an easy solution but I guess what I want to do is not possible.
Instead of trying to save time with the binding I should focus on saving time with the generation of data services. wcf data service is a good solution, I also tried codetrigger and I am actually trying to implement it with open ria services..... which seems to be buggy as hell!!
So I don't know honestly, I can do the data part using wcf data services, but I would like the silverlight app to retrieve also the name and roles on the currently authenticated user in the asp.net app in order to customized the UI. So I guess I have to use a 2nd wcf service just for that.
I am maybe dreaming but if possible I would like the silverlight app to receive a notification when a user logged in or out instead of querying the asp.net app all the times.
If I find a solution of course I will post it, because I think it is quite common when someone want to write a silverlight app.
So I have found a solution finally, it was worth searching even if the solution is easy: using WCF Data Services.
It's possible to query the database with all related entities and bind them in the xaml:
As example, consider an entity called ItemsCategory with a list of Items referring to it via a ItemCategory_Id field, and 2 others entities called ItemPrices and ItemImages referring Items via an Item_Id field:
ItemsCategory
Item
ItemPrices
ItemImages
It's not perfect but it looks like that.
So then if you want to place a datagrid for the ItemsCategory and see the related Items in a second datagrid, and the related ItemPrices and ItemImages in a third and fourth datagrid in your xaml you defined it like that:
<navigation:Page.Resources>
<CollectionViewSource x:Key="itemsCategoriesSetViewSource" d:DesignSource="{d:DesignInstance SvcCatalogDatabase:ItemsCategories, CreateList=True}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetViewSource" Source="{Binding Items, Source={StaticResource itemsCategoriesSetViewSource}}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsPricesSetViewSource" Source="{Binding ItemsPrices, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>
<CollectionViewSource x:Key="itemsCategoriesSetItemsSetItemsImagesSetViewSource" Source="{Binding ItemsImages, Source={StaticResource itemsCategoriesSetItemsSetViewSource}}"/>
</navigation:Page.Resources>
To load the entities you first define a collections like that:
public DataServiceCollection<ItemsCategories> ItemsCategoriesTracked { get; set; }
public CollectionViewSource ItemsCategories_CVSrc { get; set; }
And to query the database you just need to query ItemCategories with related entities:
ItemsCategories_CVSrc.Source = null;
ItemsCategoriesTracked.LoadAsync(this.SvcData.ItemsCategoriesSet.Expand("Items").Expand("Items/ItemsPrices,Items/ItemsImages"));
Of course you have defined a handler for the LoadCompleted event of the DataServiceCollection:
ItemsCategoriesTracked.LoadCompleted += new EventHandler<LoadCompletedEventArgs>((sender, e) => DataServiceCollection_LoadCompleted<ItemsCategories>(sender, e, ItemsCategories_CVSrc));
private void DataServiceCollection_LoadCompleted<T>(object sender, LoadCompletedEventArgs e, CollectionViewSource target)
{
if (e.Error == null)
{
if ((sender as DataServiceCollection<T>).Continuation != null)
{
(sender as DataServiceCollection<T>).LoadNextPartialSetAsync();
}
else
{
target.Source = (sender as DataServiceCollection<T>);
}
}
else
{
MessageBox.Show(string.Format("{0}: An error has occured: {1}", typeof(T).Name, e.Error.Message));
}
}
And you've linked the CollectionViewSource to the one defined in the XAML:
ItemsCategories_CVSrc = (CollectionViewSource)this.Resources["itemsCategoriesSetViewSource"];
VoilĂ ! Hope it helps someone!

Wpf - How to control observable collection updates

In the parent there is a Observable Collection PendingPayment that has a list of all pending payments of sales with a column amount paid.
Then the user can select a particular sale and open it in new child window.
The thing thats going wrong is if the user just edits the text box paid amount in child window and closes the window without saving the new paid amount to database,the observable collection containing Amount paid column in the parent window gets updated.
What I want is it the collection to get updated only when the values are updated in the database.
This can be achieved by creating a copy of your sale object when the user select it in the list, and then using this copy as the view model of your child view.
You will then be able to set the new values in the original object from your list only once the save button has been clicked and the database update succeed.
An other way to proceed if you need to edit only few of the object properties would be to create and editor object and use it as the child window's view model.
Something like this :
public class Sale
{
public int PaidAmount { get; set; }
public int Some { get; set; }
public int More { get; set; }
public int Properties { get; set; }
}
public class SaleEditor
{
private Sale _sale;
public int PaidAmount { get; set; }
public SaleEditor(Sale sale)
{
_sale = sale;
PaidAmount = sale.PaidAmount;
}
public void Save()
{
// update your data here
_sale.PaidAmount = PaidAmount;
}
}
If you need your original object to update the database, then the save method could first update the object and the revert the changes if DB update failed :
public void Save()
{
var oldAmount = _sale.PaidAmount;
_sale.PaidAmount = PaidAmount;
if (!SalesDB.Update(_sale))
_sale.PaidAmount = oldAmount;
// you could also read back the value from DB
}
Whenever possible (I've never see a reason why it cannot),for listing purpose use proxy or flatted objects, you can implement this using projections query. Then user select an item from a list and the only thing you need to grab is a key to load the full object with its required object graph as the use case might dictate.
Here is a sample implementation using Entity Framework and c# lambda expressions:
Using anonymous object:
var anonymousListProjection = DbContext.PendingPayments.Select( pp=>
new { pp.Order, pp.Amount})
Using a hardcoded proxy:
var hardcodedListProjection = DbContext.PendingPayments.Select( pp=>
new PendingPaymentProxy { Order = pp.Order, Amount = pp.Amount})
//To return an observable:
var observableColl = new ObservableCollection<PendingPaymentProxy>
(hardcodedListProjection.Tolist());
public class PendingPaymentProxy
{
public string Order { get; set; }
public decimal Amount{ get; set; }
}
Apart from avoiding possibles performance problems due to unintentional loading real objects, this way you only have to worry for your list when the user do save in the detail view.

Categories