WPF: How to implement custom Grid with CellSpacing? - wpf

I miss the HTML-cellspacing in WPF. Im trying to implement something similar in WPF's Grid by making a custom Grid-class overriding from Grid and then modifying MeasureOverride and ArrangeOverride to get the behaviour I want. Which is that each cell in a grid should have a fixed spacing (not padding) to each other cell. How could this be done?

I found this blog post about creating a custom grid panel that handles spacing between cells: http://daniel-albuschat.blogspot.dk/2011/07/gridlayout-for-wpf-escape-margin-hell.html

You could probably write your own panel, or perhaps even a Grid descendant, that does your own layout with cell spacing. It would be a fair bit of work.
Here's what I usually do instead, to achieve the same thing. Suppose I want a cell spacing of 3 pixels. You can accomplish that by applying a 1.5 pixel margin to each cell (so the total space between a cell and its neighbor is 1.5 + 1.5 = 3 pixels), and then another 1.5 pixel margin around the entire Grid so the outer margin is correct (1.5 pixel margin around the cell + 1.5 margin around the Grid = 3 pixels). The XAML looks like this:
<Grid Margin="1.5">
...
<Label Grid.Row="0" Grid.Column="0" Margin="1.5">...</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="1.5">...</TextBox>
...
</Grid>
It's ugly but it works.
If most of the controls in your Grid are of the same type (e.g. if it's all Labels, or all Labels and TextBoxes), then you can use styles, instead of declaring and re-declaring the Margin on every element in the grid:
<Grid Margin="1.5">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="Margin" Value="1.5"/>
</Style>
<Style TargetType="TextBox">
<Setter Property="Margin" Value="1.5"/>
</Style>
</Grid.Resources>
...
<Label Grid.Row="0" Grid.Column="0">...</Label>
<TextBox Grid.Row="0" Grid.Column="1">...</TextBox>
...
</Grid>
Actually, I've found in practice that I often want uneven margins -- for example, I may want a 3-pixel margin around the top, left, and right, but no margin on the bottom (because the controls below it already have a margin of their own). So I usually don't end up using 1.5 pixels all around; I usually end up with something more complex. So I can see why they didn't add a CellSpacing; it would make the simple cases simpler, but would be useless in more complicated layouts.
But half-margins-all-around is a quick way to achieve CellSpacing, and then you can tweak the margins if you need something fancier.

You may interested with my answer here, supports uniform border and cellspacing for each child in a custom grid:
How could I put a border on my grid control in WPF?

You're looking for the Margin property. You can style the DataGridCell class.
<Style TargetType="{x:Type toolkit:DataGridCell}">
<Setter Property="Margin" Value="3" />
</Style>

Related

TextBlock internal margin

I suppose when several TextBlocks are located in a row(i.e in StackPanel with the Horizontal orientation) they should look as if it was one TextBlock. But I found the following:
The first line is a TextBlock displaying "eeee". The second one is a StackPanel containing 4 TextBlocks and each TextBlock contains "e". As you can see from the picture above, the second line is a little bit longer than the first one. It looks like the TextBlocks have some internal margin. But, in my case, I'd like to prevent it.
So, do you know how to do it?
Try setting the Padding and Margin of the TextBlock to zero.
<TextBlock Padding="0" Margin="0" ... />
Since you have multiple textblocks that you are going to want to do this with, you should probable use a Style to do this.
<StackPanel Orientation="Horizontal">
<Style TargetType="TextBlock">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
</Style>
...
</StackPanel>
If this doesn't help, then you could start using negative margins:
Margin="-1,0,0,0"
However this introduces brittleness into your code, because the spacing between text depends on the font size and the text displayed. For example, if you change the font size to 13, you will no longer have this problem with five lower case 'e'. Changing the text to uppercase 'E' will result in the same problem, though.
You could try using the clipping properties on the TextBlock or even the rendering affects, but this seems like a lot of work just to get text to adjust a couple of pixels. May I ask why you are trying to get the two text samples to look the same?
Update:
I am doubtful that you can find a good way to make this work the way that you want to. May you should look into using a RichTextBox or creating a custom control.
Here is a good example use of a RichTextBox with text highlight (although it is foreground highlighting).

WPF Datagrid zoom issue

I'm working with the WPF DataGrid from the WPFToolkit and I'm having issues with trying to zoom the entire datagrid. My initial thought was that it was going to be really easy and I would just apply a scale transform to the grid and animate the ScaleX, ScaleY properties when the used clicked a button. This did not work however because the scrollbar was zoomed in making it larger. I need fixed headers and fixed columns on the datagrid so I can't simple use a scrollviewer outside of the datagrid to handle the scrolling. The second thing I tried was to just scale the font size in the grid, but this failed because on shrinking the font size the columns stay at the original width and do not shrink.
Finally I thought I had it working by using the below code which goes into the view tree for the datagrid and adds a scale transform to the Scroll Content Presenter. (Also not shown in this code, I apply a transform to the visual tree item for the headers in the same manner so that it scales as well). I thought this was working great until I tested out the horizontal scrolling after zooming. (Vertical scrolling works perfectly.) Before zooming at all the horizontal scrolling is fine, but after zooming, when I scroll horzonitally the display freaks out. It is hard to tell exactly what it is doing, but it sort of looks like the content that is scrolling off the left of the screen is "folding over" and coming back in on the left. Maybe it is just all smashing up on the left side. Does anyone have any ideas how I can get this working, hopefully without throwing out my whole datagrid that is already working quite well otherwise.
ScrollContentPresenter sp = (ScrollContentPresenter)
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(dgMatrix, 0), 0), 0),2);
ScaleTransform st = new ScaleTransform(1, 1);
sp.LayoutTransform = st;
DoubleAnimation a = new DoubleAnimation();
a.By = 1.5;
a.AutoReverse = false;
a.Duration = new Duration(TimeSpan.Parse("0:0:0.25"));
st.BeginAnimation(ScaleTransform.ScaleXProperty, a);
st.BeginAnimation(ScaleTransform.ScaleYProperty, a);
I figured out a solution. Don't know if this is the same way you did it or not:
<toolkit:DataGrid.CellStyle>
<Style TargetType="{x:Type toolkit:DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type toolkit:DataGridCell}">
<ContentPresenter>
<ContentPresenter.LayoutTransform>
<ScaleTransform ScaleX="{Binding Path=Value, ElementName=ZoomFactor}"
ScaleY="{Binding Path=Value, ElementName=ZoomFactor}" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</toolkit:DataGrid.CellStyle>
Where ZoomFactor is a slider:
<Slider x:Name="ZoomFactor"
Value="1"
Minimum=".25"
Maximum="5"
Width="180" Margin="0,0,5,0"/>

