How to draw a WrapPanel in a specific way in XAML / WPF - wpf

I have a listbox of elements, and I set the ItemPanel as a WrapPanel as I want my Panel to wrap every 4 elements.
I used the following:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
Now, I want thay my items will be displayed as follows:
When only 4 Items or less:
When more than 4 Items:
I need that the cornerRadius will be applied on the four edges whether it's only one row or not.

This turned out to be a bit painful. If you want the corner radius and the border thickness to be properly parameterized, that'll take more work: You'd need value converters to create or modify CornerRadius and Thickness values as needed.
Another approach would have been to omit the triggers and write two big multiconverters, for Thickness and CornerRadius, that take the same parameters as the one I wrote, plus the "default" border thickness and corner radius values, and then return Thickness and CornerRadius respectively.
<Style TargetType="ListBox" x:Key="GridLineListBox">
<Style.Resources>
<local:CellTypeConverter x:Key="CellTypeConverter" />
</Style.Resources>
<Setter Property="AlternationCount" Value="{x:Static sys:Int32.MaxValue}" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="UseLayoutRounding" Value="True" />
<Setter Property="BorderBrush" Value="SteelBlue" />
<Setter Property="local:GridLineListBox.ColumnCount" Value="6" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="16"
ClipToBounds="True"
>
<ItemsPresenter Margin="-1" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid
Columns="{Binding (local:GridLineListBox.ColumnCount), RelativeSource={RelativeSource AncestorType=ListBox}}"
IsItemsHost="True"
/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<!-- Put this in an attached property so we don't have to copy/paste the whole binding for each trigger -->
<Setter Property="local:GridLineListBox.CellType">
<Setter.Value>
<MultiBinding Converter="{StaticResource CellTypeConverter}">
<Binding Path="Items.Count" RelativeSource="{RelativeSource AncestorType=ListBox}" />
<Binding Path="(ItemsControl.AlternationIndex)" RelativeSource="{RelativeSource Self}" />
<Binding Path="(local:GridLineListBox.ColumnCount)" RelativeSource="{RelativeSource AncestorType=ListBox}" />
</MultiBinding>
</Setter.Value>
</Setter>
<Setter Property="Margin" Value="0" />
<Setter Property="BorderBrush" Value="{Binding BorderBrush, RelativeSource={RelativeSource AncestorType=ListBox}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<!--
Negative right/bottom margin because I'm getting a gap with
SnapToDevicePixels and I'm too lazy to figure out the real reason.
-->
<Border
x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0,0,2,2"
Background="{TemplateBinding Background}"
ClipToBounds="True"
Margin="-1"
>
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="local:GridLineListBox.CellType" Value="TopLeft">
<Setter TargetName="Bd" Property="CornerRadius" Value="16,0,0,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="TopRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,2" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,16,0,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="Right">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,2" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="BottomRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,0,16,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="Bottom">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="BottomLeft">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,0,0,16" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="SingleRowLeft">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,2,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="16,0,0,16" />
</Trigger>
<Trigger Property="local:GridLineListBox.CellType" Value="SingleRowRight">
<Setter TargetName="Bd" Property="BorderThickness" Value="0,0,0,0" />
<Setter TargetName="Bd" Property="CornerRadius" Value="0,16,16,0" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
</MultiTrigger>
<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>
</Setter.Value>
</Setter>
</Style>
C#
public enum CellType {
TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left,
SingleRowLeft, SingleRowRight, Inner
}
public class CellTypeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var itemsCount = System.Convert.ToInt32(values[0]);
var itemIndex = System.Convert.ToInt32(values[1]);
var columnCount = System.Convert.ToInt32(values[2]);
int rowCount = itemsCount / columnCount;
if (itemsCount % columnCount > 0)
++rowCount;
int lowerRightIndex = (rowCount * columnCount) - 1;
int lowerLeftIndex = (rowCount - 1) * columnCount;
if (itemIndex == 0)
{
return (rowCount == 1) ? CellType.SingleRowLeft : CellType.TopLeft;
}
else if (itemIndex == columnCount - 1)
{
return (rowCount == 1) ? CellType.SingleRowRight : CellType.TopRight;
}
else if (itemIndex < columnCount)
return CellType.Top;
else if (itemIndex == lowerRightIndex)
return CellType.BottomRight;
else if ((itemIndex + 1) % columnCount == 0)
return CellType.Right;
else if (itemIndex == lowerLeftIndex)
return CellType.BottomLeft;
else if (itemIndex > lowerLeftIndex)
return CellType.Bottom;
else if (itemIndex % columnCount == 0)
return CellType.Left;
return CellType.Inner;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public static class GridLineListBox
{
public static CellType GetCellType(ListBoxItem obj)
{
return (CellType)obj.GetValue(CellTypeProperty);
}
public static void SetCellType(ListBoxItem obj, CellType value)
{
obj.SetValue(CellTypeProperty, value);
}
public static readonly DependencyProperty CellTypeProperty =
DependencyProperty.RegisterAttached("CellType", typeof(CellType), typeof(GridLineListBox),
new PropertyMetadata((CellType)(-1)));
public static int GetColumnCount(ListBox obj)
{
return (int)obj.GetValue(ColumnCountProperty);
}
public static void SetColumnCount(ListBox obj, int value)
{
obj.SetValue(ColumnCountProperty, value);
}
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.RegisterAttached("ColumnCount", typeof(int), typeof(GridLineListBox),
new PropertyMetadata(0, ColumnCount_PropertyChanged));
private static void ColumnCount_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as ListBox;
}
}
Example:
<ListBox
ItemsSource="{Binding CollectionOfStrings}"
Style="{StaticResource GridLineListBox}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label
Content="{Binding}"
HorizontalAlignment="Center"
/>
<Label
Content="{Binding (local:GridLineListBox.CellType), RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
HorizontalAlignment="Center"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

