After several weeks of trying to improve the performance of my application I'm asking the question here:
Scenario:
A View is created when the user selects a Person out of a DataGrid. The View contains a ListView as menu and a ContentControl where custom UserControls are displayed.
My problem occurs when the DataContext of the view is set - it takes 31 (!!!) seconds to finish the InitializeComponent-Method when set in the constructor (see image below).
However, if I don't set the DataContext to the ViewModel, as shown in the image above, the method is finished in the blink of an eye.
I already tried to optimize my XAML and paid attention to the performance of every control but it did not help.
My class-structur of the important ViewModels for the View is as follows:
PersonViewModel : ViewModelBase (used when a normal person is openend)
InteressentViewModel : PersonViewModel
StudentViewModel : InteressentViewModel
In my view I have bindings on Properties and values of the ViewModel like Collections, simple values like Strings, and so on. For the simple values the binding is set to the Context Property loaded from EF.
My Question:
What could be the reason that setting the DataContext takes so much time?
EDIT 1:
It also takes so long when I set the DataContext after the Control is Loaded, in the "Loaded" Event.
EDIT 2:
Image of Profiler:
This is how it looks, when I set the DataContext in the Loaded event (Time is needed for AppCode).
When I set it in the Constructor, the InitializeComponent takes ~30secs and the Profiler states the time is needed for parsing (see comments). Constructor of ViewModel is finished after 0,5 secs in Debug-Mode.
EDIT 3:
this is how my constructor of the Student-ViewModel looks like:
public StudentViewModel_v2(T_StudentInVertiefung student) : base(student.T_Interessent)
{
// Simple Initialisations
// loading the context object from the DataBase to a T_StudentInVertiefung Object from the viewModel
_Student = student;
... some more code
// List initialisations
... about 20 ObservableCollections
// thesis values
AvailableThesisStates = new ObservableCollection<T_Abschlussarbeitsstatus>();
AvailableGrades = new ObservableCollection<T_Gesamtpraedikat>();
AvailableLecturers = new ObservableCollection<T_Vortragender>();
// set the type of person
Typ = PersonTyp.STUDENT;
// Load data values
// test, if a connection is possible
if (IngeniumEducationEntities.TestConnection())
{
// available options
// loading values for ComboBox Selection
#region available options
AvailableStates = new List<T_Personenstatus>(_dbContext.
T_Personenstatus.OrderBy(s => s.Bezeichnung));
#endregion
}
// commands
... several command initialisations
}
the class itself is derived from another ViewModel where the constructor looks about the same. Calling the constructor is finished after ~0,5s in DebugMode.
The Binding in the View looks like this:
<TextBox Text="{Binding Person.Vorname, UpdateSourceTrigger=LostFocus}" />
For each type of person (normal, student, alumni, e.g.) different views are available. To bind to a column from the database, e.g. the first name, I put the Binding on the Context-Property stored in the ViewModel like in the code above.
What I forgot say is that it doesn't takes much time everytime when opening a new view. Sometimes it's finished right away and anothertime it takes ~30s.
If you moved the datacontext assignment to Loaded eventhandler and the performance hit is moving to Loaded event too, then you have a long running data access in your properties which let the binding process slow down.
Try to load the data from database asynchronously before/in parallel and change your properties (getter) to present only already loaded data, e.g. stored/cached data in a backing field of your view model.
Check your data access to the database too. Maybe you can improve the database access too by adding proper indexes on the involved tables.
Related
Here is the scenario:
I have an Edit Dialog form with a BindingSource and some data bound text boxes on it:
I pass an entity to the form constructor and it gets loaded into BindingSource.DataSource which causes the data bound controls to show the values of properties.
The problem is as the user edits the values in TextBox controls and Validating events get passed, the data source gets changed though it is not applying to DB but it still can confuses the user as he sees the edited values on the List Form, till next application restart.
so the question is: How to prevent binding source from reflecting changes instantly or how to roll them back?
I inherited the binding source and created a new binding source like this:
public class SuperBindingSource:BindingSource
{
#region Properties
public object DataSourceBeforeChange { get; private set; }
#endregion
#region Methods
public void ResetChanges()
{
this.DataSource = this.DataSourceBeforeChange;
}
#endregion
protected override void OnDataSourceChanged(EventArgs e)
{
base.OnDataSourceChanged(e);
DataSourceBeforeChange=this.DataSource.DeepClone();
}
}
Though I am not sure if it is a good approach.
As an option, when setting up data-bindings, you can set them to update data source never.
Then at the point that you want to apply changes, for example when pressing OK button, you can set data-bindings to update data source on property change and then call end edit method of the binding source.
For Cancel button, you don't need to do anything, because the data source is not updated.
Example
In form load event:
this.BindingContext[bindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b=>b.DataSourceUpdateMode= DataSourceUpdateMode.Never);
When pressing OK:
this.BindingContext[productBindingSource].Bindings.Cast<Binding>().ToList()
.ForEach(b => b.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged);
productBindingSource.EndEdit();
You can download/clone the full source code:
r-aghaei/SuspendDataBindingExample
You can use the SuspendBinding method after the values are loaded.
After that the values should not update the source until you call ResumeBinding:
SuspendBinding and ResumeBinding are two methods that allow the temporary suspension and resumption of data binding in a simple-binding scenario. You would typically suspend data binding if the user must be allowed to make several edits to data fields before validation occurs. For example, if one field must be changed in accordance with a second, but where validating the first field would cause the second field to be in error.
According to the documentation you should be able to use this with your textboxes. If the user clicks Ok to save the values you resume the binding and if he cancels you don't.
I just can't figure this out. So I will try describe my problem best I can.
I am building application using MVVM pattern. I have user control AAAView with viewmodel AAAViewModel that is used to fill data class CDataClass. I also have main window MainView and its viewmodel MainViewModel. Next I have window DialogView with DialogViewModel.
So now MainViewModel (that has its own user control) creates DialogViewModel (with another instance of user control). How can I transfer data in CDataClass between these two user controls? I tried to create property in AAAViewModel that would hold instance of MainViewModel or DialogViewModel so I can pass data to it but I get stuck because I couldn't make it as dependency property.
My goal is to make user control that can be used in different views which can have different data in underlaying CDataClass.
Just to clarify... I am using user control as <views:GeneralInfoView Grid.Row="0" /> and don't know how to share data between two different instances of the same user control in different views. Any point to some pattern or method would be much appreciate.
Thank you for help.
I don't think it's ideal that you've got your application architecture diagrammed as relationships among views; I think a better way to think about it is as a set of relationships among viewmodels, with the views hanging off that tree as needed. When you think about it that way, "how does data get passed" gets a lot simpler. A view is just a conduit between a viewmodel and the user. You don't design a house as a set of windows and telephones and then try to figure out the floor plan from that. You start with what the house does and how people will live in it.
So this is easy:
Some viewmodels have an AAViewModel property. There may be all kinds of simple or complicated views on those viewmodels; if a view wants to let the user edit the viewmodel's AAViewModel stuff, then it includes an AAView bound appropriately to the viewmodel's AAViewModel. Your MainViewModel and DialogViewModel are both big complicated interactive views that want to let somebody edit their vm's AAViewModel stuff.
If MainViewModel is DialogViewModel's parent, or created a temporary instance of DialogViewModel just to put in a modal dialog, then MainViewModel would show the dialog, and have a look at dialogVM.AAVM.CData.IsDirty to decide what to do with it. Or maybe it gives dialogVM.AAVM a new CDataClass instance before showing the dialog (maybe a clone of its own instance), and if ShowModel() returns true, then it does something with dialogVM.AAVM.CData.
The point is that once your viewmodels are driving everything, it becomes relatively simple for them to communicate with each other. Parent-child is easy: Parent gives stuff to the child and looks at what the child brings back. A viewmodel can subscribe to another viewmodel's PropertyChanged event; a parent viewmodel can monitor its children; when something happens on a child, the parent can decide whether to update a sibling. In general, children should not know anything at all about their parents; this makes it much easier to reuse child viewmodels in disparate contexts. It's up to parents to decide what to do with that information.
All AAViewModel knows is that somebody handed him a copy of CDataClass; he updates his public properties accordingly. Then somebody else (probably AAView, but he doesn't know) hands him some changes by setting his properties; he updates his CDataClass instance accordingly. After a while, unknown to him, one viewmodel or another comes and looks at that CDataClass.
And communication between views and viewmodels happens via bindings.
UPDATE
It turns out that you're creating viewmodels in your views, and as a result you have no idea how the parent can get to them. And now you know why it's not a good idea to create child view viewmodels that way.
Here's how you do child view/viewmodels in the viewmodel-centric design I described above:
First, get rid of whatever you're doing to create the child viewmodels inside the view.
Second, create a DataTemplate for the child viewmodel type. This should go in a resource dictionary which is merged into the resources in App.xaml, but it's so simple that it won't kill you if you get lazy and just paste it into the Resources of the two different views where it's used.
I don't know what your namespace declarations look like. I'm going to assume that views are in something called xmlns:view="...", and viewmodels are in something called xmlns:vm="...".
<DataTemplate DataType="{x:Type vm:AAAViewModel}">
<view:AAAView />
</DataTemplate>
Now, you can assign an AAAViewModel to the ContentProperty of any control that inherits from ContentControl (and that's most of them), and the template will be instantiated. That means that XAML will create an AAAView for you and assign that instance of AAAViewModel to the DataContext property of the AAAView that it just created.
So let's create a child AAAViewModel next, and then we'll show it in the UI.
public class DialogViewModel
{
// You can create this in DialogViewModel's constructor if you need to
// give it parameters that won't be known until then.
private AAAViewModel _aaavm = new AAAViewModel();
public AAAViewModel AAAVM
{
get { return _aaavm; }
protected set {
_aaavm = value;
OnPropertyChanged(nameof(AAAVM));
}
}
And now we can display AAAVM in DialogView:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl
Content="{Binding AAAVM}"
Grid.Row="0"
/>
<StackPanel Orientation="Vertical" Grid.Row="1">
<!-- Other stuff -->
</StackPanel>
</Grid>
Now how does MainViewModel get in touch with a DialogViewModel? In the case of dialogs, since they have a finite lifespan, it's not actually a big deal to let them create their own viewmodels. You can do it either way. I generally lean towards having it create its own as in the second example below.
Not quite the same, but close. First, once again, get rid of whatever you're doing where the dialog creates its own viewmodel.
MainViewModel.cs
public CDataClass CDC { /* you know the drill */ }
public void ShowDialog()
{
var dvm = new DialogViewModel();
// Maybe this isn't what you want; I don't know what CDataClass does.
// But I'm assuming it has a copy constructor.
dvm.AAAVM.CDC = new CDataClass(this.CDC);
if (DialogView.ShowDialog(dvm).GetValueOrDefault())
{
CDC = dvm.CDC;
}
}
Note that this next one is view codebehind, not viewmodel.
DialogView.xaml.cs
public static bool? ShowDialog(DialogViewModel dvm)
{
var vw = new DialogView() { DataContext = dvm };
return vw.ShowDialog();
}
Now, you could let the dialog continue creating its own viewmodel; in that case you would give it a public property like this:
public DialogViewModel ViewModel => (DialogViewModel)DataContext;
And a ShowDialog method like this:
DialogView.xaml.cs
public static bool? ShowDialog(CDataClass cdc)
{
var dlg = new DialogView();
dlg.ViewModel.AAAVVM.CDC = cdc;
return dlg.ShowDialog();
}
And then the parent could interact with it like this instead:
MainViewModel.cs
public void ShowDialog()
{
var cdcClone = new CDataClass(this.CDC);
if (DialogView.ShowDialog(cdcClone).GetValueOrDefault())
{
CDC = cdcClone;
}
}
Nice and tidy.
If that dialog isn't modal, make the dialog viewmodel a private member of MainViewModel, and have MainViewModel subscribe to events on the dialog viewmodel to keep abreast of what the dialog is doing. Whenever the user updates the dialog's copy of CDataClass, the dialog would raise DataClassUpdated, and MainViewModel would have a handler for that event that sniffs at _dialogViewModel.AAAVM.CDC, and decides what to do with it. We can get into example code for that if necessary.
So now you can see what I mean by building everything in terms of parent/child viewmodels, and stuffing them into views when and as appropriate.
Edit: i have simplified this problem a lot since i first wrote it up. See the code examples at the bottom. I will clean the post up tomorrow.
I want to set the DataContext AFTER the user as filled out a form because that is when I know the type of object I need to create (based on what the user selected). The problem with doing it this way is that the BindingGroup.IsDirty flag is false because all edits are done by this point and I suppose that is why my call to BindingGroup.UpdateSources is having no effect.
Essentially this is what I'm doing:
Have user fill out form. The DataContext has not been set yet, but bindings are in place.
In the buttonSave_Click handler, create and set the correct DataContext object (based on provided user input) then call BindingGroup.UpdateSources and close the dialog.
I realize there are other ways to go about this. Probably even better ones, but I am really just experimenting with DataBinding and trying to learn it better. That is why I am trying out many possible designs with this.
Ultimately I will probably settle on a design where I ask the questions that I need answered in order to know what object to create for the data context then set it and let them fill out the remainder of the form from there. But for now I want to get it working the other way (if it is even possible) just for learning purposes.
EDIT 1:
I have determined that the IsDirty flag must not be my problem because immediately before the DataContext is set, IsDirty is true. Then immediately after DataContext is set it becomes false so I suppose it automatically updated sources then, however, I do not see the changes reflected in my data object so it obviously failed for some reason.
I know my Bindings are correct because if I set the DataContext in the Windows ctor it updates the data. Move those two lines of code to the buttonSave_Click handler and it no longer updates data.
I feel like there is something that I am missing that is going to me make me feel reaaallly stupid when I figure it out :\
UPDATE 1:
I have determined that the BindingExpression.Worker.CanUpdate is false. By looking at the source code of BindingExpression I can see that that would cause it to fail. Now to figure out why that is false....
UPDATE 2:
With all the reading i have been doing i am starting to suspect a timing issue. read this
I will be able to try it when i get home
UPDATE 3:
Still working on this. Here is a bit of code. I have simplified to get minimal code.
Here is the data objects.
public abstract class Dog
{
public Dog(SexType sex)
{
NickName = string.Empty;
}
public string NickName { get; set; }
}
public class Stud : Dog
{
public Stud()
: base(SexType.Male)
{
}
}
This code works:
//code behind
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
//set the DataContext here and the data object will be updated as expected...
NewDog = new Stud();
DataContext = NewDog;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
And the XAML
<Window x:Class="PuppyMan.AddDogWindow"
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:PuppyMan"
mc:Ignorable="d"
Title="AddStudDogWindow" Height="300" Width="300">
<StackPanel>
<TextBox HorizontalAlignment="Stretch" Text="{Binding NickName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button x:Name="buttonSave" Click="buttonSave_Click">Save</Button>
</StackPanel>
</Window>
THIS is the code that BREAKS.
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
//Notice all I have changed is moving the set DataContext out of the ctor and into this handler.
//Now the data object no longer gets updated.
NewDog = new Stud();
DataContext = NewDog;
DialogResult = true;
}
}
The idea is that I let them fully fill out the dog info and then I only create the Stud or Dame dog object based on the final decision of whether the dog is male or female. A kind of lazy data binding I suppose. I know this isn't the best design for this situation but I want to learn how to do it this way for learning purposes. Or learn why this method won't work, either is fine, I just want to learn this data binding stuff and well!
FINAL UPDATE
This post seems to be essentially the same as my problem. I am using OneWayToSource and it always sets my NickName property to the default value (in this case ""). The problem is NOT that the NickName getter is called after the setter, but that it gets set to "". The UI keeps the original value until I type in the TextBox again and INotifyPropertyChange fires then everything syncs up again.
Seems an awful lot like a bug to me that OneWayToSource would push a "" instead of the current value of the target. But I very well may be miss understanding.
Design your form such that you know beforehand which object you will get.
This can probably be done for example by checking if uses fills a particular field, or if he selects some combobox value in your form. In appropriate event of that control you can set your datacontext. And by the way Triggers are most suited here. Set trigger for your object-deciding-form-field. More can be done if you post something more, or upload some source code at dropbox.com.
//////////
You have set Bindings beforehand, and then you are changing the DataContext. Changing the DataContext will not make your TextBox value to reach the NickName property.
Secondly, you have set UpdateSourceTrigger to PropertyChanged. So, after changing your DataContext you have to make this property appear changed.
One way to do this is to :
DataContext = NewDog;
/* this will trigger property changed */
txtDogNickName.Text = txtDogNickName.Text;
string newNickName = NewDog.NickName; // updated value appear here
But this is not the proper way to do. Change your DataContext sepearately, and then edit values in your form. Changes will propagate correctly. Don't do everything in same handler. For example, you might show this form in some Button1's click handler and set DataContext, then use another Button2's click handler to get form values.
My problem turned out to be the same issue as this post. I am using OneWayToSource and when I change the DataContext my property is set to a default value which is currently "" instead of the value of the target as I was expecting. I have found no work around. I will design such that I know what object to create in advance so that the DataContext will be set in the constructor.
In the MvvmCross N=26 tutorial, dynamic fragments are loaded into a frame via button click event in the View (code snippet below). However, I'm trying to figure out how handle the click event in the ViewModel and not in the View. After the button is clicked, how do I know the button was clicked and in the View, load the correct fragment in the frame?
For instance, I may have 10 fragments and one frame in the FirstView xml. I want to be able to load any of those 10 fragments in that frame based on a property of a object referenced in the FirstViewModel. Can I check that property in the View and load the fragment that I want from the 10 fragments available? (i.e. remove the but1.Click event in the View and still run the transaction based on the value of the object in the ViewModel)
but1.Click += (sender, args) =>
{
var dNew = new DubFrag()
{
ViewModel = ((SecondViewModel) ViewModel).Sub
};
var trans3 = SupportFragmentManager.BeginTransaction();
trans3.Replace(Resource.Id.subframe1, dNew);
trans3.AddToBackStack(null);
trans3.Commit();
};
The approach you suggest of mapping a vm property to which fragment to show should work, yes.
To use this, just subscribe to property changed in your view code (there are some weak reference helper classes and extension methods to assist with this)
Alternatively, this blog post - http://enginecore.blogspot.ca/2013/06/more-dynamic-android-fragments-with.html?m=1 - introduces a mini framework that allows navigating by fragments.
A similar approach is used in the Shakespeare sample in the mvvmcross-tutorials fragments sample.
It should be possible to adapt that code to your needs
I'm new to both C# and WPF. I've written a simple program. I have a class called Counter, that exposes a read-only property Count that starts out at 0, and a public method Increment that simply increments the count by one. Counter implements INotifyPropertyChanged.
I have a Window class (code is below). I pass an instance of a Counter object to the constructor and perform a binding. The window has a button and a label. The label is bound to the counter's Count property, and the button calls Increment.
This all works.
However, most examples I've seen around the net and MSDN mostly deal with defining the binding in XAML. How can I modify my example here to move the binding operation out of code behind and into the markup? The Binding property in the Properties window of VS2010 doesn't seem to know how to do what I want. Perhaps it's not possible?
One additional question: I don't think this example fits MVVM... My Counter class stands alone, is not tied to a view anywhere except through its property. However, the CounterWindow class is holding a reference to it. Is this the proper location for this reference? I also though that perhaps I should be creating the window, then setting a property (e.g. CounterObject) that I would use instead of passing through via the constructor.
public partial class CounterWindow : Window {
Counter ctr;
public CounterWindow(Counter ctr) {
InitializeComponent();
this.ctr = ctr;
Binding b = new Binding("Count");
b.Source = ctr;
CounterLabel.SetBinding(Label.ContentProperty, b);
}
private void IncrementButton_Click(object sender, RoutedEventArgs e) {
ctr.Increment();
}
}
Something like this:
public CounterWindow(Counter ctr)
{
InitializeComponent();
DataContext = ctr;
}
Markup:
<Label Content="{Binding Count}" />
UPD.
There's two common approaches in MVVM: view-first and model-first.
View first means that you initially create the view, and then view creates view model, which it is bound to.
Model-first means that first you create the view model, then view model creates its view and passes itself (via constructor or via DataContext property setter) as data context of the view.
Hope this helps you.