How does ItemsControl remember its focused element? - wpf

Consider the following layout:
<Button Grid.Row="0">Before</Button>
<ItemsControl x:Name="MainItemsControl"
Grid.Row="1"
KeyboardNavigation.TabNavigation="Once"
KeyboardNavigation.DirectionalNavigation="Contained"
IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBoxItem>Test1</ListBoxItem>
<ListBoxItem>Test2</ListBoxItem>
<ListBoxItem>Test3</ListBoxItem>
</ItemsControl>
<Button Grid.Row="2">After</Button>
With regards to keyboard navigation, this works as you would expect:
With three <Tab>s, you can navigate from the first button to the itemscontrol and finally to the second button.
When tabbing into the ItemsControl, you can use cursor keys to navigate the contained items
When tabbing out of the ItemsControl and back into it, the last focused item is restored
Now, let's replace the ListBoxItems with another level of ItemsControls:
<ItemsControl x:Name="MainItemsControl"
[...]
<ItemsControl KeyboardNavigation.TabNavigation="Once"
KeyboardNavigation.DirectionalNavigation="Continue"
IsTabStop="False">
<ListBoxItem>One1</ListBoxItem>
<ListBoxItem>One2</ListBoxItem>
<ListBoxItem>One3</ListBoxItem>
</ItemsControl>
<ItemsControl KeyboardNavigation.TabNavigation="Once"
KeyboardNavigation.DirectionalNavigation="Continue"
IsTabStop="False">
<ListBoxItem>Two1</ListBoxItem>
<ListBoxItem>Two2</ListBoxItem>
<ListBoxItem>Two3</ListBoxItem>
</ItemsControl>
</ItemsControl>
By and large, this still works as desired. You now can use left+right arrow keys to navigate between "One" and "Two" items.
However, when I focus a "Two" item, then tab out of the ItemsControl and back into it, the focus of the last selected "One" item (i.e. the last focus of the leftmost inner ItemsControl) is restored.
What kind of mechanism causes the the ItemsControl to remember its inner focus and why does it break when I add another layer of ItemsControls inside?
(Note: No manual FocusScopes or the like are defined so far. By tinkering with FocusManager, you can make WPF restore the correct focus. I am wondering about the vanilla default behavior.)

Related

How to make the items in a WPF ListBox wrap horizontally and vertically

I want to show a list of thumbnails, and allow the user to choose one. A ListBox seemed an obvious choice, setting the template to include the thumbnail and description.
The following code does this correctly...
<ListBox Margin="5"
Name="BackgroundsLb">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5"
BorderBrush="Black"
BorderThickness="1">
<StackPanel Margin="5">
<Image Source="{Binding Path=Data, Converter={StaticResource BytesToImageVC}}"
Width="150"
HorizontalAlignment="Center" />
<TextBlock Text="{Binding Path=Description}"
HorizontalAlignment="Center" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
However, this displays the thumbnails vertically, one per row, as is normal for a ListBox. Given that the thumbnails are only 150 pixels wide, I would like to show them in something more like a grid, but (ideally) in a way so that the number of columns adapts to the size of the window.
I tried replacing the ListBox with an ItemsControl, adding in the following...
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
...and it displayed exactly how I wanted it, but the ItemsControl does not allow selection, so is no use for my purpose.
Is there a way to achieve a flexible, selectable display that fills the horizontal space, and breaks onto a new row according to the size of the window?
Thanks
You can use an ItemsPanelTemplate in a ListBox just the same as you are using one in the ItemsControl. The difference I think you're seeing is that ListBox uses scroll bars by default rather than wrapping the content. Basically the content is allowed to grow forever, so you never get the wrap. Instead you get a scrollbar. The good news is you can disable this behavior. The following should give you a horizontal wrap, where new rows are created as needed.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Binding several ItemsControl to one ObservableCollection

I have two tabs.
The code of both tabs is in the same XAML file (yes, it must be)
Each tab references the same ObservableCollection
<ItemsControl ItemsSource="{Binding ConnectorItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Depending on the tab, the collection is reloaded and contains other data
The problem is that it only displays in the last tab.
I know that this results from logical parents but I am looking for a solution in this situation
If I had a bookmark code in a separate XAML it would not be a problem but I can not use it

WPF itemscontrol child alignment

I have a WPF MVVM application with an itemscontrol. The number of horizontal child items is influenced by the itemscontrol width. There is only 1 thing i haven't solved. I'm trying to align the child elements in a way that there are always centered. I have made quick pictures in paint to demonstrate what i mean.
How it's now:
If the width further inceares then a forth item will be added horizontally. This function needs to stay.
How I would like to see it:
If there is enough room for a forth item then it needs to be added. I'm thinking that the answer could be a simple XAML property. Anybody an idea?
Put the ItemsControl in a Grid and set its HorizontalAlignment to Center:
<Grid>
<ItemsControl ItemsSource="{Binding Images}" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" .../>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

