We have a custom-rendered ListBox which maintains an instance of a StreamGeometry object that is based on its width. The control needs to then share that StreamGeometry instance with all of its items for rendering purposes.
Only way we can think is putting that StreamGeometry instance in the ViewModel for the ListBox, then binding to it in the individual DataTemplates, which just feels dirty to me considering that is a view-only thing and therefore shouldn't be in the ViewModel at all.
Note: We could also just store it via an attached property on the ListBox (or subclass the ListBox), but we're still left with binding of a view-only thing which seems wrong to me for something like this.
Any thoughts?
You can make the StreamGeometry a dependency property on your custom listview, then refer to it through Binding MyGeometry, RelativeSource={RelativeSource AncestorType=ListView}.
This way, there is no ViewModel involved.
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- default lsitviewitem style except for added path -->
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<!-- added path-->
<Path Stretch="Uniform" Stroke="DarkBlue" Fill="DarkOrchid" Data="{Binding MyGeometry, RelativeSource={RelativeSource AncestorType=ListView}}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid >
<local:CustomListView Margin="20" >
<local:CustomListView.Items>
<ListViewItem Content="ListViewItem1" />
<ListViewItem Content="ListViewItem2" />
<ListViewItem Content="ListViewItem3" />
</local:CustomListView.Items>
</local:CustomListView>
</Grid>
</Window>
CustomListView:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApplication1
{
public class CustomListView : ListView
{
public StreamGeometry MyGeometry { get { return (StreamGeometry)GetValue(MyGeometryProperty); } set { SetValue(MyGeometryProperty, value); } }
public static readonly DependencyProperty MyGeometryProperty = DependencyProperty.Register("MyGeometry", typeof(StreamGeometry), typeof(CustomListView), new PropertyMetadata(null));
protected override void OnRender(DrawingContext drawingContext)
{
StreamGeometry geometry = new StreamGeometry(); // directly opening MyGeometry results in "must have isfrozen set to false to modify" error
using (StreamGeometryContext context = geometry.Open())
{
Point p1 = new Point(this.ActualWidth * (2d / 5d), 0);
Point p2 = new Point(this.ActualWidth / 2d, -10);
Point p3 = new Point(this.ActualWidth * (3d / 5d), 0);
context.BeginFigure(p1, true, true);
List<Point> points = new List<Point>() { p2, p3 };
context.PolyLineTo(points, true, true);
}
drawingContext.DrawGeometry(Brushes.DarkOrchid, new Pen(Brushes.DarkBlue, 1), geometry);
this.MyGeometry = geometry;
base.OnRender(drawingContext);
}
}
}
Related
I have a TreeView with heterogeneuos node types. Each node type is configuered in XAML using a HierarchicalDataTemplate.
Some of the nodes have a ContextMenu, which is dependent on the type of the node. The ContextMenus are defined as static resources in XAML and attached to a DockPanel in the HierarchicalDataTemplate.
In addition, I am using the ControlTemplate for a TreeViewItem described by bendewey in the following StackOverflow question https://stackoverflow.com/a/672123/1626109. This ControlTemplate is defined so that the complete TreeViewItem is highlighted when it is selected.
A left click on any part of the line, selects the item.
In contrast, the context menus, which are defined in the HierarchicalDataTemplate, only work on the right hand part of the line.
I am looking for a way to configure the ContextMenus so that they are also available on the complete line.
This seems to require attaching the context menu to an element in the ControlTemplate, with a TemplateBinding to something defined in the HierarchicalDataTemplate, but I can't figure out how to do it.
By the way, the solution explorer in Visual Studio has exactly this behavior. The context menu is dependent on the node type, and it is available on the complete item, including to the left of the expand/collapse button.
(Edited) You need to get rid of the two columns of the Grid in TreeViewItem Style:
<Window.Resources>
<local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid Width="15" Height="13"
Background="Transparent">
<Path x:Name="ExpandPath"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="1,1,1,1"
Fill="{StaticResource GlyphBrush}"
Data="M 4 0 L 8 4 L 4 8 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Data"
TargetName="ExpandPath"
Value="M 0 4 L 8 4 L 4 8 Z"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Margin="0,0,0,0"
StrokeThickness="5"
Stroke="Black"
StrokeDashArray="1 2"
Opacity="0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type TreeViewItem}"
TargetType="{x:Type TreeViewItem}">
<Setter Property="Background"
Value="Transparent"/>
<Setter Property="HorizontalContentAlignment"
Value="{Binding Path=HorizontalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment"
Value="{Binding Path=VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding"
Value="1,0,0,0"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle"
Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<ControlTemplate.Resources>
<local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
</ControlTemplate.Resources>
<StackPanel>
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<Grid>
<ToggleButton Panel.ZIndex="2" x:Name="Expander"
HorizontalAlignment="Left"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"
Margin="{Binding Converter={StaticResource lengthConverter}, ConverterParameter=0,
RelativeSource={RelativeSource TemplatedParent}}"/>
<ContentPresenter x:Name="PART_Header" ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
</ContentPresenter>
</Grid>
</Border>
<ItemsPresenter x:Name="ItemsHost" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded"
Value="false">
<Setter TargetName="ItemsHost"
Property="Visibility"
Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems"
Value="false">
<Setter TargetName="Expander"
Property="Visibility"
Value="Hidden"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Width"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinWidth"
Value="75"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasHeader"
Value="false"/>
<Condition Property="Height"
Value="Auto"/>
</MultiTrigger.Conditions>
<Setter TargetName="PART_Header"
Property="MinHeight"
Value="19"/>
</MultiTrigger>
<Trigger Property="IsSelected"
Value="true">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected"
Value="true"/>
<Condition Property="IsSelectionActive"
Value="false"/>
</MultiTrigger.Conditions>
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
However, this will distort the Header-Items arrangements. Therefore, you need to adjust the Margin in the HierarchicalDataTemplate part:
<TreeView Margin="50" HorizontalContentAlignment="Stretch" DataContext="{Binding}" ItemsSource="{Binding Models}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Model}" ItemsSource="{Binding Models}">
<Border Background="Transparent">
<TextBlock Margin="{Binding Converter={StaticResource lengthConverter}, ConverterParameter=1,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TreeViewItem}}"
Text="{Binding Name}" />
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="dddd"/>
</ContextMenu>
</Border.ContextMenu>
</Border>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Note that you should adjusted the converter to take the required extra margin into account:
public class LeftMarginMultiplierConverter : IValueConverter
{
public double Length { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var item = value as TreeViewItem;
if (item == null)
return new Thickness(0);
int extra = int.Parse(parameter.ToString());
var t = item.GetDepth();
return new Thickness(Length * (item.GetDepth() + extra), 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
If converter parameter is 1, it will add one point to the depth of the item. I use it for the Header part.
Another approach might be to add a DataTemplate to the ContentPresenter of the TreeViewItem Style. While I don't know how you can bind the ContextMenu, I prefer this one.
May be I'm missing something, but I think there should be a nice way to add items to ListBoxes whose contents are bound to a data source, ether than having separate button near that ListBox to trigger add_new_item() from the code.
I guess I can figure out how to add a delete button near currently selected button through style. I'm not yet sure which parameter will trigger this. But I'm more concerned with adding items like shown in the image
My Awesome Custom Listbox
I'm not yet advanced into styling. What I do is, I take somebody's style and take it apart, then create what I actually need. I don't even know where to look at to see all styling techniques like looking at a class reference.
I had an idea of adding a button hanging in a corner of a ListBox or ComboBox, but how should I declare a new Event for it so I could assign unique functions to then other than one for everything.
I'm not asking for a complete solution, just a hint of what to do. I'll post an answer when I'll figure it out.
And after a year I've figured these things out.
Just bind to an ObservableCollection and add the value
If that is not what you are looking for please ask a more specific question
You have to create the button yourself and assign a command (MVVM) or click (code behind) to it. Your listbox itemssource should be observablecollection which will notify the UI anytime you add or remove an item from the listbox. If you know that you want a listbox with a button in many places, then you can create a usercontrol out of these two controls. If you want a button inside the lixtbox or combobox then you have to modify the template of these controls, slightly more complicated.
With time I figured these things (UVMCommand is my ICommand interface implementation, it is easier for me to use it this way):
public class Game : Notifiable {
public Game() {
Players = new ObservableCollection<Player>();
AddNewPlayer = new UVMCommand("AddNewPlayer", p => {
var pl = new Player() { Text = "New Player", IsSelected = true, IsEdited = true };
pl.RemoveThis = new UVMCommand("RemoveThis", pp => Players.Remove(pl));
Players.Add(pl);
});
}
private ObservableCollection<Player> _players;
public ObservableCollection<Player> Players { get { return _players; } set { _players = value; OnPropertyChanged("Players"); } }
private UVMCommand _addNewPlayer;
public UVMCommand AddNewPlayer { get { return _addNewPlayer; } set { _addNewPlayer = value; OnPropertyChanged("AddNewPlayer"); } }
}
public class Player : Notifiable {
public Player() {}
private UVMCommand _removeThis;
public UVMCommand RemoveThis { get { return _removeThis; } set { _removeThis = value; OnPropertyChanged("RemoveThis"); } }
}
public class Notifiable : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And then making a user control in code-behind declare:
public partial class EditableListBox : System.Windows.Controls.ListBox, INotifyPropertyChanged {
public EditableListBox() {
InitializeComponent();
//var s = FindResource("EditableListBoxStyle") as Style;
//Style = s;
}
[Category("Common")]
public UVMCommand AddItem {
get { return (UVMCommand)GetValue(AddItemProperty); }
set {
SetValue(AddItemProperty, value);
}
}
// Using a DependencyProperty as the backing store for AddItem. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register("AddItem", typeof(UVMCommand), typeof(EditableListBox),
new PropertyMetadata(
new UVMCommand("Default Command", p => { Debug.WriteLine("EditableListBox.AddITem not defined"); })));
[Category("Layout")]
public Orientation Orientation {
get { return (Orientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
// Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(EditableListBox), new PropertyMetadata(Orientation.Vertical));
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And in XAML I define a Style with an add button in a template at the end of a list and a delete button in an item template:
<ListBox x:Name="listBox" x:Class="MTCQuest.CustomControls.EditableListBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MTCQuest"
xmlns:zq="clr-namespace:MTCQuest.ViewModel.zQuest;assembly=MTCQuest.ViewModel"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:ccon="clr-namespace:MTCQuest.CustomControls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Style="{DynamicResource EditableListBoxStyle}"
ItemContainerStyle="{DynamicResource EditableListBoxItemStyle}" d:DataContext="{DynamicResource TestTheme}" ItemsSource="{Binding Questions}">
<ListBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/StyleRes.xaml"/>
<ResourceDictionary Source="../Resources/QuestSpacificControlStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ccon:BindingExists x:Key="BindingExists"/>
<ccon:ColorToSolidColorBrushConverter x:Key="ColorToSolidColorBrushConverter"/>
<zq:Theme x:Key="TestTheme">
<zq:Theme.Questions>
<zq:Question IsEdited="True" IsSelected="True" Text="Some Question" Color="#FF2E00FF"/>
<zq:Question Text="Another Question"/>
</zq:Theme.Questions>
</zq:Theme>
<Style x:Key="EditableListBoxStyle" TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource Pallete.Divider}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{StaticResource Pallete.PrimaryText}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="True"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ItemsPanel" Value="{DynamicResource OrientedItemsPanelTemplate}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<ScrollViewer Background="{TemplateBinding Background}" SnapsToDevicePixels="true" Focusable="false" Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<StackPanel Orientation="{Binding Orientation, ElementName=listBox}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<ccon:zButton x:Name="AddButton" Command="{Binding AddItem, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalContentAlignment="Left"
Background="Transparent" Foreground="{DynamicResource Pallete.PrimaryText}"
BorderThickness="0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Width="16" Height="16" Margin="5,0"
Fill="{DynamicResource Pallete.Accent}"
OpacityMask="{DynamicResource Icon_PlusSign}"/>
<TextBlock Text="Add" Foreground="{StaticResource Pallete.PrimaryText}"/>
</StackPanel>
</ccon:zButton>
</StackPanel>
</ScrollViewer>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="AddButton" Property="Visibility" Value="Visible"/>
</Trigger>
<DataTrigger Binding="{Binding}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Effect">
<Setter.Value>
<ccon:DesaturateEffect DesaturationFactor=".25"/>
</Setter.Value>
</Setter>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="EditableListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Margin" Value="5,0"/>
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<Grid Height="33" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="TB" Text="{Binding Text}"
VerticalContentAlignment="Center"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="5, 0" BorderThickness="0" GotFocus="TB_GotFocus"
Visibility="Collapsed"/>
<Label x:Name="Lb" Content="{Binding Text}"
VerticalContentAlignment="Center"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Padding="5, 0" BorderThickness="0" Margin="2,1,0,0" />
<xctk:ColorPicker x:Name="CB" Grid.Column="1" Width="48" Visibility="Collapsed"
SelectedColor="{Binding Color}"
ShowRecentColors="True" ShowDropDownButton="False" ShowStandardColors="False"/>
<!--<ccon:zButton x:Name="CB" OpacityMask="{DynamicResource Icon_Edit}"
Width="16" Height="16" Panel.ZIndex="19"
Background="{Binding Color, Converter={StaticResource ColorToSolidColorBrushConverter}}" BorderThickness="0"
Visibility="Collapsed" Margin="2" Grid.Column="1"/>-->
<ccon:zButton x:Name="DB" OpacityMask="{DynamicResource Icon_MinusSign}"
Command="{Binding RemoveThis}"
Width="16" Height="16" Background="#FFD12929" BorderThickness="0"
Visibility="Collapsed" Margin="2" Grid.Column="2"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!--<DataTrigger Binding="{Binding IsEdited}" Value="true">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=TB}" />
</DataTrigger>-->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="#1F26A0DA"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="#A826A0DA"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="#3DDADADA"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="#FFDADADA"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="#3D26A0DA"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="#FF26A0DA"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Visibility" TargetName="Lb" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="TB" Value="Visible"/>
<Setter Property="Visibility" TargetName="DB" Value="Visible"/>
<Setter Property="Visibility" TargetName="CB" Value="Visible"/>
</MultiTrigger>
<DataTrigger Binding="{Binding Color, Converter={StaticResource BindingExists}, FallbackValue=false}" Value="false">
<Setter Property="Visibility" TargetName="CB" Value="Collapsed"/>
</DataTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ItemsPanelTemplate x:Key="OrientedItemsPanelTemplate">
<VirtualizingStackPanel IsItemsHost="True"
Orientation="{Binding Orientation, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ccon:EditableListBox}}}"/>
</ItemsPanelTemplate>
</ResourceDictionary>
</ListBox.Resources>
And now it's just a:
<ccon:EditableListBox ItemsSource="{Binding Players}" AddItem="{Binding AddNewPlayer}" Orientation="Horizontal" HorizontalContentAlignment="Center" ScrollViewer.VerticalScrollBarVisibility="Disabled"/>
And Hell Yeagh! I can make it either vertical or horizontal.
I can't belive that when I was just learning WPF I've tried to do something that takes so many things to know and be able to write them (Commands, Templates, Triggers, DependencyProperties, INotifyPropertyChanged and many more), that I had no idea even existed. Just a yaer ago :)
I have custom AutoCompleteTextBox class which inherits from TextBox. Everything works just fine, except showing validation error, i.e. red border around the control.
Here is it's style from my Generic.xaml.
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.ControlDarkBrushKey}}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="Padding" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AutoCompleteTextBox}">
<Grid>
<Border x:Name="PART_Border" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<Popup x:Name="PART_Popup" StaysOpen="False">
<ListBox x:Name="PART_ListBox" HorizontalContentAlignment="Stretch" />
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="PART_Border" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
<Trigger Property="IsReadOnly" Value="true">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I've checked with Snoop utility and found no red border. I've reviewed WPF's Generic.xaml Aero.Normal.xaml and, to be honest, have no idea what draws that red validation border around invalid textbox.
I know similar questions were assed before, but I reviwed all answers and non of them helped.
You will find the default ErrorTemplate in validation.cs source code:
public static class Validation
{
/// <summary>
/// Template used to generate validation error feedback on the AdornerLayer. Default
/// Template is:
/// <code>
/// <border borderthickness="1" borderbrush="Red">
/// <adornedelementplaceholder>
/// </adornedelementplaceholder></border>
/// </code>
/// </summary>
public static readonly DependencyProperty ErrorTemplateProperty =
DependencyProperty.RegisterAttached("ErrorTemplate",
typeof(ControlTemplate), typeof(Validation),
new FrameworkPropertyMetadata(
CreateDefaultErrorTemplate(),
FrameworkPropertyMetadataOptions.NotDataBindable,
new PropertyChangedCallback(OnErrorTemplateChanged)));
private static ControlTemplate CreateDefaultErrorTemplate()
{
ControlTemplate defaultTemplate = new ControlTemplate(typeof(Control));
//<border borderthickness="1" borderbrush="Red">
// <adornedelementplaceholder>
//</adornedelementplaceholder></border>
FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border), "Border");
border.SetValue(Border.BorderBrushProperty, Brushes.Red);
border.SetValue(Border.BorderThicknessProperty, new Thickness(1));
FrameworkElementFactory adornedElementPlaceHolder = new FrameworkElementFactory(typeof(AdornedElementPlaceholder), "Placeholder");
border.AppendChild(adornedElementPlaceHolder);
defaultTemplate.VisualTree = border;
defaultTemplate.Seal();
return defaultTemplate;
}
...
}
If you want to remove it just set Validation.ErrorTemplate Attached Property to Null.
<TextBox Validation.ErrorTemplate="{x:Null}" />
I am new to using styles, resources and templates in WPF. What I need to do is override the ToggleButton +/- in the TreeView to be an image, with a different image for each TreeViewItem root Node. For Instance, I would want an image of a car for the "Car" node, and an image of an airplane for the "Plane" node. I have a colorful and a grayscale image of each (for expanded/collapsed).
I've found styles to override the treeview and get the image set for the toggle button, but I am not sure the best way to style each item differently.
the style code for an item is quite long, so I'm pretty sure there's a better way than copy/pasting the full style just to change the source property.
Could someone please point me in the right direction on the best way to do this?
Thank you.
Here's the style I've been playing with, which was copied from a different post and altered for my images.
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Width="16" Height="16" Background="Transparent">
<Border Width="16" Height="16" SnapsToDevicePixels="true" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" BorderThickness="1">
<Image x:Name="ExpandImg" Width="16" Height="16" Source="/MyApp;component/Images/Icons/Grayscale/car.ico" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="ExpandImg" Value="/MyApp;component/Images/Icons/Color/car.ico"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TreeViewItemStyle1" TargetType="{x:Type TreeViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="1,0,0,0"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/>
<Border x:Name="Bd" SnapsToDevicePixels="true" Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ContentPresenter x:Name="PART_Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Well, unfortunately, that's the way WPF works. If you want to change the Style of the default controls, you're most likely bound to work with a bunch of Style declarations (as you've posted above) and make some changes on it.
Using Expression Blend makes it a bit easier since you'll have a UI designer that you can use to easily modify your styles. But behind the scenes, it still has the Style declarations. As an example, here's what I got doing your requirements using Blend:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
<Image x:Name="PART_Image" Source="Image1.jpg"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False"/>
<Trigger Property="IsMouseOver" Value="True"/>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" TargetName="PART_Image" Value="Image2.jpg"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="1,0,0,0"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<TreeView HorizontalAlignment="Left" Margin="8,8,0,105" Width="196">
<TreeViewItem Header="Item1">
<TreeViewItem Header="Item2"/>
</TreeViewItem>
</TreeView>
</Grid>
</Window>
Anyway, I hope this helps.
EDIT:
Ok, I noticed that the code I posted is much like what you posted in your example. But I assure you, I used expression blend to make the modifications above. And I guess that just proves the point: you're bound to work with a bunch of Style declarations, whether using Blend or not. =)
EDIT2:
Window.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<HierarchicalDataTemplate x:Key="treeTemplate"
ItemsSource="{Binding SubItems}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Width" Value="16"/>
<Setter Property="Height" Value="16"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="Transparent" Height="16" Width="16">
<Image x:Name="PART_Image" Source="{Binding ImageSource}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="customTreeItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="1,0,0,0"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<TreeView x:Name="tree"
ItemTemplate="{StaticResource treeTemplate}"
ItemContainerStyle="{StaticResource customTreeItemStyle}"/>
</Window>
Code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private List<ViewModel> items = new List<ViewModel>();
public MainWindow()
{
InitializeComponent();
ViewModelImpl1 vm1 = new ViewModelImpl1() { Name = "VM1" };
vm1.SubItems.Add("SubItem1");
vm1.SubItems.Add("SubItem2");
vm1.SubItems.Add("SubItem3");
ViewModelImpl1 vm2 = new ViewModelImpl1() { Name = "VM2" };
vm2.SubItems.Add("SubItem1");
vm2.SubItems.Add("SubItem2");
vm2.SubItems.Add("SubItem3");
vm2.SubItems.Add("SubItem4");
ViewModelImpl2 vm3 = new ViewModelImpl2() { Name = "VM3" };
vm3.SubItems.Add("SubItem1");
items.Add(vm1);
items.Add(vm2);
items.Add(vm3);
tree.ItemsSource = items;
}
}
}
ViewModels
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication2
{
public abstract class ViewModel : INotifyPropertyChanged
{
private List<string> subItems = new List<string>();
public List<string> SubItems
{
get { return this.subItems; }
}
private string name;
public string Name
{
get { return this.name; }
set
{
this.name = value;
this.OnPropertyChanged("Name");
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
this.isExpanded = value;
this.OnPropertyChanged("IsExpanded");
UpdateImageSource();
}
}
private string imageSource;
public string ImageSource
{
get
{
if (this.imageSource == null)
UpdateImageSource();
return this.imageSource;
}
protected set
{
this.imageSource = value;
this.OnPropertyChanged("ImageSource");
}
}
protected abstract void UpdateImageSource();
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ViewModelImpl1 : ViewModel
{
protected override void UpdateImageSource()
{
if (this.IsExpanded)
this.ImageSource = "pack://application:,,,/Images/bell1.png";
else
this.ImageSource = "pack://application:,,,/Images/bell2.png";
}
}
public class ViewModelImpl2 : ViewModel
{
protected override void UpdateImageSource()
{
if (this.IsExpanded)
this.ImageSource = "pack://application:,,,/Images/star1.png";
else
this.ImageSource = "pack://application:,,,/Images/star2.png";
}
}
}
How would I create a common window look in WPF? I'm not talking about just styling the window, I'm mean having a window that has a border, grid, and some other things in it.
Thanks.
You can create a ControlTemplate for the window. Here is a pretty basic example that has some controls and triggers. You can easily add more elements to make it fit your needs.
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type Window}">
<Border x:Name="WindowBorder" Style="{DynamicResource WindowBorderStyle}">
<Grid>
<Border Margin="4,4,4,4" Padding="0,0,0,0" x:Name="MarginBorder">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
<ResizeGrip Visibility="Collapsed" IsTabStop="false" HorizontalAlignment="Right" x:Name="WindowResizeGrip"
VerticalAlignment="Bottom" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ResizeMode" Value="CanResizeWithGrip"/>
<Condition Property="WindowState" Value="Normal"/>
</MultiTrigger.Conditions>
<Setter Property="Visibility" TargetName="WindowResizeGrip" Value="Visible"/>
<Setter Property="Margin" TargetName="MarginBorder" Value="4,4,4,20" />
</MultiTrigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="CornerRadius" TargetName="WindowBorder" Value="0,0,0,0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
You can use this ControlTemplate by setting the template property of the Window:
Template="{StaticResource MyWindowTemplate}"
You will want to use this in conjunction with a style like this:
<Style x:Key="MyWindowStyle" TargetType="{x:Type Window}">
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="WindowStyle" Value="SingleBorderWindow" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="ShowInTaskbar" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border>
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And set the style on your Window like this:
Style="{StaticResource MyWindowStyle}"
What I ended up doing was creating a base class that created the UI code that I wanted in every window. This allows me to set events for controls and have the event subscription in the base class. If there is a better way of doing this, like using xaml, I'd like to know.
public WindowBase()
{
Initialized += WindowBase_Initialized;
}
private void WindowBase_Initialized( object sender, EventArgs e )
{
Border border = new Border();
border.SetResourceReference( Control.StyleProperty, "WindowBorder" );
border.Child = new ContentPresenter { Content = this.Content};
this.Content = border;
}