Scrolling ListBox, Wrapping TextBlock - wpf

I am attempting to replace a piece of an existing Winforms project using WPF. Let's call this piece a 'log viewer'. Each log entry consists of a header and some body text. The viewer should display these in one long scrollable list.
The original (Winforms) logviewer works well when the number of log entries, and their size, is small, but it suffers from some problems when displaying large numbers of long entries; the WPF virtualizingstackpanel solves these problems, but WPF, or my lack of experience with it, is adding some problems of its own.
If I build the logviewer window around a ListBox, it works perfectly except for the fact that scrolling is by item rather than smooth scrolling 'by pixel'. I gather this can be fixed by moving to .Net 4.5 but that's not a simple option.
Alternatively if I build it around a TreeView, it scrolls perfectly but the text does not wrap; instead it forms one long line per paragraph.
Here's the xaml for the treeview version; it borrows heavily from this SO question where the style seems to be used to correct the non-wrapping issue. It scrolls beautifully, but it still doesn't wrap.
<Window x:Class="zCasesheet.wCasesheet"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gl="clr-namespace:System.Globalization;assembly=mscorlib"
Title="MainWindow" Height="440" Width="674">
<Window.Resources>
<Style x:Key="MyTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Border Name="myBorder"
SnapsToDevicePixels="true"
CornerRadius="0,0,0,0"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="0"
BorderBrush="Transparent"
Height="Auto"
Margin="1,1,1,3"
Background="Transparent">
<ContentPresenter Grid.Column="1" x:Name="PART_Header" HorizontalAlignment="Stretch" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Name="grCasesheet" Background="#FFF6E7C9">
<Grid.RowDefinitions>
<RowDefinition Height="356*" />
<RowDefinition Height="45" />
</Grid.RowDefinitions>
<TreeView HorizontalAlignment="Stretch" Margin="2,2,2,0" VerticalAlignment="Top" Name="lstNarratives" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.CanContentScroll="True" >
<TreeView.ItemTemplate >
<DataTemplate >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Background="Honeydew" HorizontalAlignment="Stretch" BorderBrush="Black" Text="{Binding Path=HeaderText, StringFormat=d, ConverterCulture={x:Static gl:CultureInfo.CurrentCulture} }"/>
<TextBlock Grid.Row="1" TextWrapping="Wrap" HorizontalAlignment="Stretch" Text="{Binding Path=BodyText}" Margin="10,10,10,10"/>
</Grid>
</DataTemplate >
</TreeView.ItemTemplate >
</TreeView>
</Grid>
I've also tried xaml based on this SO answer, but I can't get that to work either - it still scrolls by item.
Any help much appreciated.

Related

Adjusting WPF chart toolkit ColumnSeries (System.Windows.Controls.DataVisualization.Charting)

