I'm not really sure how to go about my problem. Not looking for complete code just help with in which direction i should go. Since its MVVM I don't want any codebehind... if possible...
From the db i get the size of the "grid" i should create, for example 2x3.
Now i wish to place an "item" in this grid that takes up one or more spots.
I'm currently trying to do this with a ItemsControl that contains a grid (i want a grid because i want to use ShowGridLines="True")
But how do I create a dynamic grid? Thought about using uniformgrid but that one doesn't have ShowGridLines...
Second problem (since i'm a mvvm noob) is selecting the a spot in the grid. The one you click will not be a problem. The problem is if the item you're trying to place takes up two spots. How do I know which spot is the one next to the one i'm clicking?
Any help is appreciated
Since you cant easily Bind the Row/Column definitions of a Grid, i suggest you to build a simple custom control, which inherits directly from Grid.
Here is my approach with 3 additional dependency properties:
int MyColumnCount
int MyRowCount
ObservableCollection MyChilds
the MyColumnCount/MyRowCount will be bound to Properties in the ViewModel, which you update, when you get the new values from the database. Also the ViewModel will provide a collection of FrameworkElements, which will be the items in the Grid and is bound to MyChilds.
You can create new Controls in the ViewModel and use the attached property of the Grid, to set the Position. For example:
TextBlock b = new TextBlock() { Text = "Hello World!" };
Grid.SetRow(b, <your position>);
Grid.SetColumn(b, <your position>);
Grid.SetColumnSpan(b, <your column span>);
MyChilds.Add(b);
In the PropertyChangedCallbacks of the new int properties, you modify the Row/Column definitions, according to the new values. The callback of the collection property registers a collection changed event on the new ObservableCollection and adds the new items to the Grid's children, on changing events.
Thats all you need for the dynamic grid changes.
To your second question:
This is quite easy. In the event you catch you will get the sender of the clicked item in the grid. The sender is mostly the direct clicked control. You can use the static Grid functions again, to calculate the position:
Grid.GetRow(item);
Grid.GetColumn(item);
Grid.GetColumnSpan(item);
With the total number of rows/columns of the grid (My...Count) you can calculate neighbor positions.
Jan
Related
I'm not being able to update a single item's DataTemplate on a list during runtime. In detail, here is what I'm trying to accomplish.
I have a Listbox where the items can have different states (Collapsed, expanded, disabled, etc), each with a different layout. I'm using a TemplateSelector to choose the correct DataTemplate according to a property on my class and that's working all great when I first create the list, the items are shown properly. However, when I change the property that sets the DataTemplate in runtime, the NotifyPropertyChanged is called and the information of the item is updated on the list, but not the DataTemplate. For example: I have a collapsed item with a label X that i want to expand. I click on the item and the label changes to Y but the DataTemplate doesn't update.
Any idea on how I can do this? Can't the DataTemplate be updated during runtime unless it is for the whole list?
I'll appreciate any help.
Make UserControl and use it inside your data template. Now, to change the state you can call methods on this UserControl and it will update. You can use animations via storyboard too.
I'm writing a BHO that uses an ElementHost to host a WPF User Control. Within the user control, I have a DataGrid that binds to an Observable Collection. Everything functions fine, except that the content is not being read by screen readers (I'm using NVDA to test, but QA is using JAWS).
I'm restricted from copy & pasting code on a public forum, but I can describe the layout that I'm creating. There are two datagrid. One contains all of the items from the Observable Collection and the other is a subset of the items. Each datagrid is in a separate Tab Item of a TabControl. As I stated, there is an ObservableCollection that holds my business objects. Each object binds to a row in the datagrid. Several of the columns require that I display multiple properties from the business object, so I'm using the DataGridTemplateColumn. Within the CellStyle, I have 3 DataTemplates set up; one for edit, one for add and one for view. The view DataTemplate is exactly the same as the DataGridTemplateColumn.CellTemplate.
One of the columns holds my action buttons. One of the buttons is an edit button which simply applies the edit template to the rows cells. Outside of the datagrid, I have a button that will add a new default item to the ObservableCollection, call UpdateLayout on the datagrid, then set the DataTemplate of the new item to the add template.
There is also a button that will grab information from a remote server, convert it to business objects and add them to the Observable Collection. The datagrid loads the new information with no problems.
When I use the function to pull the objects from the database, none of the information will be read by the screen reader. If I click the edit button, everything is read as expected. After returning to the "view" DataTemplate, everything reads as expected. If I use the Add button, everything reads as expected.
To make this more complicated, if I edit an item in one tab page and get it to be read, then go to the other tab page and come back, it's no longer reading.
I have a feeling that it's related to the how binding and templates interact, but I don't know enough about either to figure out a direction to go in resolving this.
Any help would be appreciated.
EDIT: While I was creating the dummy project to show the issue, I discovered that the problem is not just limited to Template Columns. I created a business object with string properties, created an ObservableCollection with 10 objects in it and bound each property to a DataGridTextColumn, and it only reads the grid name and column index...the contents are never read.
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.
While working in WPF i have the need for a Dynamic Grid. By this i mean a grid that contains only one kind of object, has a template for that object etc. But unlike a similar ItemsControl like a Listbox, i want the grid to be given a Maximum Columns property. This should act as a delimiter which will then calculate the number of rows needed based on the number of objects within the grid. To do this, i thought of inherriting a Grid to make use of its Row and Column properties, but i have a problem... I dont know how to implement an ItemsSource property outside of inherriting the ItemsSource from an ItemsControl...
so my question comes in two parts...
Am i pursuing this the right way? should i be inherriting ItemsControl and trying to re-implement the Grid behavior
if this is the right way to do it, how do i implement an ItemsSource property with its corresponding ItemTemplate
Perhaps a better way would be to use a ListView? Here is an example how to achieve 3-column output: http://kristofmattei.be/2010/03/16/multi-column-listview/
Do you want something like UniformGrid? If you set the Columns property (and don't set the Rows property), it will automatically figure out how many rows to create to hold its items.
I have a table of about 450 rows that I would like to display in a graphical list for users to view or modify the line items. The users would be selection options from comboboxes and selecting check boxes etc.
I have found a listview class that extends the basic listview to allow for embeding objects but it seems kind of slugish when I load all the rows into it.
I have used a datagridview in the past for comboboxes and checkboxes, but there was a lot of time invested in getting that up and running...not a big fav of mine.
I am looking for sugestions how I can do this with minimal overhead.
thanks
c#, vs2008, .net 2.0, system.windows.forms
If you have a complicated set of controls for each row, this is the simplest way to do it. However, it won't act like a listbox; you won't be able to highlight your rows or navigate with the keyboard.
Create a usercontrol with public property to point to your row
Draw a panel on your form - you will add instances of your 'row' usercontrol at runtime to this panel.
Set the panel to autoscroll (also set property to make the active control scroll into view)
Set the panel's Anchor property so it sizes w/ the window
You can set the form's max/min size properties so the full usercontrol row always shows (have to do to prevent horiz. scroll bar in panel)
Have a routine to add the rows
In a loop, create new usercontrols, set their properties, including the row in the datatable
Also, set the .Top property to the panel's .controls(pnl.controls.count-1) for all but the first one you add
Very simple, allows complicated 'rows', gets the job done. There are better ways to do this if you want listbox-like functionality without coding it yourself, but you may not need that.