DCSIMG
June 2010 - Posts - Pavel's Blog
Sign in | Join | Help

Pavel's Blog

Pavel is a software guy that is interested in almost everything
software related... way too much for too little time

June 2010 - Posts

DirectCompute on Channel 9

Published at Jun 20 2010, 04:18 PM by pavely

DirectCompute is a new API introduced in DirectX 11, that allows more general-purpose code to run on the GPU (not just graphic rendering).

Channel 9 has started a lecture series on DirectCompute. Currently two lectures are available, and the other five lectures, will be available on the 21, 25, 28 (June), 1, 5 (July).

The first introductory lecture can be viewed here. The second lecture is here.

Check it out!

TabControl Take 2

Published at Jun 06 2010, 09:15 PM by pavely

Maybe I was too harsh on the poor TabControl in my last post.

For example, we can create a tabbed image viewer by binding the Image’s Source property to a corresponding BitmapImage object. However, this case is rather simple, and is not adequate for more complex scenarios, where data binding alone is not sufficient or not efficient enough.

The basic idea I used to implement a more straightforward and predictable tab control is based on the ListBox. Yes, ListBox. One of the well known (and mostly) used customizations of a ListBox is changing its items panel. By default, it uses a VirtualizingStackPanel, with the default vertical orientation, hence giving the ListBox its famous “list” style. The most often used change is changing the StackPanel’s orientation to horizontal, like so:

 <ListBox.ItemsPanel> 
     <ItemsPanelTemplate> 
         <VirtualizingStackPanel Orientation="Horizontal" /> 
     </ItemsPanelTemplate> 
 </ListBox.ItemsPanel> 
 
 

This may work out fine for the “Tabs”.

Another, sometimes useful, panel change is to use a WrapPanel. This give a kind of “flowing” feel to the items, changing their position depending on the available space.

The panel used can be any panel, but it seems the other WPF panels are simply not useful for this: for example, if we take a Canvas to replace the StackPanel, what would we get?

The ItemsControl and its derivatives simply call the Panel.Children.Add method passing each created object (e.g. ListBoxItem) to the panel. For the Canvas, all children will be bound to the (0,0) position, all elements on top of each other. This clearly is not useful.

But what about a UniformGrid? Each call to Children.Add adds an object to the next cell in the UniformGrid. Maybe this could be useful for some purpose, maybe a Tic-Tac-Toe style app?

For our purposes (remember, changing tab contents) we can use two list boxes: the first, using a horizontal StackPanel that mimics the TabItem headers (we can actually use the real TabControl for this, ignoring its ContentTemplate issues). The second (and more interesting) ListBox would use a 1x1 cell Grid as its items panel. What? Didn’t I just say a Grid is not very useful? I was talking about a UniformGrid. A normal Grid is different.

If we use a 1x1 cell Grid, each call to Children.Add would add the ListBoxItem to the same cell. We would get (in our multi-notepad example) multiple textboxes occupying the same space. Doesn’t seem very useful, does it? The only remaining trick is to use a trigger that will show the selected Grid only (based on the SelectedItem of the first ListBox/TabControl). To round things off, we’ll customize the ListBoxItem template to eliminate the blue “selection” behaviour, as we don’t want our textboxes to be able to get selected in any way. Another important detail is setting the IsSynchronizedWithCurrentItem to true for both TabControl/ListBox, so clicking a tab (changing selection) also changes the selected item in the lower list box, thus showing the correct text box element.

Here’s the entire markup:

     <Grid> 
         <Grid.RowDefinitions> 
             <RowDefinition Height="Auto" /> 
             <RowDefinition Height="Auto"/> 
             <RowDefinition /> 
         </Grid.RowDefinitions> 
         <TabControl Grid.Row="1" ItemsSource="{Binding Documents}" IsSynchronizedWithCurrentItem="True"> 
             <TabControl.ContentTemplate> 
                 <DataTemplate> 
                     <Border Visibility="Collapsed" /> 
                 </DataTemplate> 
             </TabControl.ContentTemplate> 
         </TabControl> 
         <ListBox Grid.Row="2" ItemsSource="{Binding Documents}" IsSynchronizedWithCurrentItem="True"> 
             <ListBox.ItemsPanel> 
                 <ItemsPanelTemplate> 
                     <Grid /> 
                 </ItemsPanelTemplate> 
             </ListBox.ItemsPanel> 
             <ListBox.ItemTemplate> 
                 <DataTemplate> 
                     <TextBox AcceptsReturn="True" /> 
                 </DataTemplate> 
             </ListBox.ItemTemplate> 
             <ListBox.ItemContainerStyle> 
                 <Style TargetType="ListBoxItem"> 
                     <Setter Property="Template"> 
                         <Setter.Value> 
                             <ControlTemplate TargetType="ListBoxItem"> 
                                 <ContentPresenter /> 
                             </ControlTemplate> 
                         </Setter.Value> 
                     </Setter> 
                     <Setter Property="Visibility" Value="Hidden" /> 
                     <Style.Triggers> 
                         <Trigger Property="IsSelected" Value="True"> 
                             <Setter Property="Visibility" Value="Visible" /> 
                         </Trigger> 
                     </Style.Triggers> 
                 </Style> 
             </ListBox.ItemContainerStyle> 
         </ListBox> 
         <Button Content="Create Document" Click="Button_Click" FontSize="20" Margin="10"/> 
     </Grid> 
 
 

I think it’s pretty elegant and works really well. In a real app, you’ll of course customize the tab appearance, add small close buttons, etc. I’ll leave that as an exercise for the enthusiastic reader.

WPF’s TabControl Issues

Published at Jun 05 2010, 10:47 PM by pavely

The TabControl class derives from Selector, which derives from ItemsControl. It’s supposed to host a collection of TabItem objects, with easy switching ability by the user, effectively creating the so-called “Tabbed Document Interface” that is so common today, replacing the old Multiple Document Interface (MDI) scheme.

At first, it looks like TabControl is just what is needed for such an interface. For example, let’s create a simple notepad close with multiple open documents managed by a TabControl:

     <TabControl> 
         <TabItem Header="File 1"> 
             <TextBox AcceptsReturn="True" Text="Text 1"/> 
         </TabItem> 
         <TabItem Header="File 2"> 
             <TextBox AcceptsReturn="True" Text="Text 2"/> 
         </TabItem> 
     </TabControl> 
 
 

This creates two tabs with two textboxes.

Although this simple code works, and by extension we can create additional TabItem objects as needed – this becomes clumsy as we need to add or remove tabs manually, perhaps create a more interesting header, etc. This is not the true WPF way. We need data templates and data binding.

Our first attempt will try to mimic the way we work with other ItemsControl types – by using the ItemTemplate property:

         <TabControl Grid.Row="1" ItemsSource="{Binding Documents}"> 
             <TabControl.ItemTemplate> 
                 <DataTemplate> 
                     <TextBox Text="Hello" /> 
                 </DataTemplate> 
             </TabControl.ItemTemplate> 
         </TabControl> 
 
 

The ItemsSource property is bound to some ObservableCollection<>, on which we call Add when a new document should be added. After calling Add twice, this is the disappointing result:

image

It seems that “Items” are in fact the tabs themselves – the textbox used for editing is part of the tab, and the “Content” has been fiilled with the ToString() of our custom Document type.

Looking again at TabControl, we find a ContentTemplate property that sounds promising. Let’s try that:

 <TabControl.ContentTemplate> 
   <DataTemplate> 
     <TextBox Text="Hello" /> 
   </DataTemplate> 
 </TabControl.ContentTemplate> 
 

At first glance, it seems we got it just about right:

image

Until we realize, that there is in fact only one textbox instead of two! We can add as many tabs as we want, no new textboxes are actually created. On second thought, maybe this is to be expected, because ContentTemplate is for the TabControl, not the TabItems themselves. The documentation states that this property is applied if it does not exist for individual TabItems. Well, maybe we should apply it for TabItems.

So, we’ll try another approach. Let’s template the TabItems and not the TabControl. We can do that by using the ItemContainerStyle property. Perhaps we should create the DataTemplate there:

             <TabControl.ItemContainerStyle> 
                 <Style TargetType="TabItem"> 
                     <Setter Property="ContentTemplate"> 
                         <Setter.Value> 
                             <DataTemplate> 
                                 <TextBox Text="Hello" AcceptsReturn="True"/> 
                             </DataTemplate> 
                         </Setter.Value> 
                     </Setter> 
                 </Style> 
             </TabControl.ItemContainerStyle>
 
 

Running this produces the same result as the previous code! That is, only one textbox is ever created! This is somewhat strange, as we are customizing the content of each and every TabItem, so the textbox creation should repeat.

After some investigation, including using Snoop to look at the actual created visuals, we find the tabs are created as needed, but the content is not.

This seems like a kind of bug to me, as we can create multiple textboxes (for example) using plain old Items.Add style code. We can’t get that with data binding and data templates. I’ve even tried to replace the control template of TabItems, but the result is pretty much the same.

How can we create “true” tab items without deriving new classes? One approach I’ve used is using plain old ListBoxes with some interesting customizations. For the details, stay tuned for part 2 in this two part (exciting) series.