I am using the WPF chart toolkit (System.Windows.Controls.DataVisualization.Charting). All stylings are in the XAML and I only bound the data to chart from my ViewModel. Everything looks alright for the first time I click on a button to show the columnseries. When I click on the button for the second time, the chart becomes bigger/corrupted; It shows only part of the graph.
The graph is drawn for the first time:
And button clicked for the second time:
The code I am using is as below:
<dvc:Chart Cursor="Cross"
Background="#FFFFFCF2"
Title="{Binding Title}"
Height="410"
Width="750"
VerticalAlignment="Top"
Name="ChartContainer">
<dvc:Chart.Series>
<dvc:ColumnSeries ItemsSource="{Binding ChartData,UpdateSourceTrigger=PropertyChanged}"
IndependentValueBinding="{Binding Path= Key}"
DependentValueBinding="{Binding Path= Value}"
Name="SummariesChart"
Margin="0,0,0,0"
IsManipulationEnabled="False">
<dvc:ColumnSeries.DataPointStyle>
<Style TargetType="dvc:ColumnDataPoint">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="dvc:ColumnDataPoint">
<Grid>
<Rectangle Fill="{TemplateBinding Background}"
Stroke="Black" />
<Grid Margin="0,0, 0, 0"
HorizontalAlignment="Center"
VerticalAlignment="Top">
<TextBlock Text="{TemplateBinding FormattedDependentValue}"
Margin="2" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</dvc:ColumnSeries.DataPointStyle>
</dvc:ColumnSeries >
</dvc:Chart.Series >
<dvc:Chart.Axes>
<dvc:LinearAxis Orientation="X"
Title="{Binding XTitle}"
Interval="1"
Location="Bottom"
ShowGridLines="True" />
<dvc:LinearAxis Orientation="Y"
Title="{Binding YTitle}"
ShowGridLines="True"
Location="Left" />
</dvc:Chart.Axes>
</dvc:Chart>
Also, to give you complete idea, I used a style on top of the page as follow:
<Style TargetType="dvc:Chart">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="dvc:Chart">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<dv:Title Content="{TemplateBinding Title}"
Style="{TemplateBinding TitleStyle}"
Margin="1" />
<Grid Grid.Row="1"
Margin="1,0,1,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<primitives:EdgePanel x:Name="ChartArea">
<Grid Canvas.ZIndex="-1"
Style="{TemplateBinding PlotAreaStyle}" />
<Border Canvas.ZIndex="10"
BorderBrush="#FF919191"
BorderThickness="1" />
</primitives:EdgePanel>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BarDataPointStyle"
TargetType="{x:Type dvc:BarSeries}">
<Setter Property="Background"
Value="Blue"></Setter>
<Setter Property="Opacity"
Value="0" />
</Style>
When I remove <dvc:Chart.Axes> block, it works correctly consistently. But I need X-axe and Y-axes descriptions existing in this code block. Do you know how I can tackle this problem? I appreciate your help.
I found a way to resolve the issue. But still, I am skeptical that something is wrong with the styling or something.
I added Minimum = 0 and Maximum in LinearAxis tag. I bounded Maximum to a variable in my ViewModel. I got the length of the array and added one to the maximum variable(MaxNumberInXAxes).
<dvc:LinearAxis Orientation="X"
Title="{Binding XTitle}" I
nterval="{Binding XAxisInterval}"
Location="Bottom"
ShowGridLines="True"
AllowDrop="False"
Minimum="0"
Maximum="{Binding MaxNumberInXAxes}"/>
The graph becomes something like this:

Layout issue with Telerik RadTileList control

