I want to implement a button, that have 4 stage (Stage1/Stage2/Stage3/Stage4)
and I want to click on it, the text and style will change (Stage1 -> Stage2 -> Stage3 -> Stage4 -> Stage1 -> ....)
Here is my XAML button code.
<ControlTemplate x:Key="StageButton" TargetType="{x:Type Button}">
<Button
Width="102"
Tag="1"
Click="Hello_Click"
BorderThickness="0"
Background="Transparent"
Cursor="Hand">
<Border
BorderThickness="2"
BorderBrush="Gray"
CornerRadius="10"
Background="Green"
Width="100"
Height="20"
>
<TextBlock
Width="100"
Foreground="White"
FontWeight="Bold"
FontSize="12"
Padding="14 1 0 0"
Text="Stage1"
/>
</Border>
</Button>
</ControlTemplate>
Thanks for help
You can make a custom Button with incremental stage (starting at 1) functionality:
using System;
using System.Windows;
using System.Windows.Controls;
public class StagedButton : Button
{
public int Stage
{
get { return (int)GetValue(StageProperty); }
set { SetValue(StageProperty, value); }
}
public static readonly DependencyProperty StageProperty =
DependencyProperty.Register("Stage", typeof(int), typeof(StagedButton),
new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null,
(d, baseValue) => Math.Min(((StagedButton)d).StageCount, Math.Max(1, (int)baseValue))));
public int StageCount
{
get { return (int)GetValue(StageCountProperty); }
set { SetValue(StageCountProperty, value); }
}
public static readonly DependencyProperty StageCountProperty =
DependencyProperty.Register("StageCount", typeof(int), typeof(StagedButton),
new PropertyMetadata(1, null,
(d, baseValue) => Math.Max(1, (int)baseValue)));
protected override void OnClick()
{
var stage = Stage;
Stage = (++stage <= StageCount) ? stage : 1;
base.OnClick();
}
}
StageCount is the number of stages. (d, baseValue) => ... part is to filter out invalid values.
Then define a Style for this Button.
<Style x:Key="StagedButtonStyle" TargetType="{x:Type local:StagedButton}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="20"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="LightGray"/>
<Setter Property="Background" Value="#c42a1c"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="TextElement.FontWeight" Value="Bold"/>
<Setter Property="TextElement.FontSize" Value="12"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:StagedButton}">
<Border BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
CornerRadius="10">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- Set 4 to StageCount. -->
<Setter Property="StageCount" Value="4"/>
<Style.Triggers>
<!-- Define Background for each Stage (up to 4) except default one. -->
<Trigger Property="Stage" Value="2">
<Setter Property="Background" Value="DodgerBlue"/>
</Trigger>
<Trigger Property="Stage" Value="3">
<Setter Property="Background" Value="Green"/>
</Trigger>
<Trigger Property="Stage" Value="4">
<Setter Property="Background" Value="DimGray"/>
</Trigger>
</Style.Triggers>
</Style>
You can increase the number of stages if you wish.
Its usage will be as follows:
<local:StagedButton Style="{StaticResource StagedButtonStyle}">
<TextBlock Text="{Binding Stage, RelativeSource={RelativeSource AncestorType={x:Type local:StagedButton}}, StringFormat=Stage {0}}"/>
</local:StagedButton>
In the following Window I define a trigger for IsMouseOver. The background color is changed correctly, but it has an gradient effect. See the pic below. How to get rid of the effect?
Only Theme.DataGrid.Row.Background.Hover is moved from separate style file to code excerpt below.
<Window x:Class="MyCompany.Application.Shared.UI.Dialogs.SomeWindow
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="350" Width="525" Background="#FFF3F3F7">
<Window.Resources>
<SolidColorBrush x:Key="Theme.DataGrid.Row.BorderBrush" Color="#FFF3F3F7" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Background" Color="White" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Background.Hover" Color="#FFAEAEB6" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Background.Active" Color="#FF0D6AA8" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Background.HoverSelected" Color="#FF009AD9" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Background.Disabled" Color="#FFAEAEB6" options:Freeze="True" />
<SolidColorBrush x:Key="Theme.DataGrid.Row.Foreground.Selected" Color="White" options:Freeze="True" />
<Style x:Key="GridView.ColumnHeader.Gripper.Style" TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="8" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Cursor" Value="SizeWE" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type GridViewColumnHeader}" >
<EventSetter Event="FrameworkElement.Loaded" Handler="GridViewColumnHeader_Loaded"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Background" Value="{StaticResource Theme.DataGrid.ColumnHeader.Background}"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="1" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
<Thumb Grid.Column="1" x:Name="PART_HeaderGripper" HorizontalAlignment="Right" Style="{DynamicResource Theme.DataGrid.ColumnHeader.Gripper.Style}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListView}" >
<Setter Property="BorderThickness" Value="{DynamicResource Theme.DataGrid.BorderThickness}"/>
<Setter Property="Background" Value="{StaticResource Theme.TreeView.Background}"/>
</Style>
<Style TargetType="{x:Type ListViewItem}" >
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="{DynamicResource Theme.DataGrid.Row.Foreground}" />
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="{DynamicResource Theme.DataGrid.Cell.Padding}"/>
<Setter Property="Margin" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource Theme.DataGrid.Row.Background.Hover}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource Theme.DataGrid.Row.Background.Active}"
<Setter Property="Foreground" Value="{DynamicResource Theme.DataGrid.Row.Background.Selected}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource Theme.DataGrid.Row.Background.Disabled}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Theme.DataGrid.Row.Background.HoverSelected}" />
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<CheckBox Content="IsGrouped" IsChecked="{Binding IsGrouped}"/>
<ListView Margin="10" ItemsSource="{Binding Users}">
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" DisplayMemberBinding="{Binding Mail}" />
<GridViewColumn Header="Group" DisplayMemberBinding="{Binding Group}" />
</ListView>
</StackPanel>
</Window>
ViewModel (note that this uses our own ViewModel-class that i.e raises PropertyChanged events)
namespace MyCompany.Application.Shared.UI.Dialogs
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public string Mail { get; set; }
public string Group { get; set; }
}
public class SomeWindowViewModel : ViewModel
{
List<User> items = new List<User>();
IEnumerable<User> GetUsers()
{
foreach (var item in items)
{
yield return item;
}
}
public ICollectionView Users { get; }
private bool _isGrouped = false;
public bool IsGrouped
{
get { return _isGrouped; }
set
{
_isGrouped = value;
if (value)
{
Users.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
}
else
{
Users.GroupDescriptions.Clear();
}
}
}
public SomeWindowViewModel()
{
items.Add(new User() { Name = "John Doe", Age = 42, Mail = "john#doe-family.com", Group = "OneGroup1" });
items.Add(new User() { Name = "Jane Doe", Age = 39, Mail = "jane#doe-family.com", Group = "OneGroup1" });
items.Add(new User() { Name = "Sammy Doe", Age = 7, Mail = "sammy.doe#gmail.com", Group = "TwoGroup2" });
items.Add(new User() { Name = "Pentti Doe", Age = 7, Mail = "pena.doe#gmail.com", Group = "TwoGroup2" });
Users = CollectionViewSource.GetDefaultView(GetUsers());
}
}
}
I had some issues with the given code sample, e.g. missing resources or additional options:Freeze="true" which could not be resolved, therefore my solution may look a little different, but I think it does what you want: it removes the gradient effect.
The issue is that the default ListViewItem template has triggers which change the background (as you have found out as well) and the way around is to use a simplified ControlTemplate without any triggers.
This example of a simplified template works and you can put it in your {x:Type ListViewItem} to get a look like shown in the pictures below.
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<GridViewRowPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
Using that template, this is what it will look like if you first selected "Sammy Doe 7" and then hover over "Jane Doe 39",
and this is how it looks when you select (e.g. click on) "Jane Doe 39":
I'm not entirely sure what you mean with
as jsanalytics points out control template results in rows to be class names and not the data
but as far as I understand it, above code should not lead to that behavior. If it does, let me know and I will find another solution.
I had implemented a template for textbox to show image inside it, but the problem I facing is that I unable to show image dynamically. Here is my code
Window.xaml
<TextBox Style="{StaticResource imageTextBox}" />
Here is my textbox template
<Style TargetType="Control" x:Key="imageTextBox">
<Setter Property="Margin" Value="0,0,0,8"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Control">
<Border x:Name="bg" BorderThickness="1" CornerRadius="3" BorderBrush="Gray">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Style="{DynamicResource BasicTextBox}" Grid.Column="1"
Foreground="{TemplateBinding Foreground}"
Background="{TemplateBinding Background}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontWeight="{TemplateBinding FontWeight}"
MinWidth="340" Margin="1" />
<Image Source="Images/img_1.gif" Width="20"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="bg" Value="Black"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" TargetName="bg" Value="Black"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BasicTextBox" TargetType="{x:Type TextBox}">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
You can use a {Binding} for the <Image.Source> property. Here's an example as a LinqPad query (get LinqPad free):
void Main()
{
// Need to parse XAML since LinqPad doesn't have an integrated XAML build
var xaml = #"
<Grid xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<Grid.Resources>
<Style TargetType=""Control"" x:Key=""imageTextBox"">
<Setter Property=""Margin"" Value=""0,0,0,8""/>
<Setter Property=""Template"">
<Setter.Value>
<ControlTemplate TargetType=""Control"">
<Border x:Name=""bg"" BorderThickness=""1"" CornerRadius=""3"" BorderBrush=""Gray"">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=""30""/>
<ColumnDefinition Width=""*""/>
</Grid.ColumnDefinitions>
<TextBox Style=""{DynamicResource BasicTextBox}"" Grid.Column=""1""
Foreground=""{TemplateBinding Foreground}""
Background=""{TemplateBinding Background}""
FontFamily=""{TemplateBinding FontFamily}""
FontSize=""{TemplateBinding FontSize}""
FontWeight=""{TemplateBinding FontWeight}""
MinWidth=""340"" Margin=""1"" />
<Image Source=""{Binding Image}"" Width=""20""/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property=""IsMouseOver"" Value=""True"">
<Setter Property=""BorderBrush"" TargetName=""bg"" Value=""Black""/>
</Trigger>
<Trigger Property=""IsFocused"" Value=""True"">
<Setter Property=""BorderBrush"" TargetName=""bg"" Value=""Black""/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<TextBox Style=""{StaticResource imageTextBox}"" />
</Grid>
";
// Get some images... these could be in your resources
var png = PngBitmapDecoder.Create(new Uri("https://upload.wikimedia.org/wikipedia/commons/9/97/Esperanto_star.png"), BitmapCreateOptions.None, BitmapCacheOption.Default);
var vm = new ViewModel { Image = png.Frames[0] };
var o = (FrameworkElement)XamlReader.Parse(xaml);
// Set the image source - in this case, a view model
o.DataContext = vm;
// Let LinqPad display it
PanelManager.DisplayWpfElement(o);
// This code is for the example only, to change the image after 2 seconds.
var dispatcher = o.Dispatcher;
Task.Run(async () =>
{
await Task.Delay(2000);
await dispatcher.BeginInvoke((Action)(() =>
{
png = PngBitmapDecoder.Create(new Uri("https://upload.wikimedia.org/wikipedia/commons/f/f6/Lol_question_mark.png"), BitmapCreateOptions.None, BitmapCacheOption.Default);
vm.Image = png.Frames[0];
}));
});
}
// Define other methods and classes here
public class ViewModel : INotifyPropertyChanged
{
private ImageSource _image;
public event PropertyChangedEventHandler PropertyChanged;
public ImageSource Image
{
get
{
return _image;
}
set
{
if (_image == value)
{
return;
}
_image = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Image)));
}
}
}
This example uses a simple view model as the Binding source, but it could be any source. There is also code which changes the Image value after a 2 second delay to show you that it does in fact update. In your code, you'd omit this and use the way you want to update it.
At first it appears:
Then after update:
First of all I'm an amateur and non-english native speaker, so I would appreciate it if you would have a little patience with me ;)
Following another stackoverflow question, I've done a TabControl that expands itself (like an expander) just modifying the template of the original TabControl, so I can just alter the height of the control when any tab is selected.
That worked perfect, but needed some ViewModel properties for the logic. Now i need to use that control again and i thought the better way would be to make a CustomControl.
The problem I'm having is that the CustomControl are just lookless, no style are applied to it although I have specified styles for it in the Generic.xaml file, and the Build Action property to Page, revised the AsemblyInfo.cs, and used the DefaultStyleKeyProperty.OverrideMetadata in the static constructor. All following other questions suggestions.
I think it'd be better to show the code I have now and then explain what more I've tried, I have to say that I was writing the logic when I noticed this, so it does not work right now.
Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PRUEBA_TABBEDEXPANDER"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:ie="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:Core="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
xmlns:int="clr-namespace:System.Windows.Interactivity">
<SolidColorBrush x:Key="TabControlNormalBorderBrush" Color="#8C8E94"/>
<Style x:Key="TabItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="3,3,3,1" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ButtonNormalBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F3F3F3" Offset="0"/>
<GradientStop Color="#EBEBEB" Offset="0.5"/>
<GradientStop Color="#DDDDDD" Offset="0.5"/>
<GradientStop Color="#CDCDCD" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemHotBackground" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#EAF6FD" Offset="0.15"/>
<GradientStop Color="#D9F0FC" Offset=".5"/>
<GradientStop Color="#BEE6FD" Offset=".5"/>
<GradientStop Color="#A7D9F5" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItemSelectedBackground" Color="#F9F9F9"/>
<SolidColorBrush x:Key="TabItemHotBorderBrush" Color="#3C7FB1"/>
<SolidColorBrush x:Key="TabItemDisabledBackground" Color="#F4F4F4"/>
<SolidColorBrush x:Key="TabItemDisabledBorderBrush" Color="#FFC9C7BA"/>
<Style x:Key="TabItemStyle1" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{StaticResource TabItemFocusVisual}"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Padding" Value="6,1,6,1"/>
<Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
<Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid SnapsToDevicePixels="true">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
<ToggleButton x:Name="Content" Content="{TemplateBinding Header}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="ToggleButton_Click"
TargetObject="{Binding RelativeSource={RelativeSource TemplatedParent}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ToggleButton>
<!--<ContentPresenter x:Name="Content" ContentSource="Header"
HorizontalAlignment="{Binding HorizontalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>-->
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemHotBackground}"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemSelectedBackground}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemHotBorderBrush}"/>
</MultiTrigger>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="BorderThickness" TargetName="Bd" Value="1,0,1,1"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="BorderThickness" TargetName="Bd" Value="1,1,0,1"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="BorderThickness" TargetName="Bd" Value="0,1,1,1"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Top"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-2,-2,-1"/>
<Setter Property="Margin" TargetName="Content" Value="0,0,0,1"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Bottom"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-1,-2,-2"/>
<Setter Property="Margin" TargetName="Content" Value="0,1,0,0"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Left"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-2,-2,-1,-2"/>
<Setter Property="Margin" TargetName="Content" Value="0,0,1,0"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="TabStripPlacement" Value="Right"/>
</MultiTrigger.Conditions>
<Setter Property="Margin" Value="-1,-2,-2,-2"/>
<Setter Property="Margin" TargetName="Content" Value="1,0,0,0"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemDisabledBackground}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource TabItemDisabledBorderBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TabbedExpanderTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Padding" Value="4,4,4,4"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StaticResource TabControlNormalBorderBrush}"/>
<Setter Property="Background" Value="#F9F9F9"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource TabItemStyle1}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<TabPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
<Border x:Name="ContentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="1"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="Grid.Row" TargetName="HeaderPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="ContentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="HeaderPanel" Value="1"/>
<Setter Property="Grid.Column" TargetName="ContentPanel" Value="0"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="HeaderPanel" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TabbedExpanderStyle" TargetType="{x:Type local:TabbedExpander}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TabbedExpander}">
<Grid>
<TabControl Style="{StaticResource TabbedExpanderTabControlStyle}"></TabControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
TabbedExpander.cs:
public class TabbedExpander : TabControl, INotifyPropertyChanged
{
static TabbedExpander()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TabbedExpander), new FrameworkPropertyMetadata(typeof(TabbedExpander)));
}
#region dependency properties
public ObservableCollection<object> ItemsS
{
get { return (ObservableCollection<object>)GetValue(ItemsSProperty); }
set { SetValue(ItemsSProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsS. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSProperty =
DependencyProperty.Register("ItemsS", typeof(ObservableCollection<object>), typeof(TabbedExpander),
new PropertyMetadata(new ObservableCollection<object>(), OnItemsSPropertyChanged));
private static void OnItemsSPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TabbedExpander control = d as TabbedExpander;
var old = e.OldValue as ObservableCollection<object>;
if (old != null)
{
// Unsubscribe from CollectionChanged on the old collection
old.CollectionChanged -= control.ItemsSCollectionChanged;
}
var n = e.NewValue as ObservableCollection<object>;
if (n != null)
{
// Subscribe to CollectionChanged on the new collection
n.CollectionChanged += control.ItemsSCollectionChanged;
}
}
private void ItemsSCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset) base.Items.Clear();
if (e.NewItems != null)
{
foreach (object item in e.NewItems)
{
base.Items.Add(item);
}
}
if (e.OldItems != null)
{
foreach (object item in e.OldItems)
{
base.Items.RemoveAt(e.OldStartingIndex);
}
}
}
#region public
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
public double EXPANDER_OFFSET
{
get { return (double)GetValue(EXPANDER_OFFSETProperty); }
set { SetValue(EXPANDER_OFFSETProperty, value); }
}
public int EXPANDER_MIN_HEIGHT
{
get { return (int)GetValue(EXPANDER_MIN_HEIGHTProperty); }
set { SetValue(EXPANDER_MIN_HEIGHTProperty, value); }
}
public int EXPANDER_MAX_HEIGHT
{
get { return (int)GetValue(EXPANDER_MAX_HEIGHTProperty); }
set { SetValue(EXPANDER_MAX_HEIGHTProperty, value); }
}
public int EXPANDER_NOTEXPANDED_HEIGHT
{
get { return (int)GetValue(EXPANDER_NOTEXPANDED_HEIGHTProperty); }
set { SetValue(EXPANDER_NOTEXPANDED_HEIGHTProperty, value); }
}
public double EXPANDER_EXPANDED_HEIGHT
{
get { return (double)GetValue(EXPANDER_EXPANDED_HEIGHTProperty); }
set { SetValue(EXPANDER_EXPANDED_HEIGHTProperty, value); }
}
#endregion
#region static
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.Register("IsExpanded",
typeof(bool),
typeof(TabbedExpander),
new PropertyMetadata(false, OnIsExpandedChanged));
private static void OnIsExpandedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TabbedExpander control = d as TabbedExpander;
if ((bool)e.NewValue) control.Height = control.EXPANDER_EXPANDED_HEIGHT;
else control.Height = control.EXPANDER_NOTEXPANDED_HEIGHT;
}
public static readonly DependencyProperty EXPANDER_OFFSETProperty =
DependencyProperty.Register("EXPANDER_OFFSET",
typeof(double),
typeof(TabbedExpander),
new PropertyMetadata(102d, OnOffsetChanged));
private static void OnOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
public static readonly DependencyProperty EXPANDER_MIN_HEIGHTProperty =
DependencyProperty.Register("EXPANDER_MIN_HEIGHT",
typeof(int),
typeof(TabbedExpander),
new PropertyMetadata(96, OnMinHeightChanged));
private static void OnMinHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
public static readonly DependencyProperty EXPANDER_MAX_HEIGHTProperty =
DependencyProperty.Register("EXPANDER_MAX_HEIGHT",
typeof(int),
typeof(TabbedExpander),
new PropertyMetadata(400, OnMaxHeightChanged));
private static void OnMaxHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
public static readonly DependencyProperty EXPANDER_NOTEXPANDED_HEIGHTProperty =
DependencyProperty.Register("EXPANDER_NOTEXPANDED_HEIGHT",
typeof(int),
typeof(TabbedExpander),
new PropertyMetadata(30, OnNotExpandedHeightChanged));
private static void OnNotExpandedHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
public static readonly DependencyProperty EXPANDER_EXPANDED_HEIGHTProperty =
DependencyProperty.Register("EXPANDER_EXPANDED_HEIGHT",
typeof(double),
typeof(TabbedExpander),
new PropertyMetadata(100d, OnExpandedHeightChanged));
private static void OnExpandedHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
throw new NotImplementedException();
}
#endregion
#endregion
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void NotifyPropChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.Height = this.EXPANDER_NOTEXPANDED_HEIGHT;
}
}
And a little MainWindow.xaml for testing:
<Window x:Class="PRUEBA_TABBEDEXPANDER.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"
xmlns:local="clr-namespace:PRUEBA_TABBEDEXPANDER"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:TabbedExpander TabStripPlacement="Top">
<TabItem Header="prueba1">
<TextBox Text="prueba1"/>
</TabItem>
</local:TabbedExpander>
</Grid>
That's my last try, my first try was to apply the TabbedExpanderTabControlStyle style directly to the custom control just changing the TargetType, then i've tried without adding the ItemsSProperty, just using the TabControl's inherited ItemsSource with the same syntax in the 'MainWindow.xaml`, and changing the syntax to:
<Grid>
<local:TabbedExpander TabStripPlacement="Top">
<local:TabbedExpander.ItemsS>
<TabItem Header="prueba1">
<TextBox Text="prueba1"/>
</TabItem>
</local:TabbedExpander.ItemsS>
</local:TabbedExpander>
</Grid>
As well as <local:TabbedExpander.Items> trying to use the TabControl's inherited Items.
The only thing that worked was to merge the Generic.xaml as a ResourceDictionary and apply TabbedExpanderTabControlStyle directly to the custom control... which I suppose is NOT the correct way to do it... right? Because i would have to apply the style to every custom control I would add to my application...
I suppose that either I'm doing something wrong applying the style, or using the tabitems collections, but right now I'm out of ideas :(
Any idea?
Normally you'd add your generic style to your window (or other high-level control) resource dictionary like this:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
So that all child controls will inherit it. Have you tried that?
Note, now you've created a custom control you'll want to change the target type of your style and it will apply to all instances of your control in your application. (though looks like you've already thought of that)
If you are using this control elsewhere it might be worth having the style in it's own file, then other projects can include it in their own generic/windows resource directories. This is how wellknown libraries like MahhApps.Metro work, for example with their controls my generic.xaml might look like this:
<ResourceDictionary ...>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
... other imported resource dictionaries
</ResourceDictionary.MergedDictionaries>
...my application specific stuff...
</ResourceDictionary>
I’m trying to create custom Itemscontrol class to display a group of different shapes.
To speed up process, I have reused source code from CodeProject (WPF Diagram Designer - Part 4) where all implementation is done but shapes are added from XAML code. For my purposes, I need to add them from code behind (dynamically) so I bound custom Itemscontrol to list of ObservableCollection. Now, instead of shapes be presented like this:
I get something like this:
Can somebody tell me what I’m doing wrong?
Any help will be appreciated. Thanks in advance.
XAML:
<s:Toolbox x:Key="FlowChartStencils" ItemsSource="{Binding ElementName=MyDesigner, Path=ToolboxDataItems}" ItemTemplate="{StaticResource toolboxItemTemplate}" ItemSize="190,150" SnapsToDevicePixels="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
</s:Toolbox>
<DataTemplate x:Key="toolboxItemTemplate">
<Grid Margin="5,5,5,5">
<Path Style="{StaticResource Process}">
<s:DesignerItem.DragThumbTemplate>
<ControlTemplate>
<Path Style="{StaticResource Process_DragThumb}"/>
</ControlTemplate>
</s:DesignerItem.DragThumbTemplate>
</Path>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text=" {Binding Title}" IsHitTestVisible="False" FontWeight="Bold"/>
</Grid>
</DataTemplate>
<Style x:Key="Process" TargetType="Path" BasedOn="{StaticResource FlowChartItemStyle}">
<Setter Property="Data" Value="M 0,0 H 60 V40 H 0 Z"/>
</Style>
<Style x:Key="Process_DragThumb" TargetType="Path" BasedOn="{StaticResource Process}">
<Setter Property="IsHitTestVisible" Value="true"/>
<Setter Property="Fill" Value="Transparent"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
<Style x:Key="FlowChartItemStyle" TargetType="Path">
<Setter Property="Fill" Value="{StaticResource ItemBrush}"/>
<Setter Property="Stroke" Value="{StaticResource ItemStroke}"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="StrokeLineJoin" Value="Round"/>
<Setter Property="Stretch" Value="Fill"/>
<Setter Property="IsHitTestVisible" Value="False"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
</Style>
<Brush x:Key="ItemStroke">#FFD69436</Brush>
<LinearGradientBrush x:Key="ItemBrush" StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FAFBE9" Offset="0" />
<GradientStop Color="Orange" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
Code behind:
// Implements ItemsControl for ToolboxItems
public class Toolbox : ItemsControl
{
// Defines the ItemHeight and ItemWidth properties of
// the WrapPanel used for this Toolbox
public Size ItemSize
{
get { return itemSize; }
set { itemSize = value; }
}
private Size itemSize = new Size(50, 50);
// Creates or identifies the element that is used to display the given item.
protected override DependencyObject GetContainerForItemOverride()
{
return new ToolboxItem();
}
// Determines if the specified item is (or is eligible to be) its own container.
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ToolboxItem);
}
}
// Represents a selectable item in the Toolbox/>.
public class ToolboxItem : ContentControl
{
…
}
public class ToolboxDataItem : DependencyObject
{
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register( "Title", typeof( string ),
typeof(ToolboxDataItem), new UIPropertyMetadata(""));
public ToolboxDataItem(string title)
{
Title = title;
}
}
public partial class DesignerCanvas : Canvas
{
private ObservableCollection<ToolboxDataItem> toolboxDataItems = new ObservableCollection<ToolboxDataItem>();
public ObservableCollection<ToolboxDataItem> ToolboxDataItems
{
get { return toolboxDataItems; }
}
public DesignerCanvas()
{
ToolboxDataItem toolboxDataItem = new ToolboxDataItem("123");
ToolboxDataItems.Add(toolboxDataItem );
toolboxDataItem = new ToolboxDataItem("456");
ToolboxDataItems.Add(toolboxDataItem );
}
}
MyDesigner:
<s:DesignerCanvas Focusable="true" x:Name="MyDesigner"
Background="{StaticResource WindowBackgroundBrush}" FocusVisualStyle="{x:Null}"
ContextMenu="{StaticResource DesignerCanvasContextMenu}"/>
So I started by trying to get an app going with the code you shared but the styles were all in an incorrect order, so after the styles were set correctly I got arround to an example like this:
<Window.Resources>
<ResourceDictionary>
<Brush x:Key="ItemStroke">#FFD69436</Brush>
<LinearGradientBrush x:Key="ItemBrush" StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Color="#FAFBE9" Offset="0" />
<GradientStop Color="Orange" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="FlowChartItemStyle" TargetType="Path">
<Setter Property="Fill" Value="{StaticResource ItemBrush}"/>
<Setter Property="Stroke" Value="{StaticResource ItemStroke}"/>
<Setter Property="StrokeThickness" Value="1"/>
<Setter Property="StrokeLineJoin" Value="Round"/>
<Setter Property="Stretch" Value="Fill"/>
<Setter Property="IsHitTestVisible" Value="False"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
</Style>
<Style x:Key="Process" TargetType="Path" BasedOn="{StaticResource FlowChartItemStyle}">
<Setter Property="Data" Value="M 0,0 H 60 V40 H 0 Z"/>
</Style>
<DataTemplate x:Key="toolboxItemTemplate">
<Grid Margin="5,5,5,5">
<Path Style="{StaticResource Process}">
</Path>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Title}" IsHitTestVisible="False" FontWeight="Bold"/>
</Grid>
</DataTemplate>
<Style x:Key="Process_DragThumb" TargetType="Path" BasedOn="{StaticResource Process}">
<Setter Property="IsHitTestVisible" Value="true"/>
<Setter Property="Fill" Value="Transparent"/>
<Setter Property="Stroke" Value="Transparent"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ItemsControl Background="Yellow"
ItemsSource="{Binding ToolboxDataItems}"
ItemTemplate="{StaticResource toolboxItemTemplate}"
SnapsToDevicePixels="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
</ItemsControl>
</Grid>
So if you check this example to yours then your problem might be in one of two places:
1) The Text binding of the TextBlock (notice the space before the {):
2) Or the Path inside the ItemsSource binding of the toolbox
<s:Toolbox x:Key="FlowChartStencils" ItemsSource="{Binding ElementName=MyDesigner, Path=ToolboxDataItems}" ItemTemplate="{StaticResource toolboxItemTemplate}" ItemSize="190,150" SnapsToDevicePixels="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
Here try something like ItemsSource="{Binding ToolboxDataItems, ElementName=MyDesigner}"