Dynamic resource merging not applying styles to first control

I tried to apply new style for my custom controls by merging the style resources to the application resources while adding the control to UI, but new styles are not applied to the controls for the first time.
Sample control
CustomTextBoxExt.cs
public class CustomTextBoxExt : TextBox
{
static CustomTextBoxExt()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomTextBoxExt), new FrameworkPropertyMetadata(typeof(CustomTextBoxExt)));
}
}
default style
Generic.xaml
<Style x:Key="TextBoxExtStyle" TargetType="{x:Type local:CustomTextBoxExt}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="AllowDrop" Value="True" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomTextBoxExt}">
<Border
x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="True">
<ScrollViewer
x:Name="PART_ContentHost"
Focusable="False"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Opacity" Value="0.56" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="#FF7EB4EA" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="#FF569DE5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="True" />
<Condition Property="IsSelectionActive" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}" />
</MultiTrigger>
</Style.Triggers>
</Style>
<Style BasedOn="{StaticResource TextBoxExtStyle}" TargetType="{x:Type local:CustomTextBoxExt}" />
Custom theme
TextBoxExtStyle.xaml
<Style x:Key="MaterialTextBoxExtStyle" TargetType="{x:Type local:CustomTextBoxExt}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Foreground" Value="#DD000000" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="#FF9E9E9E" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="SelectionBrush" Value="#FF0279FF" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="AllowDrop" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="CaretBrush" Value="#DD000000" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomTextBoxExt}">
<Grid>
<Border
x:Name="border"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<ScrollViewer
x:Name="PART_ContentHost"
Background="Transparent"
Focusable="False"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="border" Property="BorderBrush" Value="#FF757575" />
<Setter TargetName="border" Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#DD000000" />
<Setter Property="CaretBrush" Value="#DD000000" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter TargetName="border" Property="BorderBrush" Value="#FF0279FF" />
<Setter Property="BorderThickness" Value="0,0,0,2" />
<Setter Property="Padding" Value="0,0,0,-1" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="border" Property="Background" Value="Transparent" />
<Setter TargetName="border" Property="BorderBrush" Value="#FFE0E0E0" />
<Setter Property="Foreground" Value="#60000000" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style BasedOn="{StaticResource MaterialTextBoxExtStyle}" TargetType="{x:Type local:CustomTextBoxExt}" />
and using attached property, trying to change style from default style,
public class SkinExt
{
public static string GetTheme(DependencyObject obj)
{
return (string)obj.GetValue(ThemeProperty);
}
public static void SetTheme(DependencyObject obj, string value)
{
obj.SetValue(ThemeProperty, value);
}
// Using a DependencyProperty as the backing store for Theme. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ThemeProperty =
DependencyProperty.RegisterAttached("Theme", typeof(string), typeof(SkinExt), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnVisualStyleChanged)));
private static void OnVisualStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != e.OldValue && !string.IsNullOrEmpty(e.NewValue.ToString()) /*&& d is FrameworkElement && (d as FrameworkElement).IsLoaded*/)
{
SkinExt.ApplyTheme(d, e.NewValue.ToString());
}
}
internal static void ApplyTheme(DependencyObject obj, string style)
{
Type itemType = obj.GetType();
List<string> styles = GetDictionaries(obj.GetType().Name.ToString(), style);
if (styles != null && styles.Count > 0)
{
foreach (var path in styles)
{
var rdict = new ResourceDictionary() { Source = new Uri(path, UriKind.RelativeOrAbsolute) };
bool alreadyExist = false;
foreach (var dictionaryFiles in Application.Current.Resources.MergedDictionaries)
{
if (dictionaryFiles.Source.OriginalString.Contains(path))
{
alreadyExist = true;
break;
}
}
if (!alreadyExist)
{
Application.Current.Resources.MergedDictionaries.Add(rdict);
Console.WriteLine(path);
}
}
}
}
internal static List<string> GetDictionaries(String type, string style)
{
List<string> styles = new List<string>();
#region Switch
switch (type)
{
case "CustomTextBoxExt":
styles.Add("/TextBoxExt;component/TextBoxExt/TextBoxExtStyle.xaml");
break;
case "ButtonExt":
styles.Add("/TextBoxExt;component/ButtonExt/ButtonExtStyle.xaml");
break;
case "Label":
styles.Add("/TextBoxExt;component/LabelStyle.xaml");
break;
}
# endregion
return styles;
}
}
setting
local:SkinExt.Theme="Material"
in mainwindow/grid works as expected when children are added directly. But, when using below lazyextension style is not working.
public static class LazyLoadExtensions
{
public static LazyUIElementCollection GetLazyChildrens(DependencyObject obj)
{
return (LazyUIElementCollection)obj.GetValue(LazyChildrensProperty);
}
public static void SetLazyChildrens(DependencyObject obj, LazyUIElementCollection value)
{
obj.SetValue(LazyChildrensProperty, value);
}
public static readonly DependencyProperty LazyChildrensProperty =
DependencyProperty.RegisterAttached("LazyChildrens", typeof(LazyUIElementCollection), typeof(LazyLoadExtensions), new PropertyMetadata(OnLazyChildrensChanged));
private static void OnLazyChildrensChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var wrapPanel = d as WrapPanel;
var childrens = LazyLoadExtensions.GetLazyChildrens(wrapPanel);
for (int i = 0; i < childrens.Count; i++)
{
var child = childrens[i];
wrapPanel.Children.Add(child);
}
}
}
public class LazyUIElementCollection : List<UIElement>
{
public LazyUIElementCollection()
{
}
}
Works
<Grid local:SkinExt.Theme="Material">
<WrapPanel x:Name="wrapPanel">
<!--<local:LazyLoadExtensions.LazyChildrens>-->
<!--<local:LazyUIElementCollection>-->
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="MS Label" />
<Label
Width="200"
Height="25"
Content="Material" />
</StackPanel>
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="Custom TextBox" />
<local:CustomTextBoxExt
Width="200"
Height="25"
Text="Material" />
</StackPanel>
<!--</local:LazyUIElementCollection>-->
<!--</local:LazyLoadExtensions.LazyChildrens>-->
</WrapPanel>
</Grid>
Not Working
<Grid local:SkinExt.Theme="Material">
<WrapPanel x:Name="wrapPanel">
<local:LazyLoadExtensions.LazyChildrens>
<local:LazyUIElementCollection>
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="MS Label" />
<Label
Width="200"
Height="25"
Content="Material" />
</StackPanel>
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="Custom TextBox" />
<local:CustomTextBoxExt
Width="200"
Height="25"
Text="Material" />
</StackPanel>
</local:LazyUIElementCollection>
</local:LazyLoadExtensions.LazyChildrens>
</WrapPanel>
</Grid>
Works for second item
Style applying correctly for second customtextboxext
<Grid local:SkinExt.Theme="Material">
<WrapPanel x:Name="wrapPanel">
<local:LazyLoadExtensions.LazyChildrens>
<local:LazyUIElementCollection>
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="MS Label" />
<Label
Width="200"
Height="25"
Content="Material" />
</StackPanel>
<StackPanel Margin="10">
<TextBlock Margin="0,0,0,8" Text="Custom TextBox" />
<local:CustomTextBoxExt
Width="200"
Height="25"
Text="Material" />
<local:CustomTextBoxExt
Width="200"
Height="25"
Text="Material" />
</StackPanel>
</local:LazyUIElementCollection>
</local:LazyLoadExtensions.LazyChildrens>
</WrapPanel>
</Grid>
Reproducible sample : https://drive.google.com/open?id=1iB9sY90T7aRaaRTzVc1EvE2qFU13fHG7
Check the above sample and let me know your ideas
cool project !
I'm not exactly sure why the problem occurs but it seems like a timing problem.
I found the following workaround:
In LazyLoadExtensions:
private static void OnLazyChildrensChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var wrapPanel = d as WrapPanel;
var childrens = LazyLoadExtensions.GetLazyChildrens(wrapPanel);
for (int i = 0; i < childrens.Count; i++)
{
var child = childrens[i];
//The dictionaries containing the styles are added
SkinExt.SetTheme(child, SkinExt.GetTheme(wrapPanel));
wrapPanel.Children.Add(child);
}
}
This way the dictionaries containing the style are merged before the the children are added and the styles are correctly applied.
Edit:
Since you can't change the LazyLoadExtensions class:
In SkinExt class:
internal static void ApplyTheme(DependencyObject obj, string style)
{
Type itemType = obj.GetType();
List<string> styles = GetDictionaries(obj.GetType().Name.ToString(), style);
if (styles != null && styles.Count > 0)
{
foreach (var path in styles)
{
var rdict = new ResourceDictionary() { Source = new Uri(path, UriKind.RelativeOrAbsolute) };
bool alreadyExist = false;
foreach (var dictionaryFiles in Application.Current.Resources.MergedDictionaries)
{
if (dictionaryFiles.Source.OriginalString.Contains(path))
{
alreadyExist = true;
break;
}
}
if (!alreadyExist)
{
Application.Current.Resources.MergedDictionaries.Add(rdict);
if (obj is FrameworkElement frameworkElement && frameworkElement.IsInitialized)
{
//The style won't be applied automaticaly
frameworkElement.Style = rdict.Values.OfType<Style>().First(s => s.TargetType == itemType);
}
else
{
//Nothing to do, style will be applied automaticaly
}
Console.WriteLine(path);
}
}
}
}
If the IsInitialized property is true, the style from the added ressourceDictionnary won't be applied, you can check it in debugger.
https://learn.microsoft.com/en-gb/dotnet/api/system.windows.frameworkelement.isinitialized?view=netcore-3.1#System_Windows_FrameworkElement_IsInitialized
If it's helpful, applying theme inside dispatcher works as expected ,
d.Dispatcher.BeginInvoke(new Action(() =>
{
SkinExt.ApplyTheme(d, e.NewValue.ToString());
}));

