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.
Related
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.
This question has probably already been answered, but i couldn't find a solution during 30 minutes of research.
The situation is as follows:
I am working on a C# Windows Forms project.
I have a Form called 'viewMain'.
Also these two classes are important:
With the winforms designer i added two bindingSources to the Form. One for objects of Auftrag and one for objects of AuftragErp. The DataSource for auftragBindingSource is set to an Instance of Auftrag. The DataSource for auftragErpBindingSource is set to the AuftraegeErp property of the Auftrag-Instance.
Now i can begin describing the problem. The ComboBox Auswahl on the Form is bound to auftragErpBindingSource and therefore always reflects the objects in the AuftraegeErp list. When i click the button on the form i want to get a reference to the currently selected object in the combobox.
I found that this can be achieved by using uftragErpBindingSource.Current.
But since the BindingSources are private i can't access them from the Auftrag Instance.
And i don't want to get this reference in an eventhandler of the form, since then i still would have to pass it on to the Auftrag instance. And if i where to do that, then i feel as though i could go without data binding altogether. The main reason for me to use data binding is not having to worry about keeping the objects and the gui synced.
So basically i have a list in a class. This list is bound to a comboBox. From inside of the class i want to get the object from the list which is currently selected in the comboBox.
I am thankful for every answer and hope that somebody can help me.
Edit for clarification: This Drawing shows the relation between the classes and the GUI. After the user has made his selection with the combobox he can click the Button. The onClick eventhandler then calls the 'doSomething' method. This method needs the selected object to function. There are multiple ways how the method can get the necessary object. I have shown two in the drawing. a) The method gets the objects from the list in its own class, or b) The method gets the object by using the .Current Property of the bindingSource on the Form.
I want to use approach a, because then i wouldn't have to pass any Variables or Objects between the Model and the View. Data binding alone would make sure that the GUI is always up to date. But i don't know how to implement approach a.
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.
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).
So the basics: I've got a window with a ListView on it, which is populated by my grid's datacontext:
mainGrid.SetBinding(Grid.DataContextProperty,
new Binding() {
Source = new DataView()
{ Table = SQLHandler.GetHandler[classType.ToString()] }
}
);
in xaml:
<ListView Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" ItemsSource="{Binding}">
everything works fine, it's populated. As you can see above, i've got an SQLHandler class which can be accessed by Singleton, and I can access my tables with an indexer.
The problem: window loads up, i'm selecting a row, clicking the Edit button, new window loads up, where i get the selected row's details. when i delete this row via this new window and close it, the main window (where the complete datatable is shown) is not updated accordingly. i know what the solution should be, but I can't make it work. (inotifyproperty changed interface to SqlHandler class, Binding.IndexerName etc..)
here is the main thing: the dataset is not in my SqlHandler class, it's in SqlExecuter, where all of my sqlcommands are being executed.
public override DataTable this[string key]
{
get
{
if (sqlExecuter.GetDataSet.Tables.Contains(key))
return sqlExecuter.GetDataSet.Tables[key];
throw new KeyNotFoundException("The specified key was not found");
}
}
where GetDataSet is:
public DataSet GetDataSet
{
get { return ds; }
}
How can I make this work? When I delete a row in a different window and close that one, the mainwindow's listview doesn't update itself.
The only option I have is to put a refresh button up, and then rebind the datacontext property, then of course it's working, but my goal is to have a 'live' update system, that's what Binding is for after all.
What I've tried: GetDataSet in SqlExecuter: implemented the inotifypropertychanged interface, but nothing changed. and i can't have inotifypropertychanged implemented on my indexer in SqlHandler, because it doesn't have a setter, I'm always just accessing the tables from code-behind, my sqldataadapter is populating them (Fill method)
p.s: i don't really plan on creating an ObservableCollection, because 90% of my code should be rewritten and when i delete a row, i clear my dataset and fill it up again, so I'm not even expecting it to notice every change, just when i refill my datatable, my listview should know about it.. and refresh itself
I think using a second window as a popup might be the problem here. If you were doing the editing on the same page then you could use a simple
ListView1.DataBind()
To refresh the contents of the list at the end of the delete command. Or you could use the IsPostBack method to update the list if the page was bring redrawn rather than reloaded.
You could try calling the page name and then the list view from your other window but I'm not sure if you can perform that kind of command from another window.
Alternatively you could do the editing on a different page, rather than a separate window so when you return to the original page the listview is being redrawn.
Like yourself I'm sure there is a simpler solution to your problem, but unfortunately I don't know it.
You may need to set the binding mode to two way:
Mode = BindingMode.TwoWay
WPF for some non-obvious reasons, defaults to one way binding. Hope that helps. I'm not particularly well-informed, but I've seen this issue with simple data bindings to ObservableCollections, and the TwoWay binding fixes it.