Creating grid of hexagons - wpf

I have to do a "grid" like this:
Harmonic table
I'm trying to create a ListView with ItemsSource="List<Note>" where every odd note in the list is moved on the bottom...
Is the ListView the right control?
How can I draw an exact hexagon with faces that is near next object?
EDIT: hexagon drawing is solved... this is the xaml:
<Path d:LayoutOverrides="None"
d:LastTangent="0,0" Stroke="Blue" Fill="Red"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0" Width="100" Height="100" x:Name="Path"
Stretch="Fill"
Data="M8.660254,0 L17.320508,5 17.320508,15 8.660254,20 0,15 0,5 8.660254,0 z"/>

The container for your notes would be an ItemsControl or a ListBox if you need to select items. Then you give your items a template using ListBox.ItemTemplate where you include your hexagon drawing. You have a nice tutorial on Custom ListBox layout.
At this point, your hexagons are displayed one below the other as a ListBox does by default. To get your special layout, you have to change the ListBox.ItemPanel. Here you have two possibilities:
either you use the Canvas panel that supports absolute positioning. In this case your items must have X and Y properties that you will use to position them.
or you create a custom Panel, probably based on Canvas, that is able to convert your custom coordinate system (for example note name + octave number) into X and Y. A bit more difficult but much more reusable. An example of Custom Panel on CodeProject.

HexGrid: CodeProject article
HexGrid: GitHub repository
The key component of a possible solution is a WPF Panel which can arrange hexagonal elements (Standard Panels operate with rectangular child elements). Take a look my HexGrid project (too large to post here). The cental part of it is a HexGrid (WPF Panel which arranges child elements in a honeycomb pattern). Child elements are represented by HexItems (hexagon-shaped ContentControls). There is also HexList (selector ItemsControl which displays items in HexItem container on a HexGrid panel) which gives hex selection support out-of-box.
example of usage:
<hx:HexList Name="HexColors" Orientation="Vertical"
Grid.Row="1"
Padding="10"
SelectedIndex="0"
Background="{Binding Path=SelectedItem.Background, RelativeSource={RelativeSource Self}}"
RowCount="5" ColumnCount="5">
<hx:HexItem Grid.Row="0" Grid.Column="1" Background="#006699"/>
<hx:HexItem Grid.Row="0" Grid.Column="2" Background="#0033CC"/>
<hx:HexItem Grid.Row="0" Grid.Column="3" Background="#3333FF"/>
<!--...-->
<hx:HexItem Grid.Row="4" Grid.Column="1" Background="#CC9900"/>
<hx:HexItem Grid.Row="4" Grid.Column="2" Background="#FF3300"/>
<hx:HexItem Grid.Row="4" Grid.Column="3" Background="#CC0000"/>
</hx:HexList>

Related

WPF - which panel type to use for such a layout?

in the image below you see two orange rectangles. They represent the main containers. Currently I am using Wrap-Panels for them. Their content (Grids) is represented by the purple rectangles.
The top orange rectangles (Wrap Panel) is what I currently have.
The bottom orange rectangle (WrapPanel) represents what I would like.
The WrapPanel seems to place only one item per column per row.
Is there any way to have the Wrap Panel stack multiple items within a column?
The Purple controls (Grids) are generated at runtime. There can be a lot of them.
With the current layout there is a lot of wasted space.
I am looking to arrange the Grids in the most compact way possible. This can be thought of as a 2d rectangle placement optimization problem (Nesting).
Are there any Containers (can be third party) that provide such functionality?
Thank you!
Set the Orientation of the WrapPanel to Vertical:
<WrapPanel Orientation="Vertical" Height="100" Width="300" Background="Orange">
<Rectangle Margin="5" Width="80" Height="60" Fill="Purple"/>
<Rectangle Margin="5" Width="80" Height="50" Fill="Purple"/>
<Rectangle Margin="5" Width="80" Height="40" Fill="Purple"/>
<Rectangle Margin="5" Width="80" Height="30" Fill="Purple"/>
</WrapPanel>

Having issues with the scroll bar in WPF

So im trying to get my scroll bar to A only show up as needed and B show up only around my description text
Right now the scroll view is going from the top of the window to the bottom
<Window x:Class="WpfApplication3.DataWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataWindow" Height="300" Width="300">
<Grid>
<Label x:Name="lblTitle" Content="Label" HorizontalAlignment="Left" Margin="96,25,0,0" VerticalAlignment="Top" Width="186"/>
<Label x:Name="lblPublishDate" Content="Label" HorizontalAlignment="Left" Margin="96,53,0,0" VerticalAlignment="Top" Width="186"/>
<Image x:Name="imgPic" HorizontalAlignment="Left" Height="81" Margin="10,10,0,0" VerticalAlignment="Top" Width="81"/>
<ScrollViewer>
<TextBlock x:Name="tbDesc" HorizontalAlignment="Left" Margin="10,96,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="167" Width="272" Text="TextBlock" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</ScrollViewer>
</Grid>
A grid tries to let it's children take up all availble space.
Your ScrollViewer is one of the children, so it will fill all available space by default.
There are a number of ways around this.
You could use a different panel type, one that doesn't try to stretch it's children to fill all available space. Based on what you're doing with excessively large margins, a Canvas might be suitable.
I would suggest reading this for a quick understanding of WPF's available Layout Panels : WPF Layouts - A Visual Quick Start
Another alternative is to give your Grid some Row Definitions, and specify that the row containing the ScrollViewer should be of a fixed size, or should be sized so it fits whatever size the child object wants (Height="Auto")
Or you could give your ScrollViewer a fixed height, and set it's VerticalAlignment property so it gets docked to either the top or bottom of the Grid.
Personally I would recommend the first option - reviewing WPF's layout system and determining a more approrpiate panel type for your layout. And if the most appropriate panel type is a Grid, then I would highly recommend using the RowDefinitions and ColumnDefinitions to give your Grid some structure, rather than trying to use excessively large Margins to position your controls.
You're pretty close, the problem appears to be an issue of layout. Because the controls are arranged in the grid without row and column definitions the scrollviewer is attempting to resize to the full size of the grid while the textblock is adhereing to its fixed size and margin. Try the following starting point and see if it helps:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="150"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Label x:Name="lblTitle" Content="Label" HorizontalAlignment="Left" Width="186"/>
<Label x:Name="lblPublishDate" Content="Label" HorizontalAlignment="Left" Width="186"/>
<Image x:Name="imgPic" HorizontalAlignment="Left" Height="81" Width="81"/>
</StackPanel>
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="tbDesc" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock"/>
</ScrollViewer>
</Grid>

