Using a Grid as the Panel for an ItemsControl - wpf

I found this tutorial Using a Grid as the Panel for an ItemsControl but I couldn't get it to work.
I get this exception:
'Cannot explicitly modify Children collection of Panel used as ItemsPanel for ItemsControl. ItemsControl generates child elements for Panel.'
caused by grid.Children.Add(child) in this loop
foreach (FrameworkElement child in phantom.Children.ToList())
{
phantom.Children.Remove(child);
grid.Children.Add(child);
// ensure the child maintains its original datacontext
child.DataContext = phantom.DataContext;
}
Is there any work around possible by inheriting from ItemsPanel?

Shoehorning Grid into an ItemsControl is not a good idea.
You loose the function of an ItemsControl. Peter Duniho mentioned in the comments that an ItemContainer is where you see attached properties. For my application I needed to use the Selector functionality. If I used a Grid I would have had to implement this myself but I saved myself a lot of trouble just using an Listbox.
If you where wanting to use a Grid I suggest you look into the ListView as well as GridView. And if this isn't flexible enough you can always write your own ViewBase.

Related

How can you prevent a UserControl from being constructed when its parent is rendered?

I've been working to improve scrolling performance for an ItemsControl. Initially each item is a simple row of information and when an item is clicked, a detail UserControl is expanded below the item. I'm trying to eliminate the processing being done on the detail UserControl during scrolling. I eventually achieved this with a CustomControl, but I feel like I must be missing a simpler way to do it using the existing framework controls.
Initially, this detail view was a UserControl with Visibility.Collapsed. I should note that Virtualizing and Recycling are enabled for this ItemsControl. Therefore, scrolling performance was poor since each detail view was being bound to the data as the item scrolled into view, even though the detail was not visible.
I then tried using a ContentPresenter where the Content was bound to a DetailViewModel property and the ContentTemplate was set to a keyed DataTemplate. That DetailViewModel property was initialized to Nothing and then set to the appropriate ViewModel object when the item was clicked. This improved scrolling performance because there was no data binding, but I found that the detail UserControl was still getting constructed for each item, and I presume there was some rendering going on as well.
I eventually got the desired behavior with a custom ContentControl that provides dependency properties for GatedContentTemplate and IsGateOpen. When IsGateOpen goes True, the GatedContentTemplate is passed to the ContentTemplate property and the Content is set to the DataContext of the control, which causes the content to then get constructed, rendered, and bound.
<my:GatedContentControl IsGateOpen="{Binding IsDetailVisible}">
<my:GatedContentControl.GatedContentTemplate>
<DataTemplate>
<my:DetailUserControl/>
</DataTemplate>
</my:GatedContentControl.GatedContentTemplate>
</my:GatedContentControl>
I can live with this solution, but it's a little janky, and I wonder if I'm missing some way that WPF intends for this to be done.
Looks like I wasn't missing anything and this is a need that Microsoft is addressing in .net 4.6.
The Content Deferral feature is discussed in this video, starting at 26:30.
http://channel9.msdn.com/Events/dotnetConf/2015/WPF-in-46-and-beyond
Thanks for the comment HighCore.

How to check where a Panel's Children come from?

I created a custom Panel, within which I need to check whether its Children are managed by manipulating the collection manually, or whether the panel is used as ItemsPanel for an ItemsControl.
This to prevent the following exception from being thrown when trying to manipulate Children.
Cannot explicitly modify Children collection of Panel used as
ItemsPanel for ItemsControl. ItemsControl generates child elements for
Panel.
How can I differentiate between the two different usages of the panel?
A Panel's IsItemsHost property can be used to check whether the Panel "is a container for user interface (UI) items that are generated by an ItemsControl".
This property can be set manually when creating an ItemsControl template, but also seems to be set to true when using an ItemsPresenter and specifying the panel using ItemsControl's ItemsPanel property.

What WPF control should I inherit from?