How to add TooTip to MenuItem

This is my Menu:
<Menu Name="menuInterfaces" ItemsSource="{Binding MenuItems}" Margin="0,8,0,0" Style="{StaticResource StandardMenu}">
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type Menu:MenuItemViewModel}" ItemsSource="{Binding Path=MenuItems}">
<Grid>
<TextBlock Text="{Binding Description}"/>
</Grid>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
And my Style:
<Style TargetType="{x:Type Menu}" x:Key="StandardMenu">
<Style.Resources>
<Style x:Key="{x:Static MenuItem.SeparatorStyleKey}" TargetType="Separator">
<Setter Property="Height" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Separator">
<Border BorderBrush="{StaticResource MenuSeparatorBorderBrush}" BorderThickness="1" Margin="25,0,0,0"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Foreground" Value="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Menu}}}"/>
<Setter Property="FontSize" Value="{DynamicResource ApplicationFontSize}"/>
<Setter Property="Command" Value="{Binding Command}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MenuItem}">
<!--Border 1-->
<Border x:Name="Border" Background="Transparent" BorderBrush="Transparent" CornerRadius="2"
BorderThickness="1" SnapsToDevicePixels="False">
<Grid x:Name="Grid">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Col0" MinWidth="17" Width="Auto" SharedSizeGroup="MenuItemIconColumnGroup"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuTextColumnGroup"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="MenuItemIGTColumnGroup"/>
<ColumnDefinition x:Name="Col3" Width="14"/>
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0" x:Name="Icon" VerticalAlignment="Center" ContentSource="Icon"/>
<ContentPresenter Grid.Column="1" Margin="{TemplateBinding Padding}" x:Name="HeaderHost" RecognizesAccessKey="True" ContentSource="Header" VerticalAlignment="Center"/>
<ContentPresenter Grid.Column="2" Margin="8,1,8,1" x:Name="IGTHost" ContentSource="InputGestureText" VerticalAlignment="Center"/>
<Grid Grid.Column="3" Margin="4,0,6,0" x:Name="ArrowPanel" VerticalAlignment="Center">
<Path x:Name="ArrowPanelPath" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="{TemplateBinding Foreground}" Data="M0,0 L0,8 L4,4 z"/>
</Grid>
<Popup IsOpen="{Binding Path=IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}"
Placement="Right"
HorizontalOffset="-1"
x:Name="SubMenuPopup"
Focusable="false"
PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}"
AllowsTransparency="True">
<Grid Margin="0,0,5,5">
<!--Border 2-->
<Border x:Name="SubMenuBorder" CornerRadius="5"
BorderBrush="{StaticResource MenuSeparatorBorderBrush}"
BorderThickness="1"
Background="{StaticResource SubmenuItemBackground}"
SnapsToDevicePixels="True">
<Grid x:Name="SubMenu" Grid.IsSharedSizeScope="True" Margin="2">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle"/>
</Grid>
<Border.Effect>
<DropShadowEffect ShadowDepth="2" Color="Black"/>
</Border.Effect>
</Border>
<!--Border 3-->
<Border Margin="1,0,0,0"
x:Name="TransitionBorder"
Width="0"
Height="2"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Background="{StaticResource SubmenuItemBackground}"
SnapsToDevicePixels="False"
BorderThickness="1"
BorderBrush="{StaticResource SubmenuItemBackground}"/>
</Grid>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Role" Value="TopLevelHeader">
<Setter Property="Padding" Value="6,0,6,2"/>
<Setter TargetName="SubMenuPopup" Property="Placement" Value="Bottom"/>
<Setter TargetName="Col0" Property="MinWidth" Value="0"/>
<Setter TargetName="Col3" Property="Width" Value="Auto"/>
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="IGTHost" Property="Visibility" Value="Collapsed" />
<Setter TargetName="ArrowPanel" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="SubMenuBorder" Property="BorderThickness" Value="1,1,1,1"/>
<Setter TargetName="SubMenu" Property="Margin" Value="2,3,2,2"/>
<Setter TargetName="TransitionBorder" Property="Width" Value="{Binding ActualWidth, ElementName=Grid}"/>
</Trigger>
<Trigger Property="Role" Value="TopLevelItem">
<Setter Property="Padding" Value="6,0,6,2"/>
<Setter TargetName="Col0" Property="MinWidth" Value="0"/>
<Setter TargetName="Col3" Property="Width" Value="Auto"/>
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="IGTHost" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="ArrowPanel" Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuHeader">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="Padding" Value="10,3,0,3"/>
<Setter TargetName="Border" Property="MinHeight" Value="22"/>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SubmenuItemBackground}"/>
</Trigger>
<Trigger Property="Role" Value="SubmenuItem">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="Padding" Value="10,3,0,3"/>
<Setter TargetName="Border" Property="MinHeight" Value="22"/>
<Setter TargetName="ArrowPanel" Property="Visibility" Value="Collapsed"/>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SubmenuItemBackground}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsHighlighted" Value="true"/>
<Condition Property="Role" Value="TopLevelHeader"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource MenuItemHighlightedBackground}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsHighlighted" Value="true"/>
<Condition Property="Role" Value="TopLevelItem"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource MenuItemHighlightedBackground}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsHighlighted" Value="true"/>
<Condition Property="Role" Value="SubmenuHeader"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SubmenuItemBackgroundHighlighted}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsHighlighted" Value="true"/>
<Condition Property="Role" Value="SubmenuItem"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SubmenuItemBackgroundHighlighted}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSubmenuOpen" Value="true"/>
<Condition Property="Role" Value="TopLevelHeader"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="#55B3B3B6"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#55B3B3B6"/>
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0"/>
</MultiTrigger>
<Trigger Property="IsSuspendingPopupAnimation" Value="true">
<Setter TargetName="SubMenuPopup" Property="PopupAnimation" Value="None"/>
</Trigger>
<Trigger Property="Icon" Value="{x:Null}">
<Setter TargetName="Icon" Property="Visibility" Value="Collapsed"/>
</Trigger>
<!--<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Foreground" Value="LightSlateGray"/>
--><!--<Setter Property="IsSubmenuOpen" Value="False"/>-->
<!--<Setter Property="Cursor" Value="No"/>--><!--
</Trigger>-->
<!--<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="Black"/>
--><!--<Setter Property="IsSubmenuOpen" Value="True"/>
<Setter Property="Cursor" Value="Hand"/>--><!--
</Trigger>
<Trigger Property="IsSubmenuOpen" Value="true">
<Setter TargetName="ArrowPanelPath" Property="Fill" Value="{StaticResource BackgroundSelected}"/>
<Setter Property="Foreground" Value="LightSlateGray"/>
</Trigger>-->
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False"/>
<Condition Property="IsSubmenuOpen" Value="False"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="LightSlateGray"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSubmenuOpen" Value="False"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="Silver"/>
<Setter TargetName="Border" Property="Background" Value="#55B3B3B6"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="False"/>
<Condition Property="IsSubmenuOpen" Value="True"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="Silver"/>
<Setter TargetName="Border" Property="Background" Value="#55B3B3B6"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSubmenuOpen" Value="True"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="Border" Property="Background" Value="#55B3B3B6"/>
<Setter TargetName="Border" Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="Silver"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsManagement}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Foreground" Value="{StaticResource MenuDisabledForeground}"/>
</MultiDataTrigger>
<!--<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource MenuDisabledForeground}"/>
</Trigger>-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource LightBackground}"/>
<Setter Property="Foreground" Value="{StaticResource Foreground}"/>
</Style>
View model:
public class MenuItemViewModel : INotifyPropertyChanged
{
private readonly ICommand _command;
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
public string Description { get; set; }
public string Id { get; set; }
public bool _isSelected { get; set; }
public MenuItemViewModel()
{
_command = new CommandViewModel(Execute);
}
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get { return _command; }
}
private void Execute()
{
// (NOTE: In a view model, you normally should not use
}
}
Now all i want to do is add ToolTip so under Menu section inside my XAML i added this section:
<Menu.ToolTip>
<ToolTip>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Id}"/>
</StackPanel>
</ToolTip>
</Menu.ToolTip>
But nothings is shown so my suspicion is my Style but i cant find out where..
Any suggestions ?
Based on your bindings it seems to me your intention is to add the tooltip to the individual menu items rather than the top-level menu itself, so you probably want to be doing something like this:
<Style TargetType="{x:Type MenuItem}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Description}"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Id}"/>
</StackPanel>
</ToolTip>
</Setter.Value>
</Setter>
<!-- etc -->
</Style>

