WPF negative Margin working different than expected (in ItemsControl) - wpf

I'm trying to make a calendar view in WPF, because the basic <Calendar /> control isn't flexible enough, as I need a day-view to display meetings for that day. So I thought to myself, that I could just use an <ItemsControl> control and place the various meetings by applying some vertical Margin.
Before actually coding it, I wanted to test my idea and hardcode a sample. This is the result:
<ItemsControl>
<Label Content="[1] 9:30 - 10:00" Background="Fuchsia" Height="60" Margin="0 0 100 0"/>
<Label Content="[2] 9:45 - 10:45" Background="Aqua" Height="120" Margin="100 -30 0 0"/>
<Label Content="[3] 10:15 - 10:30" Background="Bisque" Height="30" Margin="0 -60 100 0"/>
<Label Content="[4] 10:30 - 10:45" Background="Coral" Height="30" Margin="0 0 100 0"/>
</ItemsControl>
The result turned out quite good, although there are two things that surprised me:
I would've assumed, that [4] would be placed directly below [3], since it has a margin-top of 0, but apparently this isn't based on the previous element, but rather the bottom most element.
It's not very easy to see: [3] is placed lower, than I had planned. Every minute is represented by 2 units, so when [3] starts 30 minutes before [2] ends, I would assume, that a margin-top of -60 would result in the correct placement. With this margin, the blank space between [3] and [4] should be big enough to perfectly fit [4] in there, but it isn't. Instead, applying a top-margin of -90 results in the wanted placement of [3].
<ItemsControl>
<Label Content="[1] 9:30 - 10:00" Background="Fuchsia" Height="60" Margin="0 0 100 0"/>
<Label Content="[2] 9:45 - 10:45" Background="Aqua" Height="120" Margin="100 -30 0 0"/>
<Label Content="[3] 10:15 - 10:30" Background="Bisque" Height="30" Margin="0 -90 100 0"/>
<Label Content="[4] 10:30 - 10:45" Background="Coral" Height="30" Margin="0 0 100 0"/>
</ItemsControl>
I have the feeling, that as soon as lower edge of the element (in this case [3]) is higher than the lower edge of the previous element (in this case [4]), a negative margin-top places it according to it's vertical center line instead of the top line. But I'm struggling to figure out an equation to come up with the correct negative margin.
Does somebody have an explanation and maybe a hint towards how to correctly calculate this?
Thanks in advance
Julius

Related

Inline Run objects not completely highlighted when a hyphen is present