How do I make an item in a toolbar fill all available space in WPF

I'm trying to make an item on ToolBar (specifically a Label, TextBlock, or a TextBox) That will fill all available horizontal space. I've gotten the ToolBar itself to stretch out by taking it out of its ToolBarTray, but I can't figure out how to make items stretch.
I tried setting Width to Percenatage or Star values, but it doesn't accept that. Setting Horizontal(Content)Alignment to Stretch in various places seems to have no effect either.
Unfortunately it looks like the default ControlTemplate for ToolBar doesn't use an ItemsPresenter, it uses a ToolBarPanel, so setting ToolBar.ItemsPanel won't have any effect.
ToolBarPanel inherits from StackPanel. By default its Orientation is bound to the parent ToolBar.Orientation, but you can override this and set its Orientation to Vertical with a Style and this will allow items to stretch horizontally:
<DockPanel>
<ToolBar DockPanel.Dock="Top">
<ToolBar.Resources>
<Style TargetType="{x:Type ToolBarPanel}">
<Setter Property="Orientation" Value="Vertical"/>
</Style>
</ToolBar.Resources>
<ComboBox HorizontalAlignment="Stretch" SelectedIndex="0">
<ComboBoxItem>A B C</ComboBoxItem>
<ComboBoxItem>1 2 3</ComboBoxItem>
<ComboBoxItem>Do Re Mi</ComboBoxItem>
</ComboBox>
</ToolBar>
<Border Margin="10" BorderBrush="Yellow" BorderThickness="3"/>
</DockPanel>
You can then use a Grid or something in place of the ComboBox above if you want multiple items on a line.
Try putting a horizontal StackPanel in the ToolBar and then the element you want inside of that StackPanel.
Have you tried wrapping your item in a Grid, not in a StackPanel?
You need a special custom panel like auto stretched stack panel, and replace the toolbarpanel. Check this out you can find one panel there
http://blendables.com/products/productsLayoutmix.aspx
I've had this same problem for a while, and there is very little help available online.
Since it doesn't sound like you need all the extra functionality of a Toolbar (collapsible/movable trays), why not just use a Top-docked Grid, and tweak the background a little to make it look like a standard toolbar?

Scaling/resizing the diameter of all bubbles on a bubble chart (in WPF or Silverlight)

