Reuse View with different ViewModel in WPF - c#

I'm learning how to make a WPF application by making a tool to help me with Dungeons & Dragons.
Basically it's a database of Items, where I can also generate a list of loot by "rolling" on a "Treasure Table" which gives me "Item Tables" to in turn roll on.
I'm done making both the Item database viewer as well as a window where you can select an Item table, see its contents in a datagrid and add, edit, and delete Item Tables.
I now need to make the same for Treasure Tables. It needs to be so similar to the Item Table editor, that I wonder If I can't just reuse it, feeding a TreasureTableVM in instead of an ItemTableVM. Depending on which VM is set as the Window's DataContext, the view would do things slightly different, including showing different some columns in the datagrid.
The TreasureTableVM and ItemTableVM have the same methods and properties, although with slightly different implementation. Also a couple properties have different types (currentTable is TreasureTable in TreasureTableVM, ItemTable in ItemTableVM. CurrentRow is TreasureTableRow in one, ItemTableRow in the other. TableList is ObservableCollection in one, ObsevableCollection in the other)
How would I go about it?

You can use the same table and just add a new field in your datacontext, named "type" and the value is "treasure" or "item".
Then on your VM just filter the data by type.

Related

Dynamically creating Tab+DataGrid for binding with multiple different classes?

I apologize for the crappy title, I wasn't quite sure how to summarize what I'm trying to do.
My scenario:
I am refactoring an existing WinForms application to WPF and MVVM. I'm currently working on a module that provides search functionality for a database comprised of many different tables such as Contact, User, Case, Product, etc. In code-behind there are classes which provide an Object for each. I have written wrapper classes for each of the searchable table Objects that expose only the properties a user would want/need to see in the search results for each type of Object, for binding to a DataGrid.
Once these search results exist, they need to be displayed in a combination of Tab Controls and Data Grids, like so:
Because of some use cases, I need to be able to create an individual display Tab+DataGrid for the results of every single search that is performed. I cannot have a single tab for each type of search that is shown/hidden as needed.
I'm not sure how to proceed from where I currently am to the goal pictured and described above. Thanks for any help anyone can provide.
Not sure I entirely understand your question, but it looks to me that it might be a candidate for datatemplateselector.
Basically, you use an ItemsControl bound to your result collection and then - using a datatemplateselector - you swap in the appropriate template to display the item based upon a code inspection.
That will let you present all the results in a single list.
If you want to display your results in a tabs, I would present a collection of each result type in your viewmodel. So you have a Users collection and a seperate Products collection. Then you can bind individual data grids to each template.
If you want to then hide the tabs when no results are present, add a data trigger using the expression.interactivity namespace to trigger the visibility of each tab page based on its respective collection count.
One more thing, if you want to create tab items dynamically, i.e. One tab for each search - the tab control has an ItemSource property. If you group each search result into an object an expose an observable collection of that object, you can bind that to your tab control and have it create tab items for each search result. Just make that object contain a collection of actual results and you should be able to create a itemscontrol as mentioned here already.
Sorry if this appears a bit of a mind dump, but like I said - not sure if I entirely get the question :)
Rather then chuck a load of code snippets in, there a plenty of samples just a google away if anything sounds helpful.

WPF C# - Complex Data Binding