Why do I draw a ellipse in grid will be blocked?

It's a WPF Program.
I draw a ellipse by c# code, then insert to grid's Children.
It's located at TreeViewItem.
I just see the part of ellipse at the front has be convert by lengthConverter.
xmal 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>
<converters: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 Name="Gd" Margin="{Binding Converter={StaticResource lengthConverter},
RelativeSource={RelativeSource TemplatedParent}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="19" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ToggleButton x:Name="Expander"
Style="{StaticResource ExpandCollapseToggleStyle2}"
IsChecked="{Binding Path=IsExpanded,
RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<ContentPresenter x:Name="PART_Header"
Grid.Column="1"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</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="IsMouseOver" Value="True">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"></Setter>
</Trigger>
<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>
C# code:
var item = sender as TreeViewItem;
if (item.Items.Count > 0) return;
var controlTemplate = item.Template as ControlTemplate;
var grid=controlTemplate.FindName("Gd",item) as Grid;
var p = args.GetPosition((IInputElement)item);
var elli = new Ellipse
{
Width = 0,
Height = 0,
Fill = Brushes.White,
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Top,
Opacity = .6
};
grid.Children.Insert(0,elli);
var animation = new DoubleAnimation(0, new Duration(TimeSpan.FromMilliseconds(400)));
animation.Completed += (s, e1) =>
{
grid.Children.Remove(elli);
};
elli.BeginAnimation(OpacityProperty, animation);
elli.BeginAnimation(HeightProperty, new DoubleAnimation(600, new Duration(TimeSpan.FromMilliseconds(400))));
elli.BeginAnimation(WidthProperty, new DoubleAnimation(600, new Duration(TimeSpan.FromMilliseconds(400))));
elli.BeginAnimation(MarginProperty, new ThicknessAnimation(new Thickness(p.X, p.Y, 0, 0), new Thickness(p.X - 300, p.Y - 300, 0, 0), new Duration(TimeSpan.FromMilliseconds(400))));
Because I insert the ellipse in first column of the grid. I set the column to solve it.