I've written a WPF control which accepts a number of UIElement objects as input and displays them docked either vertically or horizontally. The control exposes functions for enumerating, removing and inserting children, but internally I'm using a Grid to build the layout, creating a row/column for each item and inserting a GridSplitter between them. To do this I've inherited from ContentControl, and upon initialization I just set the Content property with the Grid. Everything is working as intended, but now I wonder if this might be confusing for the user of my control, as it would be counter-intuitive to have a ContentControl that has many items.
Should I be inheriting from ItemsControl instead?
Should I inherit directly from Control which is "content-agnostic"?
Is there a better way to do this?
Thanks in advance.
What you describe sounds like a Panel - basically a control which is responsible for the layout of many elements. I would consider inheriting from that.
Your mention of "docked either vertically or horizontally" leads me to believe perhaps a StackPanel would be a better fit, since it does docking as well.

How to invalidate layout of listbox from custom children

I have a custom panel for a listbox
<ItemsPanelTemplate x:Key="FloatPanelTemplate">
<Controls:FloatPanel x:Name="CardPanel" />
</ItemsPanelTemplate>
The panel lays out its children using its X and Y dependency properties.
This all works nicely when the FloatPanel is used by itself - I'm using FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure on the dependency properties of the child items to tell the FloatPanel to redraw its layout.
When I use it in a Listbox (code above) then it draws fine the first time, but when I drag the children (which modifies the item's X and Y) it is not notifying the Listbox that it needs to redraw the FloatPanel's children. I think the issue is related to the fact that each item in the bound collection is wrapped with a ListBoxItem.
Hopefully I've described what i'm doing well enough that someone can tell me how to make the panel (or its children) tell it needs to do the Layout routines again. As I said it works once (initial draw) but then dragging items doesn't work (Listbox isnt aware that its children have changed and needs to relayout.) If I drag an item and then resize the window, the listbox does a layout and the items are drawn in their new locations.
How do I notify the ListBox (or more importantly the FloatPanel in the ItemsPanelTemplate) that it needs to do a Layout pass?
Instead try FrameworkPropertyMetadataOptions.AffectsParentMeasure and FrameworkPropertyMetadataOptions.AffectsParentArrange.
Those names... Thank God for intellisense, huh?
As you've noted, since ListBoxItem is your element's immediate layout parent, changes to the dependency properties that affect the parent's layout will not be "seen" by the panel which is further up the visual tree.
So instead what you may need to do unfortunately is to traverse the visual tree until you find an element deriving from Panel and call its InvalidateArrange method.
DependencyObject obj=this;
while ( (obj=VisualTreeHelper.GetParent(obj)) != null) {
Panel p = obj as Panel;
if (p != null) {
p.InvalidateArrange();
break;
}
}
It's ugly, but maybe a WPF guru will have a better suggestion.
Are you sure that your ListBox or ListView panel isn't seeming to present an incorrect size because it has...
ScrollViewer.CanContentScroll="True"
...which is the default value?
When list control is in this mode, and when it is scrolled to the bottom, there may be extra whitespace at the bottom of its panel in order to make sure that an integral item is lined up at the top of its internal ScrollViewer, and this might seem like the ScrollViewer--and hence the list control--is not resizing itself.
To prevent whitespace at the bottom of the panel, you must enable pixel-wise scrolling on the ListBox or ListView control:
ScrollViewer.CanContentScroll="False"
or in code:
ScrollViewer.SetCanContentScroll(list_ctrl, false);
See also: WPF ListView non integral scrolling

How to find a child from the styled listbox in WPF?

Listbox has the style in which itemtemplate consists of expander and inside expander there is a one more listbox, listbox inside the expander has to be accessed. So how to go ahead?
What data does the inner list box contain? Does your outer object contain a list of child objects as a property? If so, bind the inner listbox's ItemsSource to that property.
If you want to access the listbox from code, this is probably what you're looking for: How to: Find DataTemplate-Generated Elements. It shows how to traverse the visual tree to find the generated elements. Using the visual tree is not a good solution usually though, so consider using data binding instead if at all possible.

Resources