When does the data get bound and how to speed it up? - c#

I'm stepping through the code and I see that in the constructor of my view, I set DataContext to a new instance of my view model. Stepping into it, I can see that the field Thingies is set. Stepping out back to the view's constructor, I can verify that, indeed, the property of the view model is set and the count of the elements is correct.
Then... (let's get back to this spot in a second)
Finally, I can see the rendered GUI where the data produced by the view model is shown just as supposed to. Everything has worked out as supposed to. Now, I only need to set the first row of the grid as selected, which shouldn't be a problem, because I know that the data is there (verified by a step-in to model view's constructor, watch on data context's fields and the actual window's contents).
But nooo... (now, let's get back to the aforementioned spot)
When I investigate the grid's DataItems property, while still in the constructor, I discover that there's zero elements in it. Since I've seen those on the screen, I know they must get there somehow but, apparently, the binding of the view model's property Thingies takes place after the constructor's run.
I'd like to enforce the binding to take place while still in the constructor (if that's the most appropriate approach). As the plan B, I'm thinking about reacting to some event (like Blopp_OnDataBound(...) or something) but I haven't found anything except target being updated. I've put TargetUpdated="TheGriddy_OnTargetUpdated" next to the data source's binding but the method doesn't invoke.
NB. Even though I'm doing this on a specific control, I'm convinced that the question is generally applicable to any control as it's about the XAML/WPF's data binding paradigm. Correct me if I'm wrong. (I'm on Infragistics' XamDataGrid but the behavior is most likely reproduceable for the good, old, plain DataGrid.)
As requested, I'm also providing a small sample, which I chose not to before, as I thought it wasn't of any help. I stand corrected.
public MyView()
{
InitializeComponent();
DataContext = new MyViewModel();
if (xamDataGrid.DataSource != null)
throw new HappyException("yes!");
}
private void SomeMethod()
{
if (xamDataGrid.DataSource != null)
throw new HappyException("yes!");
}
The problem is that the exception is never thrown in the constructor but is
in the method. The data source is set as we bind, so the binding must occur after the constructor's been run, right?
I need to select the first row. That can be done when the first row exists...

Data binding seems to happen during the LayoutUpdated event when the window is first shown. I think the plan B is the only possible way so far. Use the TargetUpdated event as mentioned in the How to detect data binding completed in WPF thread, but note that you need to explicitly enable NotifyOnTargetUpdated otherwise it will not fire (as you already have seen).

Related

Why is WPD Datagrid Usercontrol bound to row datacontext not updating?

I have a DataGrid bound via a ListCollectionView to an ObservableCollection of Objects with type Job, say. Each cell in the DataGrid contains a UserControl which has a dependency property named Job which is bound to the DataGridRow.DataContext (using Mode=TwoWay). Everything displays correctly.
The problem is that I have a background process which mutates objects referenced by the Job object and those get displayed by the UserControl. Obviously, Job does not change so the view does not change.
How can I get the user controls in each cell to update themselves with the new data?
With lists there are 3 kinds of ChangeNotification you have to take care of:
the one for each property of the Job Class.
the one if elements are added or removed from the collection. That is the only part ObservableCollection takes care off.
the one on the property exposing the list/CollectionView/whatever. ObservableCollection lacks a AddRange function so on large operations (like a rebuild of the list) it would spam the UI with changes. The solution is to build a new list in the background, then expose it in this property.
One particular issue might be the Background Process too, if it is Multithreading. GUI's are protected against being written from the wrong Thread. But threads are also notoriously good at swallowing Exceptions. Usually you need to write a faulty Catch, but they do it for free. As a result, your might run int oa CrossThread exception and never notice it.
For a better answer, we need some actuall code. Like teh job class, the BackgroundProcess, etc.

WPF Thread binding access error on binding in MVVM