Make ObservableCollection item invisible

This is my Model:
ObservableCollection<Student> StudentsList;
class Student
{
static ObservableCollection<Student> Students;
string SName;
String FName;
}
And i have simple Combobox:
<ComboBox
x:Name="cbStudents"
ItemsSource="{Binding StudentsList}">
I case i want to hide from my Combobox item/items, is it possible to do that without using the function Remove from my Students ObservableCollection ?
Edit:
After try mm8 solution it seems that my combo box style i cause my error so this is my style:
<!-- region combobox style -->
<SolidColorBrush x:Key="ComboBoxNormalBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="ComboBoxNormalBackgroundBrush" Color="#FF103766" />
<SolidColorBrush x:Key="ComboBoxDisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="ComboBoxDisabledBackgroundBrush" Color="#FF7AA0CD" />
<SolidColorBrush x:Key="ComboBoxDisabledBorderBrush" Color="#FF7AA0CD" />
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border
Name="Border"
Padding="5"
Margin="0"
BorderThickness="0"
CornerRadius="0"
Background="Transparent"
BorderBrush="Transparent" >
<TextBlock TextAlignment="Left">
<ContentPresenter />
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true"/>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsHighlighted" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="White"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#FF103766"/>
<Setter TargetName="Border" Property="Background" Value="#FF103766"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="false"/>
<Condition Property="IsSelected" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="White"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#FF103766"/>
<Setter TargetName="Border" Property="Background" Value="#FF103766"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true"/>
<Condition Property="IsSelected" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="White"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#FF103766"/>
<Setter TargetName="Border" Property="Background" Value="#FF103766"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="false"/>
<Condition Property="IsSelected" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="#FF103766"/>
<Setter TargetName="Border" Property="BorderBrush" Value="White"/>
<Setter TargetName="Border" Property="Background" Value="White"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate TargetType="ToggleButton" x:Key="ComboBoxToggleButtonTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border
Grid.ColumnSpan="2"
Name="Border"
BorderBrush="{StaticResource ComboBoxNormalBorderBrush}"
CornerRadius="0"
BorderThickness="1, 1, 1, 1"
Background="{StaticResource ComboBoxNormalBackgroundBrush}" />
<Border
Grid.Column="1"
Margin="1, 1, 1, 1"
BorderBrush="#444"
Name="ButtonBorder"
CornerRadius="0, 0, 0, 0"
BorderThickness="0, 0, 0, 0"
Background="{StaticResource ComboBoxNormalBackgroundBrush}" />
<Path
Name="Arrow"
Grid.Column="1"
Data="M 0 0 L 8 12 L 16 0 Z"
HorizontalAlignment="Center"
Fill="#444"
VerticalAlignment="Center"
Margin="-10,0,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="Transparent"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="White"/>
<Setter Property="Panel.Background" TargetName="Border" Value="#FF7AA0CD"/>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="False">
<Setter Property="Shape.Fill" TargetName="Arrow" Value="Transparent"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="Transparent"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="White"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="Panel.Background" TargetName="Border" Value="{StaticResource ComboBoxDisabledBackgroundBrush}"/>
<Setter Property="Panel.Background" TargetName="ButtonBorder" Value="{StaticResource ComboBoxDisabledBackgroundBrush}"/>
<Setter Property="Border.BorderBrush" TargetName="ButtonBorder" Value="{StaticResource ComboBoxDisabledBorderBrush}"/>
<Setter Property="TextElement.Foreground" Value="{StaticResource ComboBoxDisabledForegroundBrush}"/>
<Setter Property="Shape.Fill" TargetName="Arrow" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="ComboBoxFlatStyle" TargetType="{x:Type ComboBox}">
<Setter Property="UIElement.SnapsToDevicePixels" Value="True"/>
<Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="TextElement.Foreground" Value="Gainsboro"/>
<Setter Property="FrameworkElement.FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Name="ToggleButton"
Grid.Column="2"
ClickMode="Press"
Focusable="False"
IsChecked="{Binding Path=IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Template="{StaticResource ComboBoxToggleButtonTemplate}"/>
<ContentPresenter
Name="ContentSite"
Margin="5, 4, 23, 3"
IsHitTestVisible="False"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Content="{TemplateBinding ComboBox.SelectionBoxItem}"
ContentTemplate="{TemplateBinding ComboBox.SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"/>
<TextBox
Name="PART_EditableTextBox"
Margin="3, 3, 23, 3"
IsReadOnly="{TemplateBinding IsReadOnly}"
Visibility="Hidden"
Background="Transparent"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Focusable="True" >
<TextBox.Template>
<ControlTemplate TargetType="TextBox" >
<Border Name="PART_ContentHost" Focusable="False" />
</ControlTemplate>
</TextBox.Template>
</TextBox>
<Popup
Name="Popup"
Placement="Bottom"
Focusable="False"
AllowsTransparency="True"
IsOpen="{TemplateBinding ComboBox.IsDropDownOpen}"
PopupAnimation="Fade">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding FrameworkElement.ActualWidth}"
MaxHeight="{TemplateBinding ComboBox.MaxDropDownHeight}">
<Border
Name="DropDownBorder"
Background="Gainsboro"
Margin="0, 0, 0, 0"
CornerRadius="0"
BorderBrush="#FF103766"
BorderThickness="1" />
<ScrollViewer Margin="4" SnapsToDevicePixels="True">
<ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
<!-- Popup showing items -->
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="TextElement.Foreground" Value="White"/>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="False">
<Setter Property="TextElement.Foreground" Value="Gainsboro"/>
</Trigger>
<Trigger Property="ItemsControl.HasItems" Value="False">
<Setter Property="FrameworkElement.MinHeight" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" Value="Gainsboro"/>
</Trigger>
<Trigger Property="ItemsControl.IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
</Trigger>
<Trigger Property="ComboBox.IsEditable" Value="True">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
<Setter Property="UIElement.Visibility" TargetName="PART_EditableTextBox" Value="Visible"/>
<Setter Property="UIElement.Visibility" TargetName="ContentSite" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- endregion of Flat ComboBox -->
You could use an ItemContainerStyle with a DataTrigger to for example hide a Student with a particular name, e.g.:
<ComboBox
x:Name="cbStudents"
ItemsSource="{Binding StudentsList}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding SName}" Value="SomeName">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
Edit: (see the comments below) "ComboBoxFlatStyle" is a ComboBox style. The ItemContainerStyle should be based on the implicit ComboBoxItem style:
<ComboBox Style="{StaticResource ComboBoxFlatStyle}" ...>
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
...
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
you need CollectionViewSource and filter delegate
#region feed items
private ObservableCollection<OpdsItemBase> _FeedItems = new ObservableCollection<OpdsItemBase>();
public ICollectionView FeedItemView
{
get
{
return CollectionViewSource.GetDefaultView(_FeedItems);
}
}
#endregion
and
public string FilterText
{
get { return _filterText; }
set
{
_filterText = value;
FeedItemView.Filter = delegate(object obj)
{
if (string.IsNullOrEmpty(_filterText))
return true;
OpdsItemBase data = obj as OpdsItemBase;
if (data == null)
return false;
return (data.Title.IndexOf(_filterText, 0, StringComparison.InvariantCultureIgnoreCase) > -1);
};
}
}