showing different user controls - WPF MVVM

I created a dbml file, which automatically created the designer.cs
In the designer.cs (which is the Model in the MVVM) there are two different classes in the database:
ElementA and ElementB.
I have two user controls:
Element_A_UserControl - displays a single instance of ElementA
Element_B_UserControl - displays a single instance of ElementB
There is another user control that has two stack panels.
The first stack panel displays a list of Element_A_UserControl
The second stack panel displays a list of Element_B_UserControl
Here is the stack panel #1 XAML:
<StackPanel>
<ItemsControl ItemsSource="{Binding AllElements_A}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<vw:Element_A_UserControl x:Name="elementA">
</vw:Element_A_UserControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Here is the stack panel #2 XAML:
<StackPanel>
<ItemsControl ItemsSource="{Binding AllElements_B}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<vw:Element_B_UserControl x:Name="elementB">
</vw:Element_B_UserControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Up to now everything works fine.
I want to have one stack panel, which displays a list of ElementA or a list of ElementB depending of a condition.
Note: The property to get list of elements is different.
i.e.
ItemsSource="{Binding AllElements_A}
ItemsSource="{Binding AllElements_B}
I hope that my question is clear enough.
Dazy.
One way is that you could try to use a conditional DataTemplate. Something like this:
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:ElementAType}">
<vw:Element_A_UserControl x:Name="elementA">
</vw:Element_A_UserControl>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ElementBType}">
<vw:Element_B_UserControl x:Name="elementB">
</vw:Element_B_UserControl>
</DataTemplate>
</ItemsControl.Resources>
Then, in your viewmodel, create:
public ObservableCollection<object> CombinedCollection {get; set;}
and load it with either of your collections conditionally.
Alternatively, keep both ItemsControls in your XAML, and conditionally hide/show them using Visibility and a BooleanToVisibilityConverter. Given these two choices, I would likely choose this one, as it is clearer in the code, and easier to maintain than the conditional DataTemplate above. However, you seemed to indicate that you didn't want to do that, so I presented the first one as an option.

Silverlight 3 Checkbox Listbox bug when scrolling?

I've spent a few minutes searching on Google and have not found anything related to this issue I'm having:
Today I upgraded to the Silverlight 3 SDK and converted a project that I'm working on. I then noticed a bug in my program with a Listbox that has a Checkbox as its DataTemplate.
When one or more items is checked, and I scroll up and down, it seems that a few of the Checkboxes at the extremes get checked off and on randomly. This does not trigger the Checked/Unchecked event, however.
Has anyone seen this behavior? I'm not doing anything out of the ordinary, simply scrolling up and down once at least one checkbox has been checked, and a couple of others that I have not touched seem to get checked on and off repeatedly. This was definitely not happening with the Silverlight 2 SDK.
Here's the XAML definition for my Listbox:
<ListBox x:Name="cBoxSalesmen" Width="135" Height="200"
HorizontalAlignment="Left" VerticalAlignment="Top">
<ListBox.Template>
<ControlTemplate>
<Border Style="{StaticResource BorderStyleThin}">
<StackPanel Orientation="Vertical">
<TextBlock Text="Salesmen" />
<ScrollViewer Height="176" VerticalScrollBarVisibility="Visible" >
<ItemsPresenter />
</ScrollViewer>
</StackPanel>
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Margin="0" Content="{Binding}" FontSize="10" HorizontalAlignment="Left"
Checked="SalesmenCheckbox_Checked" Unchecked="SalesmenCheckbox_Unchecked"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The default ItemsPanel of the ListBox is the VirtualizingStackPanel. You can change it to use the StackPanel, this way you problem is solved.
Use this code:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
<ListBox.ItemsPanel>
I suspect your problem is a result of ListBox (in SL3) now using an ItemCollectionGenerator. The concept behind this is that not all the objects found in the source data collection need to have had their corresponding instance of the DataTemplate created and added to the Visual Tree. As you scroll toward the bottom items that may soon be needed are created. Additionally items that have already be created but are now scrolled quite same way out of view can be removed. If the user scrolls up they are re-created.
If this is the case then the IsChecked state of any checkbox in this list will be lost at some point for large lists. To solve this you would need include a property in the data type to which you can bind IsChecked. Hence as ListBox re-creates items it correctly assigns the IsChecked value.

Resources