So I am trying to incorporate dynamic-data-display for WPF into my MVVM caliburn project (I am using the LineChart control from future of dynamic data display, if anyone wishes to test this error). There is a LineChart that binds to an ObservableCollection. It only works if the collection is made in the code-behing of the control which has the LineChart. If you try to bind to a collection the ViewModel the dependency property raises an InvalidOperationException. How can this problem be solved?
I have seen that happen when you change the collection which the property is binded to and know ways to fix that, but never in the actuall proccess of binding. I've tried putting the creation of the collection in a dispatcher invoke (like you wold do with an add or remove) but it didn't help.
Edit: As I stated in the second paragraph the exception is NOT at the point of changing the collection. It is being raised at the point of binding. Even more, I have tried to use the solutions in the other question and they did not help.
Edit #2: The exception message is "The calling thread cannot access this object because a different thread owns it".
People keep telling me solutions to collection changes but it doesn't even get to the changes. It fails at binding stage (ItemsSource="{binding collection}" in xaml).
Edit #3: I double-checked and noticed that the ViewModel is created in the UI thread which only made more questions.
Ok, it took me quite a while to find the root cause of your problem.
Unlike from what others suspected, this isn't a multithreading issue at all.
Instead it's an issue with the DynamicDataDisplay library you are using.
There's a clear reason why your ItemsSource binding works on your ListBox object and doesn't work on Chart (of type Microsoft.Research.DynamicDataDisplay.Markers2.LineChart) :
Chart neither has a visual nor a logical parent.
You can check this if you insert the following code into Button_Click and set a breakpoint after them:
var visualParent = VisualTreeHelper.GetParent(Chart);
var logicalParent1 = Chart.Parent;
var logicalParent2 = LogicalTreeHelper.GetParent(Chart);
You can see that they are all null.
So the Binding that you set on LineChart.ItemsSourceProperty with Path=ExampleCollection cannot find any source value and just assigns null to the ItemsSource. That's because the DataContext is inherited from the parent - but when there isn't any parent then there isn't any DataContext to inherit either.
And because Chart isn't part of the visual or logical tree, there is no (easy) way any binding to an outside DataContext can even work.
To verify that the DataContext is null just add this line to the preceding code:
var dataContext = Chart.DataContext;
Now there are three possible solutions for this.
First, you can manually inherit the DataContext from Window using the following code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Just add the following line.
Chart.DataContext = DataContext;
Chart.StrokeThickness = 3;
Chart.SetBinding(LineChart.ItemsSourceProperty, new Binding("ExampleCollection"));
// ...
}
If you simply add this one line, you'll see that your other multithreaded code is working just fine and the chart is updated with some kind of sine wave pattern.
Second, as another possible solution you can consult the documentation for the DynamicDataDisplay library and check the correct and intended way to assign an ItemsSource to a LineChart using data binding.
I tried searching for documentation myself and even debugged a lot of code from that library for two hours or so, but the documentation is nearly non-existent and the code is far too complex to fully understand it in a few hours. I tried using several tools (Visual Studio Live Visual Tree, Snoop, ...) for displaying the visual tree of the ChartPlotter but I got a StackOverflowException every time, so essentially something in this library is kind of flawed and buggy.
Third, you can use a Resource as a kind of proxy object to create a "binding bridge" to the same instance of the MainWindowViewModel.
For this to work, you have to do something like suggested here: Data binding outside the visual tree. Data Context bridging
Bottom line: So, if you just want to get the job done, I would set the DataContext in code like shown above. (Especially if the instance of the ViewModel in the DataContext never changes.)
If you want to use pure data binding then I would probably use the "binding bridge" or search for another charting library that supports this scenario.
As THIS thread states you can use the UI dispatcher and invoke the function that changes your ObservableCollection on the UI thread. Application.Current.Dispatcher should give you the UI dispatcher. As the solution suggests it can be right in the ViewModel. However, a cleaner and more generic solution is to IMPLEMENT a concurrent and still observable collection it would still store the dispatcher and run the changes on the UI (or specified) thread.

How to access non-bindable view properties from the view model?

I'm using the Telerik WPF RadGanttView control to display a bunch of data. Since the data can be arbitrarily weird - events that are instantaneous, or that last days, or have a week between them - it's not possible to set a PixelLength (i.e. a scale factor) that's guaranteed to be useful. I've been asked to make it possible to change this scale factor with a slider. Unfortunately, just changing the scale factor with the slider has a usability issue, which I need to fix by manually adjusting where the view is scrolled. I'm at a loss as to how exactly.
To outline the usability problem I'm fixing: the view works by providing a "viewport", located at an "offset", over the whole "extent" of the data set. (Terms lifted from the Telerik API. These seem to simply represent pixels in the canvas underlying the scrollable view.) When the scale factor goes from, say, 100% to 200% (which corresponds to halving the PixelLength), what happens is that the width of the extent is doubled, but the horizontal offset remains the same. The consequence is that after zooming in, it's quite likely you'll see entirely different data than before, since the events that were there before got "pulled out" of the viewport to the right.
The way I intend to fix this is: grab the offset/viewport/extent etc before zooming in, zoom, then do some maths I haven't figured out yet with that and the new offset/viewport/extent. The problem is: the properties of RadGanttView that describe the scrolling stuff are not DependencyPropertys, and I cannot simply bind them to properties on my ViewModel. (In fact, they're not even accessible in XAML to begin with, RadGanttView implements IScrollingInfo explicitly.)
So, my question is: how do I, in my ViewModel, or wherever else in reaction to the ViewModel's scale factor changing, access properties of a control in the corresponding View, that cannot be data-bound? Every search I tried tells me that accessing the view from the viewmodel is "not MVVM", but since Telerik is a third-party library, I can't really refactor how this works on their side.
A fill-in-the-blanks outline of my code:
FooViewModel.cs
class FooViewModel
{
// A slider in the view pushes its value into this property
double ScaleFactor
{
get { /*...*/ }
set
{
PixelLength = GetNewPixelLength(value);
// ...
}
}
// The RadGanttView pulls its scale from this property
double PixelLength
{
get { ... }
set
{
// How do I get these values?
var oldOffset = ???;
var oldExtent - ???;
// Trigger the actual change in the view.
PropertyChanged("PixelLength", ...);
var newExtent = ???;
???.HorizontalOffset = GetNewOffset(...);
}
}
}
FooView.xaml
<UserControl ... d:DataContext="{d:DesignInstance my:FooViewModel}">
<telerik:RadGanttView x:Name="Gantt">
<!-- ... -->
</telerik:RadGanttView>
</UserControl>
Things I've looked into:
Making a bunch of DependencyProperty wrappers in the FooView code-behind that just access corresponding properties in the RadGanttView. This seems like it both is a horrible abuse of the system - i.e. it doesn't seem to make sense to have a dependency property not backed by a DependencyObject. And also that it plain wouldn't work - in WPF, the view seems to "push" data into the view model, and I'd still have no way to actually get the current values, since the values of the wrapper properties would never get updated.
Uh, Commands, maybe? I'm fairly new at WPF, I have no clue how those work at all, merely a vague impression that they might be a loosely coupled way for the view model to talk to the view.
Attached properties? Custom bindings? Way above my pay grade, if they help I don't know how myself. It seems like they could accomplish the "dirty" solution of just binding a control to a view model property. Since the type of that property would be IScrollingInfo, not the whole view, I could live with that.
Attached Behaviors may solve your issue. They are basically Attached Properties with a callback.
Check out my answer here. Just instead of KeyDown event, you register to the Changed event (or whatever your control is actually calling it) and then assign the value you get from the Changed event to your Attached Property, and you have two way binding on a non-bindable property