I have a very complex databinding I want to accomplish here using the below:
2 SQL CE tables named mainTable and secondaryTable
1 Fluidkit ElementFlow control named myElmntFlow
2 UserControls named myUsrCtrl and otherUsrCtrl
All of the above are already created and implemented but the UserControls are populated into the myElmntFlow control's items list programmatically through lenghty backgroundworker code that does take a good bit of time to run when the number of items to enter is > 20.
This is how they get created as of now:
The backgroundworker loops through each row of mainTable and adds the myUsrCtrl control to the list of items in myElmntFlow if the value of the row in column "Selected" = "'Yes'".
Then, it modifies the content of the newly added myUsrCtrl as such: it adds a otherUsrCtrl into the myUsrCtrl's stackpanel (named stckPanel) for each row in secondaryTable where the value of the column "FullName " = the value of the same column of the mainTable row we used to created the myUsrCtrl control.
And then populates the otherUsrCtrl's sevaral labels with the value of the secondaryTable row looked at at the moment.
Very confusing but it is a complex scenario. Let's use an example:
In mainTable, the row #4 has the FullName value of "Chad Jones" and
also has the Selected value of "Yes".
A new instance of the myUsrCtrl control is added to the
myElmntFlow's list of items as such:
myElmntFlow.Items.Add(myUsrCtrl);
The newly added myUsrCtrl control has a stackpanel (stckPanel)
We filter the secondaryTable where the FullName = "Chad Jones"
For each row in the now filtered secondaryTable, we add a new
instance of otherUsrCtrl to the previously created myUsrCtrl's
stckPanel control
The different labels in the otherUsrCtrl are populated with the values of the
row in secondaryTable
Can this possibly be converted into a DataBinding within the XAML of the controls as I want to implement several features later on (such as a nice SearchBox with autocomplete) that would be quite poor if they were to be coded behind by writing hundreds of line to tell which data to filter, sort , take , compare etc...
I wrote this as clearly as I could, just hope it's understandable.
PS: I would like to keep my SQL structure as the data is going to become quite consequent over time and I believe that the SQL is the way to go when manipulating thousands of lines.
It's not a very confusing scenario, it's just made confusing by the complex handling that goes on there. It can indeed be made much easier using bindings and MVVM (Model-View-ViewModel) so please take some time to read about the basics of that. There are a ton of tutorials and introductory materials on the web, a simple search will give you more than enough to go on.
When you're comfortable with the concepts, all you need is to transform the data into a sequence of objects (no matter how you go about it), then use an ItemsControl to represent the UI for a list of items. Use DataTemplates to specify how each item should be displayed, binding elements in the DataTemplate to the properties of each item. These things can be nested so you can have ItemsControls in your DataTemplates which use other DataTemplates etc.
In order to represent a collection of items bound to an ItemsControl look at using an ICollectionView which will help tremendously with filtering/sorting/etc.
Sorry about the very broad strokes but it is a pretty broad question. If you need more specific help I'll gladly add more.

Customizing the FrameworkElements used to display the contents of an ObservableCollection

So it seems like what I want to do should be straightforward, but I haven't been able to find a way to do it...
I need to display a list of objects that represent custom elements for entering data. Exactly how each object is displayed depends on the parameters of the object - so it could be a grid containing a name, description, and text box. It could be a grid with a couple labels and a dropdown. It could be an expander that contains multiple sub-objects. It could be something new that hasn't been built yet (so it needs to be extensible). Right now, I am populating this list by creating the FrameworkElement for each object and then adding it to a Grid by hand.
I would like to switch to keeping my objects in an ObservableCollection, and then binding that collection to a ListBox (or similar). That way, when new objects are added or removed from the list, the UI would automatically update itself accordingly. What I can't figure out is, is there a way to point it to my C# method for creating the custom-configured FrameworkElement for each object, so that when new objects are added the appropriate elements will be added to the UI?
Well, you're on the right track as far as wanting to use an ObservableCollection<T> and a ListBox control. Though, I'd venture to say you might want to simply use an ItemsControl since you probably don't care about selecting a particular item, but merely displaying an enumeration of items, and the ListBox would allow you to actually select one of those items.
Your problem is that you want each item in the list to display differently depending on certain criteria. For this you'll want to look at the DataTemplate and DataTemplateSelector classes.
Basically, a DataTemplate is a way of saying "I want my item to look like this.", and a DataTemplateSelector is a way of saying "I want to select this specific DataTemplate based on this criteria."
Here are a few examples on how to use the DataTemplate/DataTemplateSelector classes:
http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector
http://mikestedman.blogspot.com/2009/04/datatemplateselector.html
http://wpftutorial.net/DataTemplates.html
Seperating the presentation from the model is always a good idea. It seems like you are on the right track.
Foreach object type, you should create a DataTemplate and then use ItemTemplateSelector to select the correct template for each object type.
Good luck

