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.
Related
I have a WPF application whose main window contains two panels. The layout is pretty standard. The left panel is a a list of objects. The right panel displays information about the object selected in the left panel.
Right now I have the left panel directly in the XAML of the window, although I'm thinking it should be its own UC/View. The right panel is coded as a UC. The ViewModel for the main window has a field that is the VM for the right panel (call it the properties VM). The properties VM encapsulates the data for the selected object from the model.
When a different object is selected in the left panel I see two options:
1) Instantiate a new instance of the properties VM and load the corresponding data from the model
2) Load the corresponding data from the model to the existing properties VM, and issue appropriate change notifications [My current implementation]
Is there an advantage or preferred way of doing things between the two options above? Am I missing a better third option?
* Edit * Maybe a better question is - what are the advantages and disadvantages of the two methods?
Thanks!
You are describing a very standard scenario in the MVVM world. I believe you have a slight architectural issue that when resolved would make this question moot. The 'list' from where you are making your selection should already be a list of view models that wrap the 'object' a.k.a model. The properties view should simply be databound to the selected VM.
Is there an advantage or preferred way of doing things between the two options above?
Not really, but it's probably simpler to just create a new instance and set the data-bound property to this instance instead of trying to re-use and update the state of an existing instance each time the button is clicked.
If you need to cache instances, you could use an IoC container or a custom cache container that resolves the instances for you. You could for example set up the container to always return the same instance of the same class or return a new instance each time depending on your requirements.
But there is "preferred way" really. Your requirements decide what would be the best way to solve your specific issue or use case.
I have windows form application which has several user controls - each one is displayed when the relevant option is selected from a listbox.
Some of the user controls need to have access to data stored in a different user control so User Control A needs to know a value of a textbox stored in User Control B. I have done my exposing some properties in the user control B. This all works fine when the application first loads and no values are changed.
The problem I am having is if the value of the textbox in user control B is changed it is not picked up by user control A.
Do I have to do something with NotifyPropertyChanged? Any suggestions please?
Two solutions here:
Create a series of public properties and handle passing values where the Form objects are newed up.
Create an event to communicate when things change and register an event handler in the target Form to accept the change. This is similar in theme to the INotifyPropertyChanged interface but that's only required/advised for formal databinding scenarios.
I prefer events for this kind of thing.
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.
I am working on a PRISM / CAL solution, but the problem may be WPF specific:
If I create one instance of an control (e.g. TextBlock) and add it as child to a StackPanel, there is no way to add it as "child" to another StackPanel (parent already set error). I kind of understand the reason (it also occurs when using the RegionManager).
But what is the suggested way if a visual control is very complex and should be created only one time and used in two places? I agree that is does not really make sense to show an identical control 2 times on the screen, but there might be cases where it is useful (e.g. an "Close All" Button).
I know that in the button case, I should just create two buttons both databound to one ICommand. But does this rule also apply with much more complex controls (always create new instances)...
I stumbled on this problem when creating a layout switcher, which creates the button list and the stack panel for each GUI seperately, but uses a static ObservableCollection of buttons as source (which causes strange bugs)..
Any ideas on this topic?
Chris
This is normally handled by templates. That is, you abstract out the data into a particular type, and associate a template with that type. Then you place the instance of that data any number of times into your visual tree and have WPF render it with the template.
In short, don't add a TextBlock to your StackPanel. Instead, add an instance of your data type (eg. Customer) and associate a DataTemplate with the Customer type. There is no way to parent the same UIElement in multiple places.
You can add your control (or collection of controls) as a resource and refer to them via binding in your controls. This will implicitly create a copy (they will be Freezable and WPF will copy them).
Generally you should be using DataTemplates as Kent suggests, but if you have a special case, this will likely work.