I have a WPF Caliburn.Micro application that has a tab control with a DataGrid in it. The columns in the DataGrid are set programmatically. I may need to close a tab, and then open it again. And I get an exception, saying a column already exists in the DataGrid, though in fact there are no columns at all. Could you please help me with this?
On the picture, you can see in the Watch window that myGrid.Columns.Count equals 0
It looks like you might be adding a column which already exists (in the previous datagrid) to another datagrid. The error says that the column exists in the Columns collection of a datagrid, not neccessarily the one you are trying to add it to
DataGrid cannot share columns - you need to remove the column from the previous datagrid. This may mean you have a memory leak somewhere too. If your tab item VM inherits Screen you might want to override OnDeactivate and clear down the columns
Alternatively, you may be able to get a reference to the parent grid via the column and remove it from it's parent, though I've not used the standard .NET DataGrid myself (just used Teleriks one) so I can't be sure - personally though, the clear down on close seems like a cleaner implementation.
It shouldn't be the responsibility of a new instance of a class to clear up after a dead instance, it should be the responsibility of the class to clean up after itself (I keep telling my kids this!)
Related
I am planing to show hotel room booking status as a color effect. See the image. At want control I want to use?
Dude, this is WPF... you could use just about any control that you want to do that. Ok so I'm exaggerating a bit, but it really is your choice. You should use whatever you're comfortable with. However, if I had to design a UI that looked like your image, I'd probably either go with a ListBox or a DataGrid with a fairly high level of customisation.
If I were to use a DataGrid, the day names could be the column headers and the room names could be the row headers. You'd also need to use some sort of custom ColorConverter class to colour the cell data in the cells.
Using a ListBox, I'd create a DataTemplate to apply to the ListBox.ItemTemplate property that would contain the whole row including the room name. Of course, this would mean that you'd need to structure your data differently to include the name of each room. With a known number of days/columns, you could just use a Grid in the DataTemplate with the Grid.IsSharedSizeScope property set to True to keep the columns of each item aligned.
Good luck and have fun with that... it seems like an interesting project.
I've written a custom panel which displays its children docked either vertically or horizontally, separated by moving splitters in between. Since the Grid panel offers much of this functionality out-of-the-box, I just inherited from it.
To create the layout, upon Loaded is fired I do the following:
1) Read how many children it has and create the appropiate number of rows/colums.
2) Position every existing children in the corresponding row/colum.
3) Create, position and add a GridSplitter for every child.
This approach looks and works fine, but it opens the door to a lot of problems:
Since it's added a GridSplitter for each child, there are twice the number of expected children. If someone added 3 elements to it, Children.Count would return 6.
User could insert/remove things at the wrong place.
It just throws an exception when this Grid is used as the ItemsPanel for an ItemsControl, since in this case WPF (not Silverlight) does not allow direct children manipulation.
These 3 cases are the ones I've already tested, but I'm pretty sure a lot more would arise depending on what the user does with it.
As it turns out, this class must be regarded as 'implementation details', so the real question is, what control should I put in front of the user?
It sounds like it should be a Panel, but I can't control the Children property since it's not virtual, and there's also the ItemsControl which I think could be a good candidate, but I really don't know.
I'd much appreciate any kind of advice or some directions to do this the right way.
Thanks in advance.
You see using just grid you leave yourself with an imperative way of adding items only. As in
myCustomGrid1.AddMyItem(***), Grids simply don't have ItemsSource property. ItemsControls do - so if you need support for declarative items sources i.e. myControl.ItemsSource = {Binding ...} you're going to derive your control from ItemsControl. This is not a two liner - making your ItemsPanel Children writable is a big challange - there's no simple way of doing that.
This is all about a small thing overlooked during the Grid's design - splitters shouldn't have been added to Children collection, as Children are visulaizations of your BOs while spliiters are just formatting elements.
Here's what I would do.
Forget about ItemsSource & items altogether - it's aint worht the hassle. The only way to add/remove items to your control will be AddResiazableItem/RemoveResizbleItem. Calls will add items and splitter (for the middle items), extend the number of rows/cols of your grid depeneding on its orientation, set Grid.Row/Grid.Column attached properties for your visual children. You can keep your actual objects internally to support Orientation change.
If at any stage you'll want to bind your control to IEnumerable source - just create an attached behavior, which will iterate through the items and call AddResiazableItem within a loop.
Cheers.
P.S. To moderators - the editor seems to get broken, lads. I cant see the second item.
P.S.S. Got it fixed after a few tries.
I need to write an excel-like grid that can have a lot of cells (400x400). All columns have the same width and all rows the same height. Each cell can contain text or be empty and each cell can have a column and/or row span. I suppose this will never work with the Grid panel and I suppose I will need UI virtualization in both column and row direction.
So my first try was to create a virtualizing grid by deriving from VirtualizingPanel and implement IScrollInfo. This could have "easily" be the solution except that I ran into a problem:
To provide IScrollInfo with the relevant information about scroll size and position and to be able to detemine wich items need to be created (realized) next using the ItemsContainerGenerator, I need to know the column index, row indeox and columnspan for each child item (cell). The only way I can think of to do this is using attach properties. The problem is: I can only read the values of attached properties if the ItemContainer that has them is already realized. So I am in a catch 22 here. To know what to realize I need to realize all items. To provide the data for IScrollInfo I need to realize all items.
So it seems that I am at a dead end with this approach.
Do you have any idea how I could implement a control like this or know how I could reslove the above problem?
It strikes me that you may not need to instanciate the UI elements themselves- you can very easily have a collection of DependencyObject-derived viewmodels, each of which has the WidthProperty and HeightProperty set (and possibly bound to the equivalent Width and Height properties of the visible cell UI element, once those are created).
Storing 160,000 (400x400) class instances shouldn't be a problem, especially if you are able to index into the set using row and column.
I have a strange problem with a listbox. I added only the listbox and a button which adds items to the listbox. If I click an item in the listbox it seems to have some strange multi selection mode on or something... i recorded a short screen cast, see for your self:
http://www.youtube.com/watch?v=zV4424ipNEA
any ideas whats wrong?
That is a known issue, as all those strings are the same the selection gets confused because they all are essentially the same object. If you create two identical strings in .NET it does not necessarily create a new one but may reuse the first instance, i am not an expert on this though.
Either wrap the strings in a class (make them the Content of a ListBoxItem for example) or make sure the values are unique.
The winforms DataGridView has a virtual model and uses the OnCellValueNeeded method to gets cell values from a a user defined data store.
How do i do this in wpf? The DataGrid doesn't seems to have a OnCellValueNeeded method.
Thanks
Not sure if this already helps you:
DataGrid uses UI virtualization, what means that objects are not created before they are visible on screen and the corresponding properties of your source objects will not be called as long as they are not visible. It means also that ui-elements are recycled after they are no more visible.
This is for free and you don't have to do additional coding to use this feature. If I interprete your question right, the main question is, how to know that a property of a business-object is needed. For UI-virtualization, this is when the property is called the first time - You can do some lazy initialization. But beware to put too heavy initialization code into the properties (DB synchronous lookups etc), otherwise your DataGrid will become very slow and unhandy.