What's the best way for me to bind data to a DataGridView?

I'm quite new to C# (coming from a Java background) and I'm working on a Form to quickly manage different data (e.g. Users, Domains, Computers) along with providing utilities that use the data.
To avoid confusion the following is a summary of the example data structure;
<User>
<Name>joe</Name>
<Domain>europa</Domain>
</User>
<Domain>
<Name>europa<Name>
<Domain>
<Domain>
<Name>group</Name>
</Domain>
<Computer>
<Name>localhost</Name>
</Computer>
In my Form I have tab pages for each data, each containing a DataGridView (containing a column for each member) to allow for simple management. I'm using a single DataSet which reads the data from an XML file (using a schema) when the Form Load event is fired. After reading the data I am setting the tables as the DataSource of their respective DataGridView using the code;
userDataGridView.DataSource = dataSet.Tables["User"];
domainDataGridView.DataSource = dataSet.Tables["Domain"];
computerDataGridView.DataSource = dataSet.Tables["Computer"];
This is working properly for the Domain and Computer DataGridViews as they only have single text columns which are mapped to the Name properties. However, the Domain column of for the User DataGridView is a combo box and I need that to contain all the possible Domains (e.g. europa, group) as well as the selected Domain being bound to User.Domain (also, the User.Domain being initially selected).
My main question is how I achieve the above but I also have some additional questions hopefully someone can answer;
I'm assuming that changes made to a DataGridView are immediately persisted in the underlying XML file as I am reading it from the build's output directory. Is this correct or is additional configuration/process required?
During my attempts at getting this to work I tried to use a DataRelation as follows;
DataRelation dataRelation = new DataRelation("Domain users",
dataSet.Tables["Domain"].Columns["Domain"],
dataSet.Tables["User"].Columns["Domain"]);
dataSet.Relations.Add(dataRelation);
How exactly do DataRelations work and what effect do/can they have?
In case it helps you understand/explain I am using the SharpDevelop IDE for working on this application.
Thanks in advance.
As you have already found out, the DataGridView is very powerful and does most of the work in an automagically way. Unfortunately you run into problems if these defaults doesn't match your preferences (like using a ComboBox for a text property).
To get the DataGridViewComboBoxColumn into the proper place you can do this programmatically within your code or (if possible) do it with the designer (in Visual Studio don't know if SharpDevelop supports it).
Using the (Visual Studio) Designer
For this scenario it is necessary that the structure of the data is known at design time by providing a class holding all (or more) informations as simple properties (like a person class with properties name, age, etc.). If this is available you can add a BindingSource to your control and click in the properties window at the button next to the DataSource property. Select Add Project Data Source and select Object and select your desired object.
Now you have a binding source configured with a specific type of DataSource. Next select your DataGridView and change the DataSource property to the recently created binding source.
After this the DataGridView will automatically be populated with a column for each property. Now you can easily step into the Columns property and change the behaviour and type of each column as you like.
To connect your concrete data with the DataGridView just apply your table to the binding source.
Doing the same at runtime
If you have Visual Studio and followed the above steps you can afterwards easily take a look into the Designer.cs file to find how Visual Studio did all the stuff. There is nothing you can't do also manually. So if you need to just do it.
Simply create a DataGridViewComboBoxColumn, set the DataPropertyName and HeaderText and you've got a good starting point. Get the IndexOf() the column you want to replace, remove it and Insert() your freshly created column at the position you want.
Before the Grid can show some data (in the ComboBoxColumn) you need to provide a list with the possible values. For this purpose the comboBoxColumn has a DataSource property on itself where you can provide the desired values. With the simplest scenario you just provide an ICollection<string>, but you can also give something more complex like List<KeyValuePair<Enum, string>>. If that's the case you should also set the properties ValueMember and DisplayMember to tell the ComboBox which properties of the objects should be used to populate the list.
Maybe with these informations given you should take a look at the MSDN article about the DataGridViewComboBoxColumn and study the example. This should give you some additional hints on how to set it up properly.

Trying to figure out how to allow a user to edit a collection with a DataGrid

I'm working on a plug-in for a 3D modeling program to assist in architectural design. I have a Building class which needs to contain a collection of Floors.
My Floor class has properties such as Elevation, Height, ProgramType (residential, retail, etc), and ID. Each building has a FloorList property which is the collection of all the Floors contained by the Building. The properties of a Building are generally viewed through a property grid control and I was planning on using a custom UITypeEditor to allow the user to edit the FloorList collection.
I'm trying to figure out the best way to create this FloorList collection class so that creating the UITypeEditor is as easy as possible, and I can take advantage of any existing collection editing ability's in the .NET controls. For the editor I was thinking I'd use a DataGrid view to show all the floors in the collection something like the table below.
Height Elevation ProgramType ID
15' 70' Residential 23423
15' 55' Residential 42342
15' 40' Residential 98723
20' 20' Retail 23454
20' 0' Retail 98723
The user should be able to edit the height of the floor through the DataGrid, but the elevation of a floor would be derived from the sum of the floor heights below it. The program type should either be a text box or a drop down, and the ID is a read only value shown for information only.
The interface should also allow the user to edit the members of the collection such as adding one or more floors, removing one or more floors, or changing to order of the floors.
The DataGrid control has some built in functionality for allowing a user to add and delete rows, but is there built in functionality to allow a user to reorder the rows?
The only times I've ever used a DataGrid has been to show a DataTable. Is there a straight forward way to use a DataGrid control to allow a user to see and edit properties of objects in a collection? What about being able to add and remove objects from that collection by adding or removing rows from the DataGrid?
One caveat to this problem I have is I can't just edit the collection items directly, as changes to one item will affect another. For example if I remove a floor I'll need to move down (change the elevation of) any floors above the removed floor. Likewise if I increase the height of a floor I'll need to move any floors above the taller floor up. So any changes to the collection or items in the collection will need to be done through the collection itself rather then directly at the item.
So again I was thinking my FloorList collection would implement IEnumerable so that other methods could itterate over all the floors in a building. But any actual manipulation of the collection or objects in the collection would be done through Methods of the collection such as....
FloorList.AddFloor(Floor floorToAdd);
FloorList.RemoveFloor(Floor floorToRemove);
FloorList.MoveFloor(Floor floorToMove,int newPosition);
FloorList.ChangeFloorHeight(Floor floor);
etc..etc...etc..
Sorry for the long question, especially with out any sample code. But right now I don't really have any code written as I'm for once trying to plan out my work rather then jumping right into the code.
Update
I've been reading some examples and it looks like the DataGrid will show properties of objects in an IList. So now I'm thinking it makes more sense to implement a List, but set up the overridden methods to handle changing the floor elevations. If I'm ultimately going to be binding this list to a DataGrid control would it be better to base the FloorList collection of BindingList or IBindingList instead?
Update
I've done some more reading and decided to completely reword my question. The new version is here.
To answer your first question: In your business layer I would have Create, Add, Edit, Delete (and so on) methods that handle all your custom logic (just have it pass in your models to these methods). Then call those methods through the OnEditing, OnUpdating, etc. events from the DataGrid.
To answer your second question, BindingList will give you a built in OnChange event whereas the List will not. If this is being used in a Windows Form app, BindingList will come in handy. But using that event in a Web app can get kinda hairy. It depends on how you want to implement the updating logic in your business layer.

Categories