I have a bunch of UserControls ('MyUserControl') that I want to have a user manually add to one or more Canvases. One instance of a UserControl cannot be a child element of more than one container (otherwise a 'System.InvalidOperationException: Element is already the child of another element.' is thrown).
Is there a way to do this without creating new (duplicate) instances of the MyUserControls? If not, what would be the best strategy to keep duplicate instances of the MyUserControls in sync?
You cannot add the same instance of a control (any control) as a child of more than one parent. You will need to create multiple instances of your Usercontrol and place each on its own parent.
If you need to keep the data they display in sync then you should store that data in an object separate from the user controls themselves, its this object that you would only have one instance of. Typically you would assign this data object to the DataContext property of each user control, then the various component parts of the User control can get their data using data binding.
If you ensure that your data object implements INotifyPropertyChanged correctly then when one User control makes a change to the data it will be reflected in all the other User Controls referencing the same data object.
Related
My app has a background thread that periodically retrieves data from an external source, in the form of key/value pairs. I would like to expose this data for binding, presumably by storing them in some kind of static(?) model, as the data will be needed by numerous views throughout my app. There are potentially hundreds of these keys, and may be different for each customer, so I can't simply create an INotifyPropertyChanged model with a property for each value.
The app has multiple views visible at any one time, and each of these will have numerous controls (usually textboxes) that I want to bind to individual items in the above collection. When a value in the collection is updated, any controls bound to only that item should change to reflect the new value. I'm assuming an ObservableCollection wouldn't be suitable here, as a change to a single item will result in all controls updating, regardless of which item they are bound to?
To throw a further complexity into the mix, some values (which are numeric) will need formatting for display, e.g. number of decimal places, or adding a suffix such as "volts". The formatting rules are user-defined so I can't hardcode them into (say) the XAML binding's StringFormat expression. Ideally I should be able to access both the raw value (e.g. for calculations), and the formatted version (for display purposes). I'm sure it must be possible to achieve the latter using some clever WPF feature!
I would appreciate any pointers on how I can solve these requirements.
Edit: it's worth mentioning that I've previously tried implementing the model as some kind of collection. The problem is that it won't be initially populated with all values, and these only get added some time later. When they do eventually get added, a bound control doesn't update - presumably because it wasn't initially able to bind to the missing value.
I would take a different approach, namely a variation of Event Aggregation. I would have a single class that manages the overall collection (probably a singleton class like franssu suggested), but instead of binding directly to the collection in that class you create smaller models that are more specific to the individual views.
When your main model receives a new item, it publishes an event, which is consumed by the smaller models who can inspect the new item and determine whether or not they should add that item to their internal collection (the one the individual views are bound to). If it doesn't "belong" to their view, they can simply ignore the event.
You could use similar event publishing for updates to items and such, although if you're binding to the actual items you probably don't need that.
Just implement the INotifyCollectionChanged Interface and the INotifyPropertyChanged and you ll get a Collection like the ObservableCollection.
But rember if you select a Item from your Collection (as example a ObservableCollection) and you change that item your other controls won t update. So if you have a class Person in your Collection and you change the name of one person the other controls won t get the new name of the person.
Inside the Person object you still have to implement the INotifyPropertyChanged Interface and raise the event when your name changes.
So what I want to tell you is: A Collection with the interface INotifyCollectionChanged will only tell the bound controls: There is a new Item, there has been a item removed or a items index changed BUT not if the item itself changes.
So you ll need a Collection that provides the points above and a Item contained by the collection that raises events if a property of it changes.
ObservableCollection is perfect here. You should find that a standard ItemsControl bound to an ObservableCollection will only update the controls of the items that have changed, not every item in the collection.
This is the reason ObservableCollection exists - the events that it raises specifically identify items that have changed, so that the UI can handle them sensibly.
I've tested this locally with a small WPF app and it works fine. Worth noting, though, that a virtualised items panel would probbaly appear to break this behaviour when it scrolls...
EDIT: rereading your question, you actually say "When a value in the collection is updated..." If your collection contains instances of a class, and you update properties on the class, you don't even need ObservableCollection for this to work - you just need the class to implement INotifyPropertyChanged.
I created a new entity object and bound it to controls in another window (edit window). After modifying and saving I assigned a new entity object into the one in the main window. The old entity object is bound into a datagrid, now I want the datagrid to display the data that I had modified and saved.
ObjectContext.Refresh Method (RefreshMode, Object) seems to be what I want but I don't know how to use it correctly.
In short :
I have a main window with datagrid displaying the whole data of the table. Users can pick one row and edit it in a edit window. After saving, the datagrid should display what has been modified.
Your best bet here is to use an ObservableCollection as your data source for the datagrid instead of the query.
And look at implementing INotifyPropertyChanged interface in your Customer class.
The ObservableCollection is initially populated by the database query. User changes are made to elements within the ObservableCollection and once complete you then just need to trigger transferring the changes to wherever you originally obtained your list of Customer objects
By doing this changes made both to the collection of Customers and to individual Customer objects (if present within the datagrid) will be automatically updated for you.
edit
I must admit that I'm a bit rushed to offer up any code at the moment, but here's a pretty good article that explains how to use ObservableCollections and classes that implement INotifyPropertyChanged. It also has code examples, which although in VB.NET should give you enough of an idea to get started.
In effect you separate your code into distinct layers UI (View), business logic (View Model) and data layer (Model where your entity framework resides).
You bnd your datagrid to the ObservableCollection type property in your Customers class and your edit csutomer window is bound to as instance of your Customer class.
I'm trying to build a data entry form in wpf. To perform validation I apparently need to have an object attached in the datacontext of my grid. But how can I have one when I didn't create one yet?
How does it work?
For example, I have a screen with a datagrid. The datagrid contains users that were obtained from membership. Above the grid is a button: add user. When clicked a new window appears and the following can be entered: user name, password, email. To perform validation on the textboxes to see if they aren't empty. Now, it is my understanding that the way this works is by having an object attached to the window (datagrid datacontext). But how can I have it attached when it doesn't exist yet?
This is a case where MVVM design patterns are very useful.
Every WPF view has a corresponding view model object that the properties in the view are bound to. So your window with the data grid has a view model - its DataContext - and the view model has properties that are bound to properties in the view - e.g. the ItemsSource in the data grid is bound to a collection (see note 1).
The "add user" command (which is implemented as a RelayCommand in the window's view model) creates a new view (the new window) and its corresponding view model object (the new user), sets the view's DataContext to the view model, and calls ShowDialog to show the window. (See note 2.) If the user accepts the new object, ShowDialog returns true, and the logic in the command takes the view model object (which now contains whatever changes the user made) and uses the information in it to create a new model object and add it to the model. If the user cancels, ShowDialog returns false, and the command discards the view model object without creating a new model object.
Note 1: The collection here may be a collection of model objects, or it may be a collection of view model objects. It depends on whether or not you need anything that's not in the model for displaying the model objects in a data grid. It's common, in this kind of scenario, for the objects in the grid to be view models for the dialog - that is, the view model objects have properties implemented for both display in the grid and modification in the dialog window. On the other hand, if all the grid is doing is displaying data from the model, there may be no need for an intermediary object.
Note 2: Having the command create a WPF window violates a central MVVM design principle, which is that view models shouldn't create WPF objects. The reason for this principle is pretty simple: you can't build an automated unit test for this command, since it's just going to throw up a dialog and wait. There are all kinds of different approaches to this - see, for instance, this question, and Josh Smith's blog post on the Mediator pattern - and all of them involve delegating the creation and display of the actual dialog window to a separate service that can be mocked out for unit testing. If you don't want to choose one of those approaches up front, you can retrofit one into your application once you get this thing working.
The idea here is that you should attach an object which is slightly different from your business models. In your case it won't UserInfo (or whatever you have for users in grid). It will be some other class, more suitable for editing. In MVVM this class will be a ViewModel. This class will have some differences comparing to your regular user class, for example it may have some properties nullable (when you haven't set them yet). Also this class will handle validation. You should instantiate this class at the same time you're creating an editor window and put instance of this class into Window.DataContext.
Hmm, there is a lot in this question but I just created a screen with three data grids (I am using Telerik in this case) and under each datagrid is a button to add to the grid. No the window with the three datagrids has it's own view model. and each of the "pop up's" has it's own viewmodel, in this case all of these are user controls and I just create a new window and set window.content and call show dialog.
Communication is facilitated via "events" - not the standard events you are used to in .NET but in this case I am using Prism and it's CompositePresentationEvent class. When the user is done creating their new object they click add and I fire off this event with the "payload" being the object they created. The main window with the three grids listens for that event and has a method to handle it, in this case adds it to the ObservableCollection which is what I bind the grids to.
If I were you I would look into the various frameworks that are out there, Prism, MVVM light etc... Again, your question seemed rather broad, I tried to give an overview but I didn't go into detail, if you look into some sort of framework I think it will clear up a lot of these details for you.
When the users hit Add New, create a new blank copy of your object, and set the datacontext to that new object.
Set some kind of flag to identify that it is a New object. This can be the Id being NULL, 0, -1, etc or an ObjectState property set to New. That way all your validation rules apply, and once the user hits save you know to INSERT instead of UPDATE
I've got two ListBox's with objects as an ItemsSource populating them. Right now, I'm using a DragDropHelper to let me drag an object from one ListBox to the 2nd ListBox. I run custom code to change an attribute on the Object and update my two ListBox collections of objects.
However, now I want to be able to drop one of these objects onto another control in the window. But, I dont want to necessarily "DROP" the object. I just want the external control to realize (by raising an event) that it just got dropped onto by an object with an ID.
To recap, I've got 2 listboxes. one listbox is Favorites, the other is NonFavorites. I can happily drag/drop between the two listboxes and everything works. now i want to drag a favorite/nonfavorite away from the listboxes and drop it onto another control. I want that control to simply say "HEY! I just got a favorite/nonfavorite object dropped on me".
any ideas?
I did something similar to this last year (.NET .3.5).
If I remember correctly when you "Drop" an object which has been selected and dragged (via the adorner layer) you are in essence holding a reference to the selected object. When that object is "Dropped" the "InstanceDroppedOnUserControlFoo_Handler(... args)" event handler has a untyped reference to the object that has been dropped.
From this you can cast (if the type is known) and access the Id field to your hearts content.
The question now is, does the drop target user control share the same ViewModel in it's DataContext as that of the Drag Source? As in most cases where this is not the case you will not get a reference in the event args, you will get null.
If this is the case you will need to explore these options for inter ViewModel communication:
Use a MVVM message passing framework (MVVM Light Framework see Messenger component)
or
Pub Sub composite events via the WPF Prism - EventAggregator:
Then follow this process (or something more tailored to your needs):
When an item has been selected and is being Dragged, hold its reference in a property of your Drag Source's ViewModel.
When the item is dropped, publish a message saying "I want the reference to the selected item which was being dragged".
The Drag Source can publish a message in response with the reference to the object which was dragged which will be received by the requesting ViewModel.
Obviously you can tailor the reference holding at this point to your needs. I will leave you with one last suggestion, it may be worth while considering the use of a controller class which manages this kind of operation. I have seen a controller being used by the Microsoft's Patterns & Practises in coordination with MVVM in the WPF CAG (PRISM) samples, so it is not unheard of.
Assuming that you have two windows where one window has a listbox and the other window should have the detail information for an item currently selected in the listbox. In one window this is easy enough to do but how would one do this with two windows? Unfortunately, two windows is the only option here.
assuming they are both part of the same application, then there has to be some type of parent container.
Either the window with the listbox is the parent to the details window, or there is a window which owns them both.
Regardless, it's just a matter of passing the object from one window to the other.
for example, your object is called foo, and has the following parameters: fooA, fooB, fooC, fooD.
The listbox's datacontext is thus a observablecollection(of foo). when a user double-clicks on one of the list box items, the listbox's selectedItem is one of the foo object. taking that, you provide it to your details window either by direct pass [myWindow.LoadFooForEditing(myFoo)] or by raising a event to the listbox window's owner and letting it pass the data.
If they are two separate applications then you need to start considering things such as remoting or MSMQ as tools to pass information between two applications.