While I've already submitted a support ticket to Telerik on this issue, I'm also posting the question here hoping someone may have a solution:
The RadTileList control is adding extra padding at the top and at the bottom of the tiles. To demonstrate this, I've created a simple WPF application that has three tiles. The app consists of a Telerik RadTileList control contained within a red border (to show where the control begins and ends), and the RadTileList contains three hardcoded Telerik Tiles. Two of them are the "Single" sized tiles, and the other one is a "Double". What I'm trying to do is get the tiles to align to the upper left-hand corner of the control.
The following is the xaml:
<Window x:Class="TelerikRadTileListIssue.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="MainWindow" Height="522" Width="559">
<Border BorderBrush="Red" BorderThickness="2">
<telerik:RadTileList >
<telerik:RadTileList.Items>
<telerik:Tile TileType="Single">
<TextBlock>Tile 1</TextBlock>
</telerik:Tile>
<telerik:Tile TileType="Double">
<TextBlock>Tile 2</TextBlock>
</telerik:Tile>
<telerik:Tile TileType="Single">
<TextBlock>Tile 3</TextBlock>
</telerik:Tile>
</telerik:RadTileList.Items>
</telerik:RadTileList>
</Border>
</Window>
I have found a better solution. I tried out the DevExpress WPF TileLayoutControl. With the exception of not allowing the user to select a tile, it does just about everything else the Telerik TileListControl does, and more. Most importantly, if I set the Padding property to "0", the tiles perfectly align to the upper-lefthand corner. I think I'm starting to become more of a DevExpress fan now.
I'm regretting giving up 100 points of my rep on StackoverFlow for a question that nobody seems to want to answer, but, hindsight is 20/20. If somebody finds a solution to the Telerik issue, I will be grateful.
Telerik responded to my question with a solution that [somewhat] works. Here is what they wrote:
You may try to reset the Padding of TileListPanel - by default we set this value to 30 pixels on the top for the grouping functionality of this component. Directly setting this value to 0 will reset the distance on the top, as shown below:
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<telerik:TileListPanel Padding="0" AllowDrop="True" Background="Transparent" CanHorizontallyScroll="True" CanVerticallyScroll="False" IsItemsHost="True" telerik:ScrollingSettingsBehavior.IsEnabled="True" telerik:ScrollingSettingsBehavior.ScrollStepTime="0:0:0.05" ScrollOwner="{x:Null}" telerik:ScrollingSettingsBehavior.ScrollStep="24" telerik:ScrollingSettingsBehavior.ScrollAreaPadding="200"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
Also, I am pasting you the full template with this modification for RadTileList:
<Style x:Key="RadTileListStyle1" TargetType="{x:Type telerik:RadTileList}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type telerik:RadTileList}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="PART_PanoramaBackground" ClipToBounds="True" Content="{TemplateBinding PanoramaBackground}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
<ScrollViewer x:Name="PART_ScrollViewer" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CanContentScroll="True" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" Margin="0" Padding="{TemplateBinding Padding}" VerticalScrollBarVisibility="Hidden">
<telerik:StyleManager.Theme>
<telerik:Office_BlackTheme/>
</telerik:StyleManager.Theme>
<ItemsPresenter/>
</ScrollViewer>
<ContentPresenter x:Name="dragVisualPlaceholder" HorizontalAlignment="Stretch" Visibility="Collapsed" VerticalAlignment="Stretch"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<telerik:TileListPanel Padding="0" AllowDrop="True" Background="Transparent" CanHorizontallyScroll="True" CanVerticallyScroll="False" IsItemsHost="True" telerik:ScrollingSettingsBehavior.IsEnabled="True" telerik:ScrollingSettingsBehavior.ScrollStepTime="0:0:0.05" ScrollOwner="{x:Null}" telerik:ScrollingSettingsBehavior.ScrollStep="24" telerik:ScrollingSettingsBehavior.ScrollAreaPadding="200"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="GroupTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="SnapsToDevicePixels" Value="True"/>
</Style>
*Please give it a try and let us know how we can assist you further.
Regards,
Vanya Pavlova
Telerik*
Here are the results:
When I first launch the app:
If I resize it ever so slightly:
If I resize it a significant amount:
As shown in the image above, the tiles don't consistently stay positioned to the upper-left. For now, I will incorporate this code into my app, but I'm going to follow up again with Telerik and see if there is another layer in this solution.
I tried playing around with the layout templates to no avail,
then I set the height of the RadTileList. It looks like the ItemsPanelTemplate:TileListPanel behaves like a WrapPanel. Stretching itself until MaxHeight.
You will also need to set a different height if you add/remove or change the TileType of the tiles.
Pic is here: LayoutIssue.jpg
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="MainWindow" >
<Window.Resources>
<DataTemplate x:Key="DeptMapsTileGroupTemplate">
<TextBlock Margin="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
FontFamily="Segoe UI"
FontSize="28"
Foreground="Black"
Text="{Binding Header}" />
</DataTemplate>
<telerik:TileGroup x:Key="tilegroup"
DisplayIndex="0"
Header="Group" />
</Window.Resources>
<Grid VerticalAlignment="Top">
<telerik:RadTileList GroupTemplate="{StaticResource DeptMapsTileGroupTemplate}" Height="400">
<telerik:RadTileList.Items>
<telerik:Tile TileType="Single" Group="{StaticResource tilegroup}">
<TextBlock>Tile 1</TextBlock>
</telerik:Tile>
<telerik:Tile TileType="Double" Group="{StaticResource tilegroup}">
<TextBlock>Tile 2</TextBlock>
</telerik:Tile>
<telerik:Tile TileType="Single" Group="{StaticResource tilegroup}">
<TextBlock>Tile 3</TextBlock>
</telerik:Tile>
</telerik:RadTileList.Items>
</telerik:RadTileList>
</Grid>
Create a DataTemplate for your RadTileList and in that template set VerticalAlignment property to Top...
<DataTemplate x:Key="GroupTemplate">
<TextBlock Text="{Binding Header}"
FontSize="20"
FontFamily="Segoe UI"
Margin="10"
VerticalAlignment="Top"
HorizontalAlignment="Left"/>
</DataTemplate>
<telerik:RadTileList GroupTemplate="{StaticResource GroupTemplate}">