Make ScrollViewer scroll bound to the right

I am building a calculator application and I have a label to show the term and the result, but when the term gets too long it woudld dissappear. Because of that I implemented a scrollviewer control.
Now what I want to have is that the auto scroll of the scrollviewer is bound to the right, since my label has the content going right to left. See pictures for better explanation
What it is right now
What I want to have
This is my WPF Code for the relevant parts:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="10, 10, 10, 0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" Height="50" >
<Label x:Name="CalculatorTextBlockSolution" FontFamily="Cambria Math" HorizontalContentAlignment="Right" FontSize="24"/>
</ScrollViewer>
Add to the ScrollViewer:
FlowDirection="RightToLeft"
Update (see comments)
Add also to the Label:
FlowDirection="LeftToRight"

Setting width in a data template based on the size of a parent control

I am using a telerik TreeView in WPF, and I'm using a HierarchicalDataTemplate to show the nodes. Nodes represent matched items - which can be left only, right only, equal or inequal (a tree based comparison).
I am currently using a DataTemplateSelector to select from one of four templates, which all look similar to the following:
<HierarchicalDataTemplate x:Key="EqualTreeItemTemplate" ItemsSource="{Binding}">
<Grid Name="rowGrid" HorizontalAlignment="Stretch" Height="Auto" d:DataContext="{d:DesignInstance Carbon:ICarbonComparisonPair }">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Path Data="F1M574.042,314.611L533.8,344.398 522.251,328.798 515.235,333.988 526.786,349.593 526.782,349.596 531.978,356.603 579.235,321.622 574.042,314.611z" Stretch="Uniform" Fill="#FF000000" Width="16" Height="16" VerticalAlignment="Center" Margin="0,0,0,0" />
<TextBlock Grid.Column="1" Text="{Binding ObjectName}" Style="{StaticResource ObjectNameStyle}" Margin="4,0,0,0" />
<TextBlock Grid.Column="2" Text="{Binding ObjectName}" Style="{StaticResource ObjectNameStyle}" Margin="4,0,0,0" />
</Grid>
</HierarchicalDataTemplate>
The problem that I have is that the item content area is a different width based on the level of the tree that the item appears in. This means that the columns that I have don't line up - the text in the right hand column shifts to the right a bit for each level of the tree that you expand.
What I would like to do is specify the right hand grid column's width to be 50% of the size of the tree control as a whole, rather than 50% of the grid's natural area. I thought maybe I could do this with a binding with a RelativeSource, but I just can't seem to make it work. Is there a way to achieve this in XAML, or do I need to resort to code-behind?
If I'm understanding it correctly you want column index 2 to align across all items?
Check out the TreeListView control and see if that gives you what you need.
Silverlight demo here (just so you can see what it looks like - the WPF version is pretty much the same)
[Edit - More info]
The SharedGroupName property on ColumnDefinition is tempting but, thanks to the indent, it won't quite work - you'll end up with all of the content in column 1 or 2 being sized the same, but the pesky indent still throws it off. Check out ListView's View Property. I'm believe it's at least in the same spirit as what Telerik TreeListView is, if not darned similar in implementation.
Here's a decent writeup on how to use it. (Ironically I have that page bookmarked in a folder called "TreeGridList" so apparently at some point I had the idea to do that to accomplish something similar :) )

Best way to store multiple controls in an array

I have a UserControl in WPF with numerous child controls which I would like to index like an array. These child controls are in the same grid control as other child controls which I am not interested in.
I'd like to be able to index these controls in a way similar to:
someControl.Children[3];
With out having to avoid controls which I am not interested in. Here's a sample of what I have:
<Grid x:Name="gCalendar">
// more crap here...
<TextBlock Grid.Row="0" Grid.Column="7" TextBlock.TextAlignment="Center">Blah</TextBlock>
<Internal:DayCalendarTime Grid.Row="1" Grid.Column="0" />
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="1"/>
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="2"/>
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="3"/>
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="4" />
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="5"/>
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="6"/>
<Internal:DayCalendarCore Grid.Row="1" Grid.Column="7"/>
</Grid>
I would like to have an array consisting of just the Internal:DayTimeCore controls by putting some sort of wrapping control around them.
Is this possible, or will I manually have to make the array by looping through all children of the grid and adding the ones that are of the type I am interested in?
Another non-WPF way,
Create a code behind control collection by
foreach(DayCalendarTime control in gCalendar.Children)
{
controlCollection[i++] = control;
}
You can wrap these controls inside another Grid control

Resources