Which executes first Code Behind or View Model

Based on my previous question Accessing variables from XAML and object from ViewModel using Code Behind:
How would I know which executes first?
Is it the code behind or the ViewModel?
I just want to make sure that my code behind executes prior the ViewModel
The View and the ViewModel are both regular classes that get instantiated. This is done by calling the constructor as in any other class. So, as a simple answer to your question: Set a breakpoint in each constructor and see which one gets hit first.
There is no general answer to your question because it depends on your architecture and use case. Often, some control is bound to a property of the ViewModel of it's parent, which changes at some point. At that point your View already exists and you have no idea how long the value to which the property has been set is existing already. In other cases, your View is created for a specific ViewModel and takes it as constructor parameter.
The one way to make sure that the ViewModel exists before the View is to pass the ViewModel as a constructor parameter. The idea behind constructor parameters is to express: "This class needs existing instances of type xy to be created", which is what you are asking for. However, as you will set it as the Views DataContext in the constructor and as the DataContext can change after creation of the View, you cannot be sure that the View won't get a new ViewModel assigned after creation. Even worse, you will not be able to use your control in XAML anymore, because it doesn't have a default constructor anymore.
According to your first question, it is not really clear why the ViewModel should exist prior to the View. If you need to read a resource value from your View and assign it to a property on your ViewModel, I would expect it to be the other way around? Or are you accessing the View in your ViewModel (don't!)?
The question is, why you have to ask this question in the first place. There is something pretty wrong in your (or your bosses...) concept: View and ViewModel are two entities which should really work without knowing about each other. The idea is to build applications that could work perfectly without a single View existing by just getting/setting values on ViewModels and to have Views which would compile any run perfectly well without ViewModels, just without anything to show or do... If you try to hack this approach, you're better off not using MVVM at all.

Binding to ObservableCollection with MVVM Light

I have a MainViewModel that contains a reference to an ObservableCollection:
public ObservableCollection<SomeClass> ListOfPeople
{
get
{
return MyClass.BaseCollection;
}
}
BaseCollection is also an instance of ObservableCollection<SomeClass>. ListOfPeople is bound to a ListBox control on the second page - the application starts with the first page, initiates the download process to populate BaseCollection and switches to the second page while the download is still in progress.
The problem is that when the binding occurs, BaseCollection is null and therefore the ListBox is not populated. However, even when the download process finishes, the ListBox still remains empty. I am assuming this is happening because BaseCollection isn't notifying the proper instance about existing changes to the collection, but I am not sure.
BaseCollection has items inside it - I confirmed it.
Any suggestions on how I can work around the issue? Anyone here binding to an ObservableCollection via MVVM Light just like I showed above?
If you donot want to instantiate an empty ListOfPeople in the constructor and use this instance for database-loading you have to do this:
After loading of ListOfPeople is completed, your MainViewModel must call RaisePropertyChanged("ListOfPeople"); to tell the view that the data has changed.
Background: Thanks to ObservableCollection MyClass.BaseCollection.Add() updates the gui. As soon as MyClass.BaseCollection = new Obser... is called there is no more update of the gui since the gui holds a reference to the old contend of MyClass.BaseCollection. mvvm-light-RaisePropertyChanged() tells the gui to update its reference to a new collection
I haven't worked on MVVM Light, so sorry if there is something specific about it that i am missing.
Looking at your implementation,
public ObservableCollection<SomeClass> ListOfPeople
{
get
{
return MyClass.BaseCollection;
}
}
This code should work, and the control which is binded to this source should get propert updated without being concerned about the actual source where the instance of observable is created.
Thus, the only possible problem here could be that your MyBase.BaseCollection is null in the begining. So, if you avoid that situation and create an empty collection where you have declared this observable item, and then trigger your downloading process the way it is, then everything should work fine.
Hope this would be of help.

Categories

Resources