I have a ListViewItem which has an Eventhandler attatched and inside the ControlTemplate of the ListViewItem is a Button which does something different. But if I click the Button, the Behaviour is like I clicked on the ListViewItem.
AppointmentOverview.xaml:
<Window.Resources>
<Style TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource NormalListViewItem}">
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="ListViewItem_PreviewMouseLeftButtonDown" />
</Style>
</Window.Resources>
Styles.xaml(My ResourceDictionary):
<!--Style für die normalen ListViewItems-->
<Style x:Key="NormalListViewItem" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="#5076A7" BorderThickness="1">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFFFF" Offset="0.0"/>
<GradientStop Color="#FFFEB603" Offset="1.0"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel TextElement.FontFamily="Segoe UI" TextElement.FontSize="12">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="15"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="Betreff" Padding="3,0,0,0" Text="{Binding Betreff}" TextTrimming="CharacterEllipsis" Grid.Column="0" Grid.Row="0"/>
<Button Grid.Column="1" Grid.Row="0" Style="{StaticResource ListViewItemButton}"/>
AppointmentOverview.xaml.cs:
private void ListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var item = sender as ListViewItem;
if (item != null)
{
AppointmentOverviewViewModel apvm = this.DataContext as AppointmentOverviewViewModel;
apvm.editAppointment(item);
}
}
It worked when I had the complete ListViewItem Style in the Window.Resources of Appointmentoverview.xaml. But I didn't like that because the would kind of beat the purpose of Styles.xaml. Also I didn't have a Style for the Button, just did all the Styling inside the Button. But this was very Basic and now I need more complex Styling so I wanted to create a separate Style.
<Button FontSize="7" Content="X" Grid.Column="1" Grid.Row="0"
Command="{Binding DataContext.DeleteButtonCommand, RelativeSource={
RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding ItemId}"/>
Update:
If I observe my EventHandler this is also triggered if the Button is pressed. It just says the source is a Button but the command is not executed.
Styles.xaml.cs
void ListViewItem_MouseLeftDown(object sender, MouseButtonEventArgs e)
{
DependencyObject current = sender as DependencyObject;
while (current != null && current.GetType() != typeof(ListViewItem))
{
current = VisualTreeHelper.GetParent(current);
}
var item = current as ListViewItem;
if (item != null)
{
Window parent = Window.GetWindow(current);
AppointmentOverviewViewModel apovm = parent.DataContext as AppointmentOverviewViewModel;
apovm.editAppointment(item);
}
}
Styles.xaml
<!--DataTemplate für die normalen ListViewItems-->
<DataTemplate DataType="{x:Type local:SCSMAppointment}">
<Border BorderBrush="#5076A7" BorderThickness="1" PreviewMouseLeftButtonDown="ListViewItem_MouseLeftDown">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFFFFF" Offset="0.0"/>
<GradientStop Color="#FFFEB603" Offset="1.0"/>
</LinearGradientBrush>
</Border.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<Button FontSize="7" Content="X" DockPanel.Dock="Right" Width="15"
Command="{Binding DataContext.DeleteButtonCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding ItemId}"/>
<TextBlock Name="Betreff" Padding="3,0,0,0" Text="{Binding Betreff}" TextTrimming="CharacterEllipsis" />
</DockPanel>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<TextBlock Padding="3,0,0,0" Text="{Binding Kunde}"/>
<TextBlock Padding="3,0,0,0" Text="|"/>
<TextBlock Padding="3,0,0,0" Text="{Binding IncidentId}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBlock FontWeight="Bold" Padding="3,0,0,0" Text="{Binding Ort}"/>
<TextBlock Padding="3,0,0,0" Text="("/>
<TextBlock Text="{Binding Alternative}"/>
<TextBlock Text=")"/>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
It is apparent that your XAML is not correct. A ControlTemplate is primarily used to Specify the visual structure and behavioral aspects of a Control that can be shared across multiple instances of the control. In plain English, that means that they are used to change the default look of a Control. If that is not what you are doing, then you should not be using them.
On the other hand, DataTemplates Describe the visual structure of a data object, so you should be declaring DataTemplates to define what your data looks like instead of using ControlTemplates. Therefore, I suggest that you read the Data Binding Overview page on MSDN, which will give you a much better understanding, before you continue to work with WPF.
When you have updated your project, your problem will probably fix itself, but if it doesn't please return here to edit your question with your new XAML.
Related
I'm trying to acomplish exactly what this question is requesting, but unfortunately the code sample that was provided as the answer is gone, and I'm also not using WPF Toolkit, here the question that he did:
I am trying to Merge cells in WPF toolkit datagrid .I am trying to do something as shown in the image below.We can do this in Winforms datagrid.But how to do this using WPF toolkit datagrid ?.Or is there any alternative controls..?
Can we do this using listview or listbox..? Or is there any free
controls available which have this functionality ?
I found several answers that manage to do this with the DataGridView control, but I do not want to use Form objects in a WPF project, is there a way to acomplish this?
Recource
<Window.Resources>
<Color x:Key="customBlue" A="255" R="54" G="95" B="177" />
<SolidColorBrush x:Key="customBlueBrush" Color="{StaticResource customBlue}"></SolidColorBrush>
<SolidColorBrush x:Key="customBlueBrushOpacity" Color="LightGray" Opacity="0.11"></SolidColorBrush>
<Style x:Key="calcyListbox" TargetType="ListBox">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="35"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Height="30" VerticalAlignment="Top" Background="{StaticResource customBlueBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Manufacturer" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" ></TextBlock>
<TextBlock Text="Name" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"></TextBlock>
<TextBlock Text="CPU" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"></TextBlock>
<TextBlock Text="RAM" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"></TextBlock>
<TextBlock Text="Price" FontSize="14" FontFamily="Segoe Ui Dark" Foreground="White" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="4"></TextBlock>
</Grid>
<Border Grid.Row="1" SnapsToDevicePixels="True" Background="Transparent" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0">
<ScrollViewer x:Name="ScrollViewer" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="0">
<ItemsPresenter />
</ScrollViewer>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="noStyleToListboxItem" TargetType="ListBoxItem">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border>
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
xaml
<ListBox MaxHeight="300" ItemsSource="{Binding ManufacturerList}" Background="{StaticResource customBlueBrushOpacity}" x:Name="ManufacturerListBox" ScrollViewer.VerticalScrollBarVisibility="Auto" Style="{StaticResource calcyListbox}" ItemContainerStyle="{StaticResource noStyleToListboxItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Company}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Border BorderThickness="0,0,0,1" BorderBrush="Black" ></Border>
<ListBox Grid.Column="1" BorderThickness="1,0,1,1" Background="{StaticResource customBlueBrushOpacity}" HorizontalContentAlignment="Stretch" ItemsSource="{Binding Models}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="0"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="1"></Border>
<Border BorderThickness="0,0,1,0" BorderBrush="Black" Margin="-2" Grid.Column="2"></Border>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0"/>
<TextBlock Text="{Binding CPU}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1"/>
<TextBlock Text="{Binding Ram}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="2"/>
<TextBlock Text="{Binding price}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
c#
InitializeComponent();
List<Manufacturer> ManufacturerList = new List<Manufacturer>();
ManufacturerList.Add(new Manufacturer()
{
Company = "DEll",
Models = new List<Model>(){new Model(){CPU = "T7250", Name = "Inspiron1525", price =234434 , Ram= "2048 MB" },
new Model(){CPU = "T5750", Name = "Studio 1535", price =234443 , Ram= "2048 MB" },
new Model(){CPU = "T5780", Name = "Vastro 1510", price =234434 , Ram= "2048 MB" },}
});
ManufacturerList.Add(new Manufacturer()
{
Company = "Lenovo",
Models = new List<Model>(){new Model(){CPU = "T1230", Name = "l123", price =23546454 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "l1423", price =2346456 , Ram= "1024 MB" },
new Model(){CPU = "T1230", Name = "ldf123", price =2344646 , Ram= "1024 MB" },}
});
ManufacturerListBox.ItemsSource = ManufacturerList;
public class Manufacturer
{
public string Company { get; set; }
public List<Model> Models { get; set; }
}
public class Model
{
public string Name { get; set; }
public string Ram { get; set; }
public double price { get; set; }
public string CPU { get; set; }
}
For a merged cell, bind its Margin and set it to a negative value equal to the sum width/height of the associated merged cells - then the cell will spill across and on top the neighboring cells.
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="{{Binding BorderThickness[5], Mode=OneWay}}"/>
<Setter Property="Margin" Value="{{Binding CellMargins[5], Mode=OneWay}}"/>
<Setter Property="Block.TextAlignment" Value="Center"/>
</Style>
The bindings are indexed because CellMargins is an ObservableCollection of Thickness. The viewmodel is row-level, so each column binds to a different index.
Before this gets downvoted, let me say this can lead to some undesirable visual quirks. Getting this to work perfectly requires a bit of code-behind, such as:
The cell margins' negative values need to be updated whenever a column width changes, so I handled the datagrid's LayoutUpdated event, where I iterated through the columns, measured their current ActualWidths and updated the margins accordingly. I likewise update the BorderThickness to show/hide cell borders as needed. Disabling the selection highlight for cells that are "hidden" is another trick.
Note this means keeping track of what cells (i.e., which row viewmodels and column indices) you want merged separately in code-behind.
This method is complicated and probably not the easiest for every situation, but I found it useful for my purposes. I needed a Datagrid where the user could merge/unmerge cells and create/delete columns, similar to Excel. Furthermore, I wanted to stick with DataGridTextColumns (instead of custom TemplateColums) because of their built-in functionality (recognized keyboard edit commands, copy/paste, etc.)
I am having a problem hooking up some events in my custom dropdown or Popup template in my combobox.
This custom template by the way looks like the one in IE 10. Here's the picture
I got the partial look (the one below is mine). But I'm having a problem deleting an item in history list. Below is my PART_Popup template
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=Placement}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<!--<ScrollViewer>-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel>
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Border Padding="5">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="History" Foreground="{StaticResource SeparatorLine}" />
<Border Height="2" Grid.Column="1" Margin="5,0,0,0" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0.5" />
</Grid>
<ListBox x:Name="listHistory" BorderThickness="0" Margin="0" Padding="0" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding }" />
<Button Grid.Column="1" HorizontalAlignment="Right" x:Name="btnDeleteHistoryItem" Content="r" FontFamily="Marlett" Style="{DynamicResource ButtonStyle}" Visibility="Hidden" Opacity="0.75" />
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility" TargetName="btnDeleteHistoryItem" Value="Visible" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</StackPanel>
<Border Grid.Row="1" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0,1,0,0" Padding="5" Height="30">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#33000000" Offset="0"/>
<GradientStop Offset="1"/>
<GradientStop Offset="0.375"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="btnClearHistory" Content=" Clear History " Style="{DynamicResource ButtonStyle}" />
</StackPanel>
</Border>
</Grid>
<!--</ScrollViewer>-->
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
alright, in the Listbox(listHistory) I have a button there called btnDeleteHistoryItem and I cannot hook it. It's returning a Null error in my code here
protected override void OnSourceInitialized(EventArgs e)
{
ListBox lb = (ListBox)cbSearch.Template.FindName("listHistory", cbSearch);
lb.ItemsSource = this.SearchHistory;
lb.SelectionChanged += cbResults_SelectionChanged;
Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
// if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
// or --------------
Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch);
// if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
}
Coding is a little classic here, so no MVVM implemented in hooking with events. So the problem is this Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb); or Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch);
line where it cannot find that btnDeleteHistoryItem.
How can I resolve this?
-- UPDATE --
lb.Loaded += (a, b) =>
{
Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
//if (btnDeleteHistoryItem != null)
{
btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
}
};
doesn't work either
Using a command will probably be the easiest solution here. You can hook a command up to your delete buttons and pass the current item in as the parameter. Here's a simple sample to illustrate.
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
SomeCommand = new MySampleCommand();
cbo.Items.Add("Hello");
cbo.Items.Add("Item 1");
cbo.Items.Add("Another Item");
cbo.Items.Add("Something else");
cbo.Items.Add("Yet another item");
}
public MySampleCommand SomeCommand { get; set; }
}
public class MySampleCommand : ICommand {
public bool CanExecute(object parameter) {
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) {
MessageBox.Show(string.Format("You are trying to remove {0}", parameter.ToString()));
}
}
Your combobox binding would look something like:
<Window x:Class="SOComboWithEmbeddedButton2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="ListBoxItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button Command="{Binding SomeCommand, ElementName=Window}" CommandParameter="{Binding}" Content="Remove" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ComboBox ItemTemplate="{StaticResource ListBoxItemTemplate}" x:Name="cbo" />
</StackPanel>
Just adjust the Command binding on the DataTemplate to point to the appropriate object that has an instance of your command object. Hope that helps!
I have styled the tabitem in a tabcontrol as such
<Style x:Key="TabHeaderStyle" TargetType="{x:Type TabItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate >
<Grid HorizontalAlignment="Stretch" Height="22">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="2" ></RowDefinition>
<RowDefinition Height="Auto" ></RowDefinition>
</Grid.RowDefinitions>
<Rectangle Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem} }, Path=Content.DataContext.HeaderColor}" Grid.ColumnSpan="2"></Rectangle>
<TextBlock Grid.Row="1" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem} }, Path=Content.DataContext.HeaderInfo}"
VerticalAlignment="Bottom" Margin="4,0,8,0"/>
<Button Grid.Row="1" Grid.Column="1" Content="x" ToolTipService.ToolTip="Close this view." BorderBrush="{x:Null}" Background="{x:Null}" Foreground="#FF224A71" VerticalAlignment="Center" Padding="3,0">
<Button.OpacityMask>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#4BFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Button.OpacityMask>
<ei:Interaction.Triggers>
<ei:EventTrigger EventName="Click">
<Controls:CloseTabbedViewAction />
</ei:EventTrigger>
</ei:Interaction.Triggers>
</Button>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The tabitem look ok when i have a couple of tabs open like this
This look ok with the "x" , close view right aligned. My problem is that when i open more tabs, the standard tabcontrol functionallity is to add a row when there is to little space for the tabs. When this happens the tabitems are resized but it does not seem that they notify the childcontrols because the tabs look like this
Notice that the "x" is not right aligned as before. I have tried to base the datatemplate on both grid and stackpanel with different style to no avail. Anyone knows how i will get the "x" button right aligned even when i have multiple rows of tabs.
Your Grid's ColumnDefinitions will limit their width (Auto means automatically size to content)
Instead try:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
(Also, I don't know if will make a difference, but in my TabItem styles I usually set the Template with a ControlTemplate, rather than set the HeaderTemplate with a DataTemplate as you are doing)
Try this in your TabItem template...
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
Let me know if it solves your problem. I cant test your code here.
Currently I am setting the background on each Grid row individually:
<Grid>
<Grid.RowDefinitions><RowDefinition /><RowDefinition /></Grid.RowDefinitions>
<Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /><ColumnDefinition /></Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="6" Height="24" BorderBrush="#FF252A30" CornerRadius="10" BorderThickness="1">
<Border.Background>
<LinearGradientBrush EndPoint="1.036,0.367" StartPoint="-0.194,0.362">
<GradientStop Color="#AAE098" Offset="0.1"/>
<GradientStop Color="#D5E9D4" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="6" Height="24" BorderBrush="#FF252A30" CornerRadius="10" BorderThickness="1">
<Border.Background>
<LinearGradientBrush EndPoint="1.036,0.367" StartPoint="-0.194,0.362">
<GradientStop Color="#AAE098" Offset="0.1"/>
<GradientStop Color="#D5E9D4" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
Surely there must be some way to set this Border once for all rows. How is that done?
Thanks!
Or you could use this grid I just made. It will automatically add a border to every cell in the grid. This is the result:
C#:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace GridWithBorder
{
public class BorderGrid : Grid
{
protected override void OnRender(DrawingContext dc)
{
double leftOffset = 0;
double topOffset = 0;
Pen pen = new Pen(Brushes.Black, 3);
pen.Freeze();
foreach (RowDefinition row in this.RowDefinitions)
{
dc.DrawLine(pen, new Point(0, topOffset), new Point(this.ActualWidth, topOffset));
topOffset += row.ActualHeight;
}
// draw last line at the bottom
dc.DrawLine(pen, new Point(0, topOffset), new Point(this.ActualWidth, topOffset));
//foreach (ColumnDefinition column in this.ColumnDefinitions)
//{
// dc.DrawLine(pen, new Point(leftOffset, 0), new Point(leftOffset, this.ActualHeight));
// leftOffset += column.ActualWidth;
//}
// draw last line on the right
//dc.DrawLine(pen, new Point(leftOffset, 0), new Point(leftOffset, this.ActualHeight));
base.OnRender(dc);
}
}
}
XAML:
<Window x:Class="GridWithBorder.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GridWithBorder"
Title="MainWindow" Height="350" Width="525">
<local:BorderGrid>
<local:BorderGrid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</local:BorderGrid.RowDefinitions>
<local:BorderGrid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</local:BorderGrid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.Column="0" Fill="Red" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="1" Fill="Blue" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="2" Fill="Orange" Margin="5" />
<Rectangle Grid.Row="0" Grid.Column="3" Fill="Red" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="0" Fill="Yellow" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Green" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="2" Fill="Purple" Margin="5" />
<Rectangle Grid.Row="1" Grid.Column="3" Fill="Green" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="0" Fill="Orange" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="1" Fill="Red" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="2" Fill="Blue" Margin="5" />
<Rectangle Grid.Row="2" Grid.Column="3" Fill="Red" Margin="5" />
</local:BorderGrid>
</Window>
You could pull that border out into a reusable resource, but I suspect what you're really trying to do is create a GridView.
You can just set the Background property on your Grid. If there is commonality between the border which you are applying to the different rows, you can create a default style (and if desired, limit the scope of this style to the Grid itself):
XAML
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Border}">
<!-- All rows -->
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="CornerRadius" Value="5" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.Row="0">
<TextBlock Text="This row has a black border (default)." />
</Border>
<Border BorderBrush="Red" Grid.Row="1">
<TextBlock Text="This row has a red border." />
</Border>
<Border BorderBrush="Green" BorderThickness="4" Grid.Row="2">
<TextBlock Text="This has a thick green border." />
</Border>
</Grid>
With a default Style, no additional property needs to be set on your row's Border to achieve a default look (row one above). If a certain row needs to tweak the look and feel, then just provide additional properties on the Border to override the ones set in the default Style (rows two and three above). If this technique is something you are applying across multiple views in your application, then you can extract this style into a separate ResourceDictionary and simply merge it where appropriate.
Hope this helps!
Upper part of TabControl consists of TabItem controls. Is there a way to reuse remaining space there to put some WPF content?
I think I could use a "fake" TabItem with different styling and put my stuff in TabItem.Header but I was hoping there's a better way.
Solution
Based on the answer below, I got the desired behavior by wrapping TabPanel in the template below within e.g. StackPanel and adding my additional content after it.
<StackPanel Orientation="Horizontal">
<TabPanel
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
Background="Transparent" />
<TextBlock>Foo</TextBlock>
</StackPanel>
I tried a different way, which was to create another grid that occupies the same space as the TabControl, ie both are in Grid.Row=0. I have bound the grid height to the height of the first tab so if the tabs change height the other controls will remain centered. I set MinWidth on the window so the controls dont overlap the tabs.
Paste this code into a new WPF Window...
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="306" Width="490" MinWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TabControl Grid.Row="0" x:Name="tabControl">
<TabItem x:Name="tabItem" Header="TabItem" Height="50">
<Grid Background="#FFE5E5E5"/>
</TabItem>
<TabItem Header="TabItem">
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
<Grid Grid.Row="0" Height="{Binding ActualHeight, ElementName=tabItem}"
VerticalAlignment="Top" Margin="0,2,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"
VerticalAlignment="Center" Margin="20,0">
<TextBlock VerticalAlignment="Center" Margin="10,0" FontSize="16"
Foreground="Red" FontFamily="Calibri">My Text</TextBlock>
<Button Content="My Button" />
</StackPanel>
</Grid>
</Grid>
</Window>
...and you will get this:
You can use a template and make it do whatever you want, that is the power of WPF. Here is a nice article on customizing the TabControl and the TabItem controls.
< EDIT Adding code for TabControl template from Switch On The Code article>
<Style TargetType="{x:Type TabControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabPanel
Grid.Row="0"
Panel.ZIndex="1"
Margin="0,0,4,-1"
IsItemsHost="True"
Background="Transparent" />
<Border
Grid.Row="1"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="0, 12, 12, 12" >
<Border.Background>
<LinearGradientBrush>
<GradientStop Color="LightBlue" Offset="0" />
<GradientStop Color="White" Offset="1" />
</LinearGradientBrush>
</Border.Background>
<ContentPresenter ContentSource="SelectedContent" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
all you have to do is add your content to the Template, the part that holds the tab items is the <TabControl>