I have a bubble chart in a WPF application with LOTS of points in a BubbleSeries. The automatically drawn sizes of the bubbles result in so much overlap of the plotted bubbles, that most of the bubble points are obscured. The drawn bubble size does not change if I alter the data reduce the SizeValues of all the plotted points (some sort of hidden logic seems to be determining how to automatically scale the SizeValues when drawing the bubbles).
How can I reduce the diameter of every bubble by 75% (so each bubble's diameter is one fourth the normal automatic size)?
Thanks, Alan
[I am working with the charting/data visulaization controls in the June 2009 WPF control toolkit, but I think the same question and answer probably applies to Silverlight 3 bubble charts.]
Creating the Style:
<Style x:Key="BubbleDataPointStyle" TargetType="chartingToolkit:BubbleDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:BubbleDataPoint">
<Grid RenderTransformOrigin=".5,.5">
<Grid.RenderTransform>
<ScaleTransform ScaleX=".25" ScaleY=".25" />
</Grid.RenderTransform>
<controlsToolkit:Viewbox x:Name="viewbox">
<Ellipse Width="1" Height="1" />
</controlsToolkit:Viewbox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Add the Style to your data points:
<chartingToolkit:Chart>
<chartingToolkit:Chart.Series>
<chartingToolkit:BubbleSeries
ItemsSource="{Binding ObjectCollection}"
IndependentValuePath="AxisX"
DependentValuePath="AxisY"
SizeValuePath="Size"
DataPointStyle="{StaticResource BubbleDataPointStyle}" />
</chartingToolkit:Chart.Series>
</chartingToolkit:Chart>
If I understand the question correctly, you want to be able to scale your content without actually having to redraw it, correct? If so, you should have a look at the Viewbox class.
Not sure if there's something like this in WPF toolkit charts but in amCharts for WPF there are MinBulletSize/MaxBulletSize properties to control scaling of the bubbles. I think there must be something along these lines in WPF/Silverlight toolkit charts too.

WPF Style Trigger

I change the FontSize of Text in a Style trigger, this causes the Control containing the text to resize as well. How can I change the Fontsize without affecting the parent's size?
A nice trick to isolate an element from its parent layout wise is to place the element in a Canvas
In the markup below there are two copies of your element
The first is hidden and establishes the size of your control
The second is visible but wrapped in a Canvas so its layout size does not affect the parent.
<Parent>
<Grid>
<Element Visibility="Hidden"/>
<Canvas>
<Element />
</Canvas>
<Grid>
</Parent>
You can increase the Padding at the same time you decrease the FontSize - this will cause the calculated height of the Button to remain the same:
<StackPanel>
<Button Content="ABC">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="20"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Padding" Value="5"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Margin="0,20" Content="123" FontSize="20"/>
<Button Content="Do Re Mi" FontSize="20"/>
</StackPanel>
You can do the reverse and set a negative Padding if the FontSize is increasing, as well.
You could also use a binding from FontSize to Padding to accomplish the same thing in a general way, but if you're only dealing with a fixed set of FontSizes it would be easier to just hardcode it as above.
There is absolutely NO need for hard-coded widths, crazy measure overrides, tricky bindings, or anything of that sort.
The solution is actually incredibly simple. Instead of changing the font size in a style trigger, create a simple control template for your button with a RenderTransform applied to the content presenter element. Add a ScaleTransform to the RenderTransform. Inside a IsPressed trigger definition set the vertical and horizontal scales on the ScaleTransform to a smaller ratio, say 0.8.
Using a RenderTransform will keep the layout of the pressed button the same with, so it won't influence the position of the other elements. By contrast, using a LayoutTransform would have actually caused the button container to shrink and the parent container's ArrangeOverride method would cause the adjacent buttons to move to fill the extra space.
I'm really busy right now so I'll leave the actual implementation up to you! ;-)
http://msdn.microsoft.com/en-us/library/system.windows.media.scaletransform.aspx
I am creating a ControlTemplate for a ButtonControl so it looks like a label (flat text, no borders) with triggers for IsKeyboardFocused, IsPressed, IsDefaulted etc.
The IsPressed is defined to drop the FontSize (from default of 30) down to 28. To give a pressed animation effect.
One use of these Buttons is a horizontal StackPanel of Button, separated by vertical separators. When the IsPressed trigger is fired on a button and it is resized, the entire row of buttons gets re adjusted, which is not a pleasing visual effect.
My preference is for a template based solution, to avoid introducing new controls in order to provide overrides. The only problem with the hard coded size approach is internationalisation, other languages will increase the orginal size.
The solution I am going with is to set the minWidth in C# after the button's DesiredSize has been calculated. Note that Width is NaN even after the Button is rendered hence the use/existence of DesiredSize. Later I will try and XAMLize the C#.
What kind of control are you using? If this is a HeaderedControl like a GroupBox or TabItem then you need to specifically set the HeaderTemplate like this:
<DataTemplate x:Key="MyHeaderTemplate">
<TextBlock Text="{Binding}" Fontsize="14" FontWeight="Bold" />
</DataTemplate>
I can think of a couple of things you could try:
You can override the Measure Pass of the control - when a control is rendered in WPF it undergoes two passes. The first is a 'measure pass', where the control comes up with what sizes that it wants to be. The second is the 'arrange pass', where it actually lays out the control. WPF provides a method called MeasureOverride. If you override this method you can provide custom behavior that can be used to adjust the size of the control.
Note - I believe that you will have to call the Measure method all of your controls children during this override in order to get your control to lay out properly.
Hard code the height and width on the control - this will override the control's DesiredSize with your values. While generally a not the greatest of ideas, it will work.

Resources