I've got this really weird problem where I'm doing highlighting on certain parts of text on a TextBlock object containing multiple Run objects.
<TextBlock Name="InlineTextBlock" Background="White">
<Run Foreground="White"
Background="Blue"
FontSize="75"
FontFamily="Helvetica">17-Oct-13</Run>
<Run Foreground="White"
Background="Blue"
FontSize="75"
FontFamily="Helvetica">17/Oct/13</Run>
</TextBlock>
It's pretty straightforward code to read, and from reading, you would expect both 17-Oct-13 and 17/Oct/13 to be completely highlighted with a Blue Background and White Foreground.
The odd thing is, the hyphen character has 1 pixel on the left and the right where the Background color is not being applied.
There are no issues with highlighting for the second Run object with 17/Oct/13.
Would appreciate any help to figure out this odd problem.
EDIT:
Running this on my computer displays the following (you'll have to look really closely to see it):
Also, I noticed this only occurs on certain fonts like Helvetica, Arial, Consolas (to name a few). Verdana seems to display fine.
Unfortunately, running your code on my computer does not suffer from this problem:
I'm guessing that you have something else causing your problem.
For your information, I'm running Visual Studio 2010 on Windows 7 and .NET 4.0.
UPDATE >>>
Thanks for the update... I can now confirm that I see your mysterious vertical white lines around the hyphens using the Helvetica font. I can't imagine what is causing it, but assuming that you can't set the main TextBlock.Background to Blue, you can still fix this issue by using an inner TextBlock with its Background set to Blue:
<TextBlock Name="InlineTextBlock" Background="White">
<TextBlock Background="Blue">
<Run Foreground="White"
Background="Blue"
FontSize="50"
FontFamily="Helvetica">17-Oct-13</Run>
<Run Foreground="White"
Background="Blue"
FontSize="50"
FontFamily="Helvetica">17/Oct/13</Run>
</TextBlock>
</TextBlock>

Silverlight stopped displaying text after certain number of characters

So I dont really touch or have touched silverlight programming but my company has this application built in it and I have a bug they want me to fix. The issue is my text box can hold up to about 778 characters or 16 rows but then you cant view anymore of what you wrote. If you copy all you get everything you wrote even though you might only see 3/4 of it. I am not sure what I need to do, if I need to create a control panel thing for over flow or set a max length, etc. What would be my best step for this? Any good ideas?
Code:
<TextBox HorizontalAlignment="Left" Name="txtDesc" Width="300" Height="80" Grid.Column="1" Grid.Row="0" TextWrapping="Wrap" Text="{Binding Path=ExcursionDescription, Mode=TwoWay, Converter={StaticResource StripNonAsciilCharacters1}}" VerticalScrollBarVisibility="Auto" Margin="0,0,5,0"/>

Grid inside a StackPanel: why do auto and * behave strangely?

My google and stackoverflow search-fu have failed me, so I present to the community this question.
(This is all generated using VS2010 and .NET 4.0, in a blank default WPF Solution)
Consider the following XAML:
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Name="aborder" Grid.Column="0" Grid.ColumnSpan="2"
Background="Red" Width="200"/>
<Border Name="aborder2" Background="Green"/>
</Grid>
</StackPanel>
What would you predict the width of "aborder2" to be?
If you guessed "20 pixels", you would be wrong. The correct answer is 110 pixels.
Consider this XAML:
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Name="aborder" Grid.Column="0" Grid.ColumnSpan="2"
Background="Red" Width="200"/>
<Border Name="aborder2" Background="Green"/>
</Grid>
</StackPanel>
What would you predict the width of "aborder2" to be?
If you guessed either 20 pixels or 110 pixels, you would be wrong. The correct answer is 200 pixels.
I cannot figure this out and it's driving me insane. It seems like the answer should be obvious; clearly there's some interaction between an auto-filling grid column and the stackpanel that causes the grid to freak out. But it just doesn't seem to make sense - whatever rules are governing this behavior seem to be arbitrary. Why 110 pixels? Why not 109 pixels or 100 pixels? I would understand if the auto-sized column failed to expand fully or something, but to have the fixed-width column randomly ignore its width has left me a burnt out shell of a developer.
Any help or guiding lights would be much appreciated!
I have no idea why the first example isn't rendering correctly
The 2nd is because Auto means "the same size as the contents", but you have nothing in Column2 so Column2 is getting rendered at 0px. You have something in Column1 which spans 2 cells, but since Column2 is rendered at 0 px it means Column1 is stretched to 200 px. By default, Grid's expand their children to fill all available space in the Cell, so this is making aborder2 stretch to 200px instead of 20.
I think the first example might be a similar situation, where Column2 is rendering at 0px because it has no content, however I am not sure why it is setting aborder2 to a width of 110. The 110 seems to come from (GridWidth / TotalColumns) + (1stColumnWidth / TotalColumns * NumberOfStarColumns), so I think it's a bug.
As a side note, you can set a Column1's MaxWidth="20" to force Column1 to always render as 20px
No answer for you, but it seems to happen in Silverlight too. I'd assume there's some bug in the arrange and measure passes. If you put a custom control in there instead of a border and override the measure and arrange methods you would probably get a better picture of what's going on.
To try to solve the mystery of where 110 comes from, I'm guessing (200 - 20) / 2 + 20
EDIT: I tried a few other formulas and it didn't hold up, looks more like:
(200 + 20) / 2

What is the Data value of plus sign in Path

How to draw plus and minus signs in using data property in Path object.
This is my triangle path object. I need to change it to plus symbol.
<Path x:Name="trianglePath" Data="M 0 8 H 12 V 15 Z"/>
Here, I've created a plus and minus sign which is 10 x 10 pixels using XAML path markup syntax:
<Path Margin="10" Stroke="White" Data="M0,5 H10 M5,5 V10Z" StrokeThickness="2" Height="10" Width="10" />
<Path Margin="10" Stroke="White" Data="M0,5 H10" StrokeThickness="2" Height="10" Width="10" />
When experimenting with designing your path drawing, it's helpful to set the path element's height & width first. To better understand XAML path markup syntax, see MSDN.
You might find it easier if you draw them out on a piece of graph paper and then label the vertices with the values needed to reach it from the previous one.
Then copy this into your code.

Irregular layout ItemsControl

I have a strange layout for an ItemsControl.
I have a 4x6 grid with the following pattern:
1 2 3 4
13 14 15 16
5 6 7 8
17 18 19 20
9 10 11 12
21 22 23 24
Is there an easy way to do this? should I be using 6 Items Controls and take "sections" of my list? is there a good way to do this? What about notification?
It's important to note that I may, or may not, have all 24 entries present, but the layout needs to be maintained (think of it like filled slots on a bingo card or something)
Edit:
Ideally, I'd like to be able to take a list, and do some fun sorting/padding type stuff off properties on the items in the list.
for instance, if I have an ObservableCollection with a few units, and Unit has a property "Index", I'd like to have a view consumable Collection generated that automatically uses Index to make a padded list. I guess an observable dictionary could work, but that seems gross. Maybe a new custom layout panel is in order?
There is a clever way of doing this in pure XAML using a custom template for your ItemsControl. It's easiest if all your "cards" have a fixed size, say 100x100:
<!-- Wrap each card in a decorator twice as high as the card cell -->
<DataTemplate x:Key="ItemInDoubleHighBox">
<Decorator Width="100" Height="200">
<Decorator Width="100" Height="100" ClipToBounds="True">
<ContentPresenter />
</Decorator>
</Decorator>
</DataTemplate>
<!-- Define a template for use with WrapPanel -->
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
<WrapPanel />
</ItemsPanelTemplate>
<!-- Now the actual ItemsControl template -->
<ControlTemplate TargetType="ItemsControl">
<Grid Width="600" Height="600" ClipToBounds="True">
<!-- Items 1 to 12 -->
<ItemsControl ItemsSource="{TemplateBinding ItemsSource}"
ItemsPanel="{StaticResource WrapPanelTemplate}"
ItemTemplate="{StaticResource ItemInDoubleHighBox}" />
<!-- Items 13 to 24 -->
<ItemsControl ItemsSource="{TemplateBinding ItemsSource}"
ItemsPanel="{StaticResource WrapPanelTemplate}"
ItemTemplate="{StaticResource ItemInDoubleHighBox}"
RenderTransform="1 0 0 1 0 -500" />
</Grid>
</ControlTemplate>
How it works: The DataTemplate causes the items to be "double-spaced" with only 1-12 visible, and the RenderTransform on the second ItemsControl makes items 13-24, which are also "double-spaced" appear in the spaces between the first rows of items.
Note: You can make the height and width data-bindable, but it takes more XAML. Just add ScaleTransforms everywhere "200", "500" or "600" appears in the XAML. For example, to deal with the "200" you can set a scale transform on the inner decorator with ScaleY="0.5" and on each ItemsControl with ScaleY="2". Now the outer decorator's height will be 100, which can be data-bound. The other constants can be dealt with via similar pre- and post- scaling of the content. And because WPF combines all the transforms before rendering anyway, the extra transforms will cost basically nothing.
WPF makes this pretty trivial. Basically you just need to specify an ItemsPanelTemplate.
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
Now whatever items you add to the ListBox will be arranged according to the layout logic of the panel which in this case is a UniformGrid.
Note that you'll still need to keep the items in your collection in the order that you want them to appear. So I would sort them out first before adding them to the ListBox. If you need to create "holes" in the collection then I would use some type of placeholder object (maybe new object() will do) instead of trying to use complex layout logic to spread the items.

Resources