Generic Button Template

I have the following button style that has a cross in it and is used for a close button on a TabItem
<Button Grid.Column="2"
Width="15"
Height="15"
HorizontalAlignment="Center"
VerticalAlignment="Center"
DockPanel.Dock="Right"
AttachedCommand:CommandBehavior.Event="Click"
AttachedCommand:CommandBehavior.Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CloseWorkspaceCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Focusable" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<Path x:Name="ButtonPath"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M0,0 L1,1 M0,1 L1,0"
SnapsToDevicePixels="True"
Stretch="Uniform"
Stroke="{DynamicResource CloseButtonStroke}"
StrokeEndLineCap="Flat"
StrokeStartLineCap="Flat"
StrokeThickness="2"/>
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"
Value="false" />
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TabItem}}"
Value="false" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Visibility" Value="Hidden" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background"
Value="{DynamicResource CloseButtonBackgroundHighlighted}" />
<Setter TargetName="ButtonPath"
Property="Stroke"
Value="{DynamicResource CloseButtonStrokeHighlighted}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background"
Value="{DynamicResource CloseButtonBackgroundPressed}" />
<Setter TargetName="ButtonPath"
Property="Stroke"
Value="{DynamicResource CloseButtonStroke}" />
<Setter TargetName="ButtonPath"
Property="Margin"
Value="2.5,2.5,1.5,1.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
This produces
I want to add a CheckButton styled to show a pin next to the close button like VS2012. I can do this no problem, but I want to place the CheckBox inside the same button template above so I get highlighting when the mouse is over the area. The problem is the button above contains the Path that draws the 'X'.
Is there a was I can make the button style above generic so I can include either the 'X' Path or the CheckBox, without replicating all the trigger stuff?
Thanks for your time.
I would approach this by creating my button class like ToolBarButton below and have a dependency property ButtonType. I will set ButtonType on the ToolBarButton.
public enum ButtonType
{
Close =0,
Pin
}
public class ToolBarButton : Button
{
public ButtonType ButtonType
{
get { return (ButtonType)this.GetValue(ButtonTypeProperty); }
set { this.SetValue(ButtonTypeProperty, value); }
}
public static readonly DependencyProperty ButtonTypeProperty = DependencyProperty.Register(
"ButtonType", typeof(ButtonType), typeof(ToolBarButton), new PropertyMetadata(ButtonType.Close));
}
in xaml:
<local:ToolBarButton ButtonType="Pin"/>
Then I will add the trigger in style to check for buttontype and apply the controltemplate containing checkbox
<Style.Triggers>
<Trigger Property="ButtonType" Value="Pin">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<CheckBox/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>

Resources