Can the WPF Datagrid control be made to behave like the WinForms DataGridControl in regard to column layout?

Basically, I'd like to make the WPF DataGrid control layout its columns exactly the way the WinForms DataGridView does
And more specifically, here is the behaviour I'm looking for:
The grid control should take up the space it's given (i.e. however much space is available in its parent control for it to use). Here I am referring just to the control, and not to the columns.
The columns created (whether automatically or manually) may or may not take up all this space.
If there is extra space left over after the columns are created, the last column should not be expanded to fill this space
If there is extra space left over after the columns are created, an empty column with nothing in it should not be created to fill this extra space
From what I can tell, in WPF the last two bullet points seem to be mutually exclusive and you must choose one or the other. Has anyone found a way to do both? I've searched quite a bit and haven't found quite what I'm looking for, however all the posts I'm finding tend to be a couple years old so I'm hoping someone has figured this thing out by now.
EDIT: sa_ddam213, here's a quick xaml project I put together to test your suggestion.
<Window x:Class="DataGridFix.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridFix"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="data"
ObjectType="{x:Type local:TestObject}"
MethodName="GetTestData" />
</Window.Resources>
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="150" VerticalAlignment="Top" ItemsSource="{Binding Source={StaticResource data}}" />
</StackPanel>
</Window>
And here's the code behind:
namespace DataGridFix
{
public class TestObject
{
public int Id { get; set; }
public string Name { get; set; }
public static List<TestObject> GetTestData()
{
var items = new List<TestObject>();
items.Add(new TestObject() { Id = 1, Name = "Joe" });
items.Add(new TestObject() { Id = 2, Name = "Matt" });
items.Add(new TestObject() { Id = 3, Name = "Hal" });
return items;
}
}
}
Really the only noteable thing I see from your suggestion is to set HorizontalAlignment to Left. I did that, and tried setting the ColumnWidth to the various settings but had the same problem with each (except * of course... technically I can mess that one up to but I won't go into that).
If you use your mouse to expand any of the columns, and then decrease the column size then the empty filler column appears. The only other difference I noted from your post was that you put your DataGrids in a StackPanel since you had more than one of them. I tried that just for the heck of it but same result. If you see any other difference between what I'm doing and what you suggested please let me know.
In order to get the behavior you want, you probably have to modify the control template of the DataGrid.
Take a look at the code. I have gotten pretty close to the WinForms DataGridView look i think.
To remove the extra column you have to remove the filler column from the DataGridColumnHeadersPresenter. I have just commented it out. The rest is just the default template.
The other modification is to the template of the DataGrid.
By setting HorizontalAlignment="Left" on the ScrollContentPresenter, the rows no longer take up all the width of the control.
Those are the only 2 changes I have made to the default templates.
<Style TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<!-- Remove this filler column -->
<!--<DataGridColumnHeader x:Name="PART_FillerColumnHeader" IsHitTestVisible="False" />-->
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True"
Padding="{TemplateBinding Padding}">
<ScrollViewer Focusable="false" Name="DG_ScrollViewer">
<ScrollViewer.Template>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Command="{x:Static DataGrid.SelectAllCommand}"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=CellsPanelHorizontalOffset}"
Style="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type DataGrid}, ResourceId=DataGridSelectAllButtonStyle}}"
Focusable="false"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.All}}" />
<DataGridColumnHeadersPresenter Grid.Column="1" Name="PART_ColumnHeadersPresenter"
Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=HeadersVisibility, Converter={x:Static DataGrid.HeadersVisibilityConverter}, ConverterParameter={x:Static DataGridHeadersVisibility.Column}}"/>
<!-- Set HorizontalAlignment="Left" to have the rows only take up the width they need and not fill the entire width of the DataGrid -->
<ScrollContentPresenter HorizontalAlignment="Left" x:Name="PART_ScrollContentPresenter" Grid.Row="1" Grid.ColumnSpan="2" CanContentScroll="{TemplateBinding CanContentScroll}" />
<ScrollBar Grid.Row="1" Grid.Column="2" Name="PART_VerticalScrollBar"
Orientation="Vertical"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{Binding Path=VerticalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=NonFrozenColumnsViewportHorizontalOffset}"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollBar Grid.Column="1"
Name="PART_HorizontalScrollBar"
Orientation="Horizontal"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{Binding Path=HorizontalOffset, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
</Grid>
</Grid>
</ControlTemplate>
</ScrollViewer.Template>
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
UPDATE
It does indeed look like there is a difference between .NET 4 and .NET 4.5.
I develop on a Windows 8 machine with Visual Studio 2012, so as a test I tried targeting .NET 4 to see if I could replicate the wrong behavior. But it still worked fine.
To be sure, I tried running the app on a different machine with only .NET 4 installed, and here the empty rows showed up when making a column bigger and then smaller again.
The issue seems to be that the DataGridRows are not behaving properly. When running on a machine with only .NET 4 installed, they keep their current size when making the column smaller. On .NET 4.5 they resize as expected.
The new solution to get the behavior you need, is actually much simpler than the previous one.
By simply setting the HorizontalAlignment on the DataGridRows to left, and removing the filler column, it works on both .NET 4 and .NET 4.5. And there is no longer a need to replace the entire template of the DataGrid.
<Style TargetType="DataGridRow">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style TargetType="DataGridColumnHeadersPresenter">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeadersPresenter}">
<Grid>
<ItemsPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
There ar plenty of layout options for Columns in WPF, its just a matter of choosing what you want to be displayed.
Pixel
SizeToCells
SizeToHeader
Auto
Proportional(*)
And if you set the HorizontalAlignment to Left the DataGrid will resize to fit its contents based on the ColumnWidth you picked.
Here is a example of some of the avaliable column settings
<StackPanel>
<DataGrid HorizontalAlignment="Left" ColumnWidth="100" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToCells" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="SizeToHeader" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="Auto" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
<DataGrid HorizontalAlignment="Left" ColumnWidth="*" Height="64" VerticalAlignment="Top" ItemsSource="{Binding ElementName=UI, Path=GridItems}" />
</StackPanel>

WPF tabitem positioning

What is the proper way of positioning for example three tabitems at the very top left corner and one at the very top right corner of a tab control using WPF?
I have tried to move the fourth tabitem to the right by changing its margin but this doesn't produce a good result; first of all it is cut short and second of all it does not display correctly when selected.
The problem is that the TabPanel, which is used internally by the TabControl to lay out the tabs, does not seem to support what you want. A quick workaround would be to replace the TabPanel by something else, for example, a DockPanel:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<TabControl>
<TabControl.Template>
<ControlTemplate TargetType="TabControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border BorderThickness="0,0,1,1" BorderBrush="#D0CEBF" Grid.Row="1">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="SelectedContent"/>
</Border>
</Border>
</Border>
<DockPanel IsItemsHost="True" LastChildFill="False" Margin="2,2,2,0" />
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabItem Header="Item 1" />
<TabItem Header="Item 2" />
<TabItem Header="Item 3" />
<TabItem Header="Item 4" DockPanel.Dock="Right" />
</TabControl>
</Window>
(Reference: This is a modified version of an MSDN example for styling a TabControl.)
The simple DockPanel doesn't work as smooth as the TabPanel -- the tabs "jump" a bit when switching between them, but this might get you started. Maybe subclassing the TabPanel and overriding the relevant parts would give you a more accurate result; I guess it depends on how much effort you want to put into this.
I found that by inserting an "invisible" tab I could adjust the spacing, (i.e. move the tabs down from the top)
For example:
TabItem Height="100" Visibility="Hidden" <br>
TabItem..... <br>
TabItem.... <br>
You would need to swap out the TabPanel within the TabControl to something custom which provided the desired behavior. None of the default panels are going to provide your desired behavior out of the box.
This will most likely need to involve overriding MeasureOverride and ArrangeOverride to provide the custom placement within the panel that is desired based on the number of items it contains.
This will involve a custom ControlTemplate for the TabControl. I tried an example using a DockPanel as the items host rather than the default TabPanel.
<Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle"
Value="True" />
<Setter Property="SnapsToDevicePixels"
Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Name="HeaderPanel"
LastChildFill="False"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border Name="Border"
Grid.Row="1"
Background="WhiteSmoke"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="2"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2">
<ContentPresenter Name="PART_SelectedContentHost"
Margin="4"
ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that I don't know of a way of exposing the DockPanel.Dock property to the TabItems outside of the ControlTemplate E.G.
<TabControl Margin="10">
<TabItem Header="Tab One" DockPanel.Dock="Left"/>
<TabItem Header="Tab Two" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Three" DocKPanel.Dock="Left"/>
<TabItem Header="Tab Four" DocKPanel.Dock="Right"/>
</TabControl>
// Note: This does not work!!
I guess you will need to write your own Panel to host the TabItems; Note that this will be quite a lot of work as you will need to handle things like overflow behaviour which is built into the TabPanel.
Even if you did try this I think you would have to write a custom TabControl if you wanted to expose this functionality outside of the ControlTemplate.
If you want to go down this road then see my answer in this post

WPF DropShadow Disappears

Wpf dropshadow disappears.
Here is how to reproduce.
Type the following in xaml pad.
<Page.Resources>
<DropShadowEffect x:Key="shadow"
Opacity="1"
ShadowDepth="1"
Color="Blue"
BlurRadius="30"/>
</Page.Resources>
<Grid>
<Button HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,0,0,0">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
BorderBrush="Black" BorderThickness="1"
Background="Yellow"
CornerRadius="8"
Effect="{StaticResource shadow}">
<TextBlock Text="Hello out there" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</Grid>
You should see some text with a border abound it, and a drop shadow around the border.
Now change the Margin="0,0,0,0" to Margin="0,300,0,0", and size your xaml pad window so you can see the border. On my machine, the drop shadow is now gone.
Anyone else see this? Please help.
I wish I had a good explanation for you, but there were some weird things in your XAML that I played with and I think I have a solution for you.
If you use a Grid, most likely you want to lay out a specific number of rows and columns. You should specify those. This doesn't affect your problem, however.
Likewise, you should specify the Row and Column for your element because you'll eventually need to put this information in your XAML anyway. It's a good habit to start with IMO.
The problem that I can't explain is with the combination of HorizontalAlignment and VerticalAlignment. When you put the Button in the Grid, I would expect the Button to take up the entire space, but it doesn't. The only way you can make this work as far as I could figure out was to specify Height and Width. If you do this, the Effect will work. I found that the threshold in your original XML was a total Y margin of 239. For example, if you used 0,239,0,0, it would fail. If you used 0,139,0,100, it would also fail because the sum is 239. Weird stuff.
Here's my XAML that works:
<Page.Resources>
<DropShadowEffect x:Key="shadow"
Opacity="1"
ShadowDepth="2"
Color="Blue"
BlurRadius="30"/>
</Page.Resources>
<Grid Width="Auto" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Width="90" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,300,0,0" Grid.Row="0" Grid.Column="0">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
BorderBrush="Black" BorderThickness="1"
Background="Yellow"
CornerRadius="8"
Effect="{StaticResource shadow}">
<TextBlock Text="Hello out there" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
EDIT The OP does not want to specify a size for the Button because the Content of the Button can change dynamically. It turns out that if you set the MinHeight to something like 18 (I think this is reasonable for most content), the dropshadow effect will work again.
<Border x:Name="Bd" BorderBrush="Black" BorderThickness="1" Background="Yellow" CornerRadius="8" Effect="{StaticResource shadow}" MinHeight="18">
<StackPanel Orientation="Vertical">
<TextBlock>hi</TextBlock>
<TextBlock>there</TextBlock>
</StackPanel>
</Border>

Resources