I customized a Listbox to show a Pie-Chart (each Listitem is one slice of the Pie). To do this i used an Itemtemplate which (for now) only consists of a Shape. To make those shapes form a full circle, i calculated start/endangle for each piece and used a custom ItemsPanelTemplate to stack the Items on top of each other.
When I click anywhere near the Pie, the "last" item gets selected since it is located on top of the others. This is quite obvious, but I hoped since the ItemTemplate only contains a Shape, it would inherit the hit-testing from there and not assume that all items are represented by rectangles.
Where am I supposed to include the hittesting? I would like to set IsHitTestVisible="false" to everything inside my ItemTemplate, except for the shape - but since it doesn't actually contain anything except for my shape, i am stuck right now.
Edit:
I tried surrounding my Shape with a Grid with transparent background, on which i did set IsHitTestVisible="false". This still results in selecting the last element on each click while i would've assumed that nothing would be selected. I think i might be confused about how hittesting is supposed to work?
Code:
Since i am new to WPF i might have missed something during the implementation. I added the relevant codeparts of a minimal example:
My Shape-class:
public class PiePiece : Shape
{
protected override Geometry DefiningGeometry
{
get { return GetPieGeometry() }
}
//some DependencyProperties and helper methods.
private Geometry GetPieGeometry()
{
//calculations
}
}
XAML:
<Window [...] xmlns:Runner="clr-namespace:MyNamespace">
<Window.Resources>
<!-- some custom converters -->
<!-- ListBox-Style with the custom ControlTemplate for my Listbox -->
<ItemsPanelTemplate x:Key="ItemPanel">
<Grid/>
</ItemsPanelTemplate>
</Window.Resources>
<ListBox [...] ItemsPanel="{StaticResource ItemPanel}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="{x:Null}" IsHitTestVisible="False">
<Runner:PiePiece IsHitTestVisible="False" [...]>
<!-- Dependency Properties are set via Multibinding here -->
</Runner:PiePiece>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
I finally found the reason why the hittesting did not work as desired:
The default template for the ListBoxItem-Style surrounds the ContentPresenter with a Border with transparent background. All click-events were caught and handled by that border, instead of my ContentPresenter. Writing my own style for the ListBoxItem and setting the Background to {x:null} as suggested by Gaz fixed the problem (as did removing the border, but I added another one by now for further customizations).
Related
I have a requirement which is to display a user information at top of the page and a ListView of images will follow it, and I've wrote following code (it's a pseudocode but I think it's enough to explain what I've done):
<ScrollViewer>
<StackPanel>
<Grid>
<!-- User Information Part -->
</Grid>
<ListView>
<!-- Images Part, This is a custom virtualized ListView, it's ItemsPanel is a custom VirtualizingWrapPanel -->
</ListView>
</StackPanel>
</ScrollViewer>
But in this scenario, the VirtualizingWrapPanel (by which has been tested on another individual ListView without an explicit ScrollViewer declaration and it works correctly) and the virtualization of ListView won't work because the desired height of ScrollViewer is positive infinity and all the items in the ListView will be expanded and rendered, I wonder whether there is a way that can make the ListView in ScrollViewer being virtualizable? Thanks
You can't virtualize a list that has all elements being rendered (because of the StackPanel),
A workaround that will work for you: you need a single ListView. With the first row customized to display the User Information Part, and all other rows displaying images.
Im about to create a diagram designer, and have create nodes and edges as usercontrols.
I have an stackpanel where I want to place them. I have managed to make it works with the following code:
<Window.Resources>
<DataTemplate DataType="{x:Type Model:Node}">
<Canvas>
<View:NodeUserControl></View:NodeUserControl>
</Canvas>
</DataTemplate>
</Window.Resources>
<StackPanel Name="DisplayArea">
<ItemsControl ItemsSource="{Binding Nodes}" >
</ItemsControl>
</StackPanel>
Where Nodes is an observablecollection
But this also shows an {NewItemPlaceholder} text and I can't figure out why. Would really appreciate if someone could point out my mistake.
EDIT: I have tried to create a new solution with just the beforementioned code and this doenst show the [NewItemPlaceholder}. Now Im really confused can't see the difference and what else that would cause this.
I got the same issue and the cause was that die source data was bound twice: One time to a DataGrid control and a second bound to an ItemsControl. The ItemsControl shows its items on a canvas and also {NewItemPlaceholder} becomes visible there.
The solution was to avoid adding new items to the DataGrid by:
ok. I didn't need adding.
I have a list (ListBox) of items in XAML using a StackPanel- based element template. The layout is fine, but I would now like to have a rectangle as a background for each item - creating a box around each one.
I was thinking of using a Canvas somehow, but as each item's height varies (as well as the height of the items inside the StackPanel), I'm not sure how to do it (I'm new to C#/XAML). What would be the best composition for the template in this situation?
You can just specify it in an ItemTemplate and it will do what you want, something like;
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Border BorderBrush="Red" BorderThickness="2" Background="Blue"/>
<!-- Insert the rest of your Item template stuff here -->
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
ListBox is a type of ItemsControl, which exposes several properties to control the appearance of the items. In this case, have a look at ItemContainerStyle (in the case of ListBox, the item containers are instances of ListBoxItem). You could, for instance, set the Background property in an ItemsContainerStyle to some color.
I am trying to write a UserControl that will allow both single controls in it (like Label), as well as "layout" controls like StackPanel and friends.
I am having trouble doing that. The code I have works for single controls, but not for layout controls. I have a feeling this is an obvious fix, I am new to WPF. Here is the UserControl XAML:
<UserControl <!-- namespaces omitted for brevity -->>
<UserControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{TemplateBinding Content}" />
</DataTemplate>
</UserControl.ContentTemplate>
</UserControl>
When I try to use it like this:
<my:SpecialUserControl>
hello
</my:SpecialUserControl>
It's fine. But when I try to do something like
<my:SpecialUserControl>
<StackPanel>
<!-- stuff -->
</StackPanel>
</my:SpecialUserControl>
I get an error in Visual Studio Intellisense saying
The specified value cannot be assigned to the collection. The following type was expected: UIElement
And when I run the app (it builds), I get this exception at that place in the XAML:
'Add value to collection of type System.Windows.Controls.UIElementCollection threw an exception.' Line number x and line position y.
What can I do to make my UserControl able to accept any type of content?
The ContentPresenter should be a part of the ControlTemplate (<UserControl.Template>) of your control, not inside your ContentTemplate. I think that could be your problem.
Inherit your UserControl from ContentControl. ContentControl already has Content property combined with ContentPresenter inside ControlTemplate.
You can also use microsoft blend to get ContentControls visual tree.
I'm currently creating a WPF application, using C# and XAML in Visual Studios 2010.
I have a master grid. In that master grid I have a group bar which you can select different items. Depending on what you select, the middle of the master grid can be totally different. What I was wondering is, what's the best way to program the middle part?
Right now, I have it set up in such a way that everything in the middle is dynamically programed in C#, and everything on the outside is programmed in XAML.
In C# I programmed: for each group bar item, there is a grid that goes with it (so that different content can be displayed on it). Each grid is a child of the master grid. Each grid is visible or hidden when necessary. Is this the best way to approach this?
The best example of this is in Outlook 2007, where you have your group bar on the right hand side. When you select different items on the group bar (mail, calendar, tasks) the right of the group bar completely changes.
The easy way to do this in WPF is to define DataTemplates for each of your "middle" sections.
Using the Outlook example, you might have a MessageCollection class that stores a list of messages, an EventCollection class that stores a list of calendar events, and a TaskCollection class that stores a list of tasks.
In your "middle" area you would simply have a single ContentPresenter whose Content would be set to a MessageCollection, EventCollection, or TaskCollection. Presumably this would be done using a binding to a view model property.
Here is how it might look:
<Window ...>
<Grid>
<!-- group bar area -->
...
<!-- "middle" area -->
<ContentPresenter Grid.Row="1" Grid.Column="1"
Content="{Binding SelectedCollection}" />
</Grid>
</Window>
Now you create a DataTemplate for each of the collection types, for example:
<DataTemplate TargetType="{x:Type my:MessageCollection}">
<Grid>
... put the XAML for displaying mailbox contents here ...
</Grid>
</DataTemplate>
<DataTemplate TargetType="{x:Type my:EventsCollection}">
<Grid>
... put the XAML for displaying a calendar here ...
</Grid>
</DataTemplate>
<DataTemplate TargetType="{x:Type my:TasksCollection}">
<Grid>
... put the XAML for displaying a to-do list here ...
</Grid>
</DataTemplate>
With this setup, all you have to do to switch the inner grid is to set your "SelectedCollection" property in your view model to a different collection type.