I'm making an application using C# and WPF
I have 8 toggle buttons.
When I click one of the buttons others should be disabled so I can't click it except one is activated.
When I click this button again others should be enabled
Styles.xaml:
<!--Toggle Button-->
<Style x:Key="ToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<ToggleButton x:Name="CategoryToggle1"
Grid.Row="1"
Style="{StaticResource ToggleButton}"
Checked="ShowOptions"
Unchecked="HideOptions" />
How Can I achieve this by code and XAML?
I want to do it like in this video:
Video
Thank you for comments guys, I found another solution.
MainWindow.XAML:
<RadioButton x:Name="CategoryToggle1"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource RadioButton}"
GroupName="ToggleButtonsGroup"
Checked="OpenOptions"
Unchecked="HideOptions"/>
Styles.xaml:
<!--Radio Button-->
<Style TargetType="RadioButton"
x:Key="RadioButton"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Toggle Button-->
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.cs:
private void OpenOptions(object sender, RoutedEventArgs e){
RadioButton radioButton = sender as RadioButton;
radioButton.IsChecked = true;
//Disable all option buttons except one that active
MyGrid.Children.OfType<RadioButton>().Where(rb => rb != radioButton &&
rb.GroupName == radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = false);
}
private void HideOptions(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
MyGrid.Children.OfType<RadioButton>().Where(rb => rb.GroupName ==
radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = true);
}
Using Click events of each ToggleButton
One way you could do it is by giving a name to all your ToggleButtons, hook-up to their Click event and manually uncheck others in the code-behind:
XAML
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="MaxWidth" Value="15"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" Click="button1_Click"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" Click="button2_Click"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" Click="button3_Click"/>
<TextBlock Text="Line 3"/>
</StackPanel>
</StackPanel>
Code-behind
private void button1_Click(object sender, RoutedEventArgs e) {
if (button1.IsChecked == true) {
button2.IsChecked = false;
button3.IsChecked = false;
}
}
private void button2_Click(object sender, RoutedEventArgs e) {
if (button2.IsChecked == true) {
button1.IsChecked = false;
button3.IsChecked = false;
}
}
private void button3_Click(object sender, RoutedEventArgs e) {
if (button3.IsChecked == true) {
button1.IsChecked = false;
button2.IsChecked = false;
}
}
This method is tedious, error-prone, requires code-behind and is not very scalable.
Binding IsChecked properties to a collection of bool with one true at a time.
Another way you could go (still by using code-behind) is to define a collection of boolean values and bind each ToggleButton.IsChecked on one of the bool in the collection, and ensure that the collection only contains at most one true at a time:
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" IsChecked="{Binding [0]}"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" IsChecked="{Binding [1]}"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" IsChecked="{Binding [2]}"/>
<TextBlock Text="Line 3"/>
</StackPanel>
Code-behind
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
ObservableCollection<bool> states = new ObservableCollection<bool> { false, false, false };
states.CollectionChanged += States_CollectionChanged;
DataContext = states;
}
private void States_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
var collection = sender as ObservableCollection<bool>;
if (e.Action == NotifyCollectionChangedAction.Replace) {
if ((bool)e.NewItems[0]) {
for (int i = 0; i < collection.Count; i++) {
if (e.NewStartingIndex != i) {
collection[i] = false;
}
}
}
}
}
}
Again, this uses code-behind and not the view model but at least it is easier to add rows.
The behavior you need is very specific.
I don't know how, but i got here trying to make a toggle button behave like a radio button. Your answer was enlightning.
For what it's worth, here's how you would do that :
Resource :
<Style x:Key='RadioToggle' TargetType='RadioButton'
BasedOn='{StaticResource {x:Type ToggleButton}}' />
Control :
<RadioButton Content='RadioToggle1' IsChecked='True'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle2'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle3'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
Related
I have a custom RadioButton style with an Image and 2 TextBlocks.
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >
<Setter Property="Background" Value="#32353B" />
<Setter Property="Margin" Value="10,5"/>
<Setter Property="Height" Value="45" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="1" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<Image Source="{TemplateBinding Button.Tag}" HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE" FontSize="18" FontFamily="Arial">Strona</TextBlock>
<TextBlock Foreground="#52555C" FontSize="12" FontFamily="Arial"> Login</TextBlock>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#282B2E"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
When I create a new button at runtime, setting the Style and I want to change the Image and the text for every single RadioButton. Right now I am thinking about using a Tag for that.
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
radioButton.Style = (Style)Resources["ToggleButton_Chose"];
radioButton.Tag = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
Is there any other method to set this?
I will have around 100 of the RadioButtons and any of them should get different images and texts.
Custom Control
If you want to create a ToggleButton that requires additional (bindable) properties, you could create a custom control with dependency properties for the image and texts based on the ToggleButton type. Create a new type AdvancedToggleButton that derives from ToggleButton.
public class AdvancedToggleButton : ToggleButton
{
static AdvancedToggleButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AdvancedToggleButton), new FrameworkPropertyMetadata(typeof(AdvancedToggleButton)));
}
public ImageSource ImageSource
{
get => (ImageSource) GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
nameof(ImageSource), typeof(ImageSource), typeof(AdvancedToggleButton));
public string FirstText
{
get => (string) GetValue(FirstTextProperty);
set => SetValue(FirstTextProperty, value);
}
public static readonly DependencyProperty FirstTextProperty = DependencyProperty.Register(
nameof(FirstText), typeof(string), typeof(AdvancedToggleButton));
public string SecondText
{
get => (string) GetValue(SecondTextProperty);
set => SetValue(SecondTextProperty, value);
}
public static readonly DependencyProperty SecondTextProperty = DependencyProperty.Register(
nameof(SecondText), typeof(string), typeof(AdvancedToggleButton));
}
Then you can create a default implicit style (by omitting the x:Key) in your application resources or another resource dictionary in scope, so the style is applied automatically.
<Style TargetType="{x:Type local:AdvancedToggleButton}">
<Setter Property="Background"
Value="#32353B" />
<Setter Property="Margin"
Value="10,5" />
<Setter Property="Height"
Value="45" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Padding"
Value="1" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:AdvancedToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center" />
<Image Source="{TemplateBinding ImageSource}"
HorizontalAlignment="Left"
Stretch="Uniform"
Width="45"
IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE"
FontSize="18"
FontFamily="Arial"
Text="{TemplateBinding FirstText}"/>
<TextBlock Foreground="#52555C"
FontSize="12"
FontFamily="Arial"
Text="{TemplateBinding SecondText}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Background"
Value="#282B2E" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E"
Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Note, that I removed the duplicate setter for HorizontalAlignment. The Image binds its Source to the ImageSource property and the TextBlocks bind to FirstText and SecondText respectively. You can either define the AdvancedToggleButton in XAML or code.
<local:AdvancedToggleButton ImageSource="\Resources/Check.jpg"
FirstText="Strona"
SecondText="Login"/>
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
radioButton.ImageSource = new BitmapImage(new Uri("https://www.google.com/favicon.ico"));
radioButton.FirstText = "Strona";
radioButton.SecondText = "Login"
Useful resources on custom controls:
Control authoring overview
Dependency properties overview
Attached Properties
An alternative without creating a custom control is to create a set of attached properties.
public static class ToggleButtonProperties
{
public static ImageSource GetImageSource(DependencyObject dependencyObject)
{
return (ImageSource) dependencyObject.GetValue(ImageSourceProperty);
}
public static void SetImageSource(DependencyObject dependencyObject, ImageSource value)
{
dependencyObject.SetValue(ImageSourceProperty, value);
}
public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.RegisterAttached(
"ImageSource", typeof(ImageSource), typeof(ToggleButtonProperties));
public static string GetFirstText(DependencyObject dependencyObject)
{
return (string) dependencyObject.GetValue(FirstTextProperty);
}
public static void SetFirstText(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(FirstTextProperty, value);
}
public static readonly DependencyProperty FirstTextProperty = DependencyProperty.RegisterAttached(
"FirstText", typeof(string), typeof(ToggleButtonProperties));
public static string GetSecondText(DependencyObject dependencyObject)
{
return (string) dependencyObject.GetValue(SecondTextProperty);
}
public static void SetSecondText(DependencyObject dependencyObject, string value)
{
dependencyObject.SetValue(SecondTextProperty, value);
}
public static readonly DependencyProperty SecondTextProperty = DependencyProperty.RegisterAttached(
"SecondText", typeof(string), typeof(ToggleButtonProperties));
}
These properties can be bound in the control template using parentheses, which is the binding syntax for attached properties and RelativeSource to the parent ToggleButton.
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}">
<Setter Property="Background"
Value="#32353B" />
<Setter Property="Margin"
Value="10,5" />
<Setter Property="Height"
Value="45" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="Padding"
Value="1" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Left"
Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center" />
<Image Source="{Binding (local:ToggleButtonProperties.ImageSource), RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Left"
Stretch="Uniform"
Width="45"
IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE"
FontSize="18"
FontFamily="Arial"
Text="{Binding (local:ToggleButtonProperties.FirstText), RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Foreground="#52555C"
FontSize="12"
FontFamily="Arial"
Text="{Binding (local:ToggleButtonProperties.SecondText), RelativeSource={RelativeSource TemplatedParent}}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Background"
Value="#282B2E" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E"
Opacity="0.5" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Assigning or binding the attached properties is done via the static class.
<ToggleButton Style="{StaticResource ToggleButton_Chose}"
local:ToggleButtonProperties.ImageSource="\Resources/Check.jpg"
local:ToggleButtonProperties.FirstText="Strona"
local:ToggleButtonProperties.SecondText="Login"/>
RadioButton radioButton = new RadioButton();
radioButton.GroupName = "Side";
ToggleButtonProperties.SetImageSource(radioButton, new BitmapImage(new Uri("https://www.google.com/favicon.ico")));
ToggleButtonProperties.SetFirstText(radioButton, "Strona");
ToggleButtonProperties.SetSecondText(radioButton, "Login");
Useful resources on attached properties:
Attached Properties Overview
But is ther any other method to sett this? I will have around 100 of the RadioButtons and any of there should get other image and text.
Create a simple data type:
public class ButtonContent
{
public string Strona { get; set; }
public string Login { get; set; }
public object ImageSource { get; set; }
}
This type will be used to pass the button data into the context.
Therefore, we will slightly change the style:
<Style x:Key="ToggleButton_Chose" TargetType="{x:Type ToggleButton}" >
<Setter Property="Background" Value="#32353B" />
<Setter Property="Margin" Value="10,5"/>
<Setter Property="Height" Value="45" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Padding" Value="1" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
<ContentPresenter HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<Image Source="{Binding ImageSource}"
HorizontalAlignment="Left" Stretch="Uniform" Width="45" IsEnabled="True" />
<StackPanel Margin="2">
<TextBlock Foreground="#DCDDDE" FontSize="18" FontFamily="Arial"
Text="{Binding Strona}"/>
<TextBlock Foreground="#52555C" FontSize="12" FontFamily="Arial"
Text="{Binding Login}"/>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#282B2E"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="#FF282B2E" Opacity="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
For a set of buttons use ItemsControl:
<DataTemplate x:Key="itemTemplate" DataType="{x:Type local:ButtonContent}">
<ToggleButton Style="{DynamicResource ToggleButton_Chose}"/>
</DataTemplate>
<ItemsControl x:Name="itemsControl"
ItemTemplate="{DynamicResource itemTemplate}">
</ItemsControl>
Create an observable collection with data and pass it to the ItemsControl source:
public partial class MainWindow : Window
{
private readonly ObservableCollection<ButtonContent> ButtonContents
= new ObservableCollection<ButtonContent>();
public MainWindow()
{
InitializeComponent();
itemsControl.ItemsSource = ButtonContents;
ButtonContents.Add(new ButtonContent() { Strona = "Strona1", Login = "Login1", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona2", Login = "Login2", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona3", Login = "Login3", ImageSource = "Image/block.png" });
ButtonContents.Add(new ButtonContent() { Strona = "Strona4", Login = "Login4", ImageSource = "Image/block.png" });
}
}
I created a context menu for a tabcontrol which changes the name of the tab. However, if the mouse over on an unselected tab and mouse right clicked on context menu pops up. If I click the the menu item it changes the selected tab's name. For instance, I right clicked on Favorite 4 tab and tried to change its name but it changed first tab's name (selected tab) as shown below.
I would like to select the tab with right mouse click as well as with left mouse click so it will not cause confusion or not intentional tab name change.
XAML
<TabControl x:Name="FavoritesTabs" HorizontalAlignment="Stretch" Height="23"
Initialized="FavoritesTabs_Initialized" Margin="8,0,7,0"
MouseRightButtonDown="FavoritesTabs_MouseRightButtonDown" MouseEnter="FavoritesTabs_MouseEnter" >
<TabControl.ContextMenu>
<ContextMenu Name="tabContextMenu">
<MenuItem Header="Change Tab Name" Click="MenuItem_Click" />
<MenuItem Header="Save Favorite Layers" />
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
C#
private void FavoritesTabs_Initialized(object sender, EventArgs e)
{
FavoritesList.Add("Favorite 1");
FavoritesList.Add("Favorite 2");
FavoritesList.Add("Favorite 3");
FavoritesList.Add("Favorite 4");
FavoritesTabs.ItemsSource = FavoritesList;
}
private void FavoritesTabs_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
}
private void MenuItem.Click(object sender, RoutedEventArgs e)
{
int index = FavoritesTabs.SelectedIndex;
FavoritesList[index] = "New tab";
}
I tried this answer but it did not work.
You have to style your TabControl adding event for MouseDown then select the tab item accordingly.
This is the code:
XAML
At fist, you need to define reosurces for TabControl style and also a style for a Grid. The latter is needed because you can't define an event handler within the TabControl style directly.
<Window x:Class="WpfApp7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp7"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style x:Key="GridStyle" TargetType="Grid">
<EventSetter Event="Mouse.MouseDown" Handler="UIElement_OnMouseDown"/>
</Style>
<Style x:Key="TabcontrolStyle" TargetType="{x:Type TabControl}">
<Style.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Height="20"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true"
Style="{StaticResource GridStyle}">
<ContentPresenter Margin="10,0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header" >
</ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="false">
<Setter Property="Background" Value="Transparent" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Static SystemColors.ControlBrush}" />
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="{x:Static SystemColors.ActiveCaptionBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TabPanel Name="HeaderPanel"
Panel.ZIndex="1"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1" />
<ContentPresenter Name="PART_SelectedContentHost"
Margin="10"
Grid.Row="1"
ContentSource="SelectedContent" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl x:Name="MyTabControl" Style="{StaticResource TabcontrolStyle}">
<TabControl.ContextMenu>
<ContextMenu Name="tabContextMenu">
<MenuItem Header="Change Tab Name" />
<MenuItem Header="Save Favorite Layers" />
</ContextMenu>
</TabControl.ContextMenu>
<TabItem Header="First">First Content</TabItem>
<TabItem Header="Second">Second Content</TabItem>
<TabItem Header="Third">Third Content</TabItem>
</TabControl>
</Grid>
Code behind
In the code behind you can equals the TabItem header to selected the TabItem accordingly.
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
ContentPresenter contentPresenter = null;
if (e.Source is Grid)
contentPresenter = (ContentPresenter) ((Grid) e.Source).Children[0];
else if (e.Source is ContentPresenter)
contentPresenter = ((ContentPresenter) e.Source);
if (contentPresenter == null) return;
var header = contentPresenter.Content.ToString();
var selectedIndex = -1;
foreach (var item in this.MyTabControl.Items)
{
selectedIndex++;
var tabItem = item as TabItem;
if (tabItem?.Header != null && tabItem.Header.ToString().Equals(header, StringComparison.InvariantCultureIgnoreCase))
{
this.MyTabControl.SelectedIndex = selectedIndex;
}
}
}
There are two buttons "ShowAllExpanders" and "HideAllExpanders"
expand all headers work perfectly but collapsing all take just some headers and I have to press the button "hide all" each time to collapse one by one.
If I bind Tag to the Expander, then I can collapse all expanders but also if I click on just one expander to collapse it, all the rest are collapsed.
ParameterConfigViewList when expanding all headers gives me all headers( count =17) but when I press "hide all" gives me just two( Count=2).
What could I do to collapse every expander apart and also to collapse all if I press button "HideAllExpanders"?
Xaml Code is:
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Expander IsExpanded="True">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border Background="{TemplateBinding Background}">
<DockPanel>
<ToggleButton x:Name="HeaderSite" MinWidth="0" MinHeight="0" Padding="
{TemplateBinding Padding}" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
DockPanel.Dock="Top"
FocusVisualStyle="{DynamicResource ExpanderHeaderFocusVisual}"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource
TemplatedParent}}"
Style="{DynamicResource ExpanderDownHeaderStyle}" />
<ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false"
Visibility="Collapsed" />
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Bottom" FontSize="14" Text="{Binding Name}" />
<TextBlock Margin="10,0,0,0" VerticalAlignment="Bottom" Text="{Binding ItemCount}" />
<TextBlock VerticalAlignment="Bottom" Text="configuration" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
Xaml_code
The code in View.xaml.cs:
private void HideAllExpanders_Click(object sender, RoutedEventArgs e)
{
List<Expander> expander = GetVisualTree<Expander>(ParameterConfigViewList);
for (int i = 0; i < expander.Count; i++)
{
expander[i].Height = 0;
expander[i].Height = Double.NaN;
expander[i].IsExpanded = false;
}
}
private void ShowAllExpanders_Click(object sender, RoutedEventArgs e)
{
List<Expander> expander = GetTreeObjects<Expander>(ParameterConfigViewList);
expander.All(a => a.IsExpanded = true);
}
private List<T> GetTreeObjects<T>(DependencyObject obj) where T : DependencyObject
{
List<T> objects = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if(child != null)
{
T requestedType = child as T;
if(requestedType != null)
objects.Add(requestedType);
objects.AddRange(this.GetTreeObjects<T>(child));
}
}
return objects;
}
What could I do to collapse every expander apart and also to collapse
all if I press button "HideAllExpanders"?
You may want to implement the following effect. I made a sample depend on the code your provided above.
XAML:
<Grid Margin="10">
<ListView Name="lvUsers" Margin="0,0,290,0">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Style>
<Style TargetType="{x:Type Expander}">
<Setter Property="Visibility" Value="Visible" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<Border Background="{TemplateBinding Background}">
<DockPanel>
<ToggleButton x:Name="HeaderSite" MinWidth="0" MinHeight="0" Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
DockPanel.Dock="Top"
FocusVisualStyle="{DynamicResource ExpanderHeaderFocusVisual}"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource
TemplatedParent}}"
Style="{DynamicResource ExpanderDownHeaderStyle}" />
<ContentPresenter x:Name="ExpandSite" DockPanel.Dock="Bottom" Focusable="false"
Visibility="Collapsed" />
</DockPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter TargetName="ExpandSite" Property="Visibility" Value="Visible" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Expander.Style>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="Gray" FontSize="22" VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}" FontSize="22" Foreground="Green" FontWeight="Bold" FontStyle="Italic" Margin="10,0,0,0" VerticalAlignment="Bottom" />
<TextBlock Text=" item(s)" FontSize="22" Foreground="Silver" FontStyle="Italic" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<Button x:Name="button" Content="HideAllExpanders" HorizontalAlignment="Left" Margin="578,52,0,0" VerticalAlignment="Top" Width="154" Click="Button_Click"/>
<Button x:Name="button1" Content="ShowAllExpanders" HorizontalAlignment="Left" Margin="578,128,0,0" VerticalAlignment="Top" Width="154" Click="Button1_Click"/>
</Grid>
XAML.CS
private List<T> GetTreeObjects<T>(DependencyObject obj) where T : DependencyObject
{
List<T> objects = new List<T>();
int count = VisualTreeHelper.GetChildrenCount(obj);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null)
{
T requestedType = child as T;
if (requestedType != null)
objects.Add(requestedType);
objects.AddRange(this.GetTreeObjects<T>(child));
}
}
return objects;
}
/// <summary>
/// ShowAllExpanders
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button1_Click(object sender, RoutedEventArgs e)
{
List<Expander> expander = GetTreeObjects<Expander>(lvUsers);
expander.All(a => a.IsExpanded = true);
}
/// <summary>
/// HideAllExpanders
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Click(object sender, RoutedEventArgs e)
{
//List<Expander> expander = GetVisualTree<Expander>(ParameterConfigViewList);
//for (int i = 0; i < expander.Count; i++)
//{
// expander[i].Height = 0;
// expander[i].Height = Double.NaN;
// expander[i].IsExpanded = false;
//}
//List<Expander> expanderss = FindVisualChildren<Expander>(lvUsers).ToList();
foreach (Expander tb in FindVisualChildren<Expander>(lvUsers))
{
tb.Height = 0;
tb.Height = Double.NaN;
tb.IsExpanded = false;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
I have a ListView with different itemTemplate, and each itemTemplates have an ExpanderDoubleClick : Expander inside
I would like to select items in extended Mode in a listview with custom selection design (declared in each items UserControl).
So here is my xaml for the listView:
<ListView x:Name="ListViewModules" ItemsSource="{Binding ListOfModules}"
ItemContainerStyle="{StaticResource ContainerListViewItemStyle}"
ItemTemplateSelector="{DynamicResource ModuleTemplateSelector}"
SelectionMode="Extended"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True"
dd:DragDrop.UseDefaultDragAdorner="True"
Grid.Column="0" Grid.Row="0" Height="494" Width="634" Background="#FFCDCDCD"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderBrush="#666666" BorderThickness="1" Padding="0"
ClipToBounds="True" SnapsToDevicePixels="True" >
</ListView>
with styles for removing the standard blue selection (declared in App.xaml):
<Style x:Key="ContainerListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Margin" Value="2,2,2,0"/>
<Setter Property="dd:DragDrop.DragSourceIgnore" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd" BorderBrush="Transparent" BorderThickness="0" Background="Transparent" Padding="0" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have different ItemTemplates with each "UserControl's" and here an exemple of one of them with it's selection design :
<UserControl x:Class="Topinambour.Templates.Modules.DelayTemplate" ...>
<Grid Width="602" MinHeight="24">
<Grid.ColumnDefinitions> <ColumnDefinition Width="40"/><ColumnDefinition /> </Grid.ColumnDefinitions>
<ToggleButton x:Name="TgBtIsActive" IsChecked="{Binding IsChecked}" IsThreeState="{Binding IsThreeState}" Grid.Column="0" Height="24" Width="40" click="TgBtIsActive_Click"/>
<templates:ExpanderDoubleClick x:Name="ModuleExpander" IsExpanded="{Binding IsExpanded, Mode=OneWay}" Height="auto" Template="{DynamicResource TemplateExpander}" Grid.Column="1" Margin="4,0,0,0" Collapsed="Expander_Collapsed" Expanded="Expander_Expanded" HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<templates:ExpanderDoubleClick.Resources>
<Style x:Key="ExpanderHeaderStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="BorderIndent" BorderBrush="Gray" BorderThickness="1" Height="24" Width="558" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Padding="{Binding Indent}">
... my Header Content ...
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded}" Value="true">
<Setter Property="IsEnabled" TargetName="TbComment" Value="true"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListViewItem}}" Value="true">
<Setter Property="Background" TargetName="headerCanvas" Value="#999999"/>
<Setter Property="BorderBrush" TargetName="TbComment" Value="#666666"/>
<Setter Property="Foreground" TargetName="LbHeaderTitle" Value="White"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</templates:ExpanderDoubleClick.Resources>
<Border Background="White" BorderBrush="#666666" BorderThickness="1,0,1,1" Height="136" Width="558">
<Grid>
... my Expanded Content ...
</Grid>
</Border>
</templates:ExpanderDoubleClick>
</Grid>
and here the ExpanderDoubleClick.cs :
public class ExpanderDoubleClick: Expander
{
private static readonly DependencyPropertyKey IsMouseDoubleClickedPropertyKey = DependencyProperty.RegisterReadOnly(
"IsMouseDoubleClicked",typeof(Boolean),typeof(ExpanderDoubleClick),new FrameworkPropertyMetadata(false));
public static readonly DependencyProperty IsMouseDoubleClickedProperty = IsMouseDoubleClickedPropertyKey.DependencyProperty;
public Boolean IsMouseDoubleClicked
{
get { return (Boolean)GetValue(IsMouseDoubleClickedProperty); }
}
static ExpanderDoubleClick()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ExpanderDoubleClick), new FrameworkPropertyMetadata(typeof(ExpanderDoubleClick)));
}
protected override void OnMouseDoubleClick(MouseButtonEventArgs e)
{
base.OnMouseDoubleClick(e);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
ContentControl contentControl = base.GetTemplateChild("HeaderSite") as ContentControl;
if (contentControl != null)
{
contentControl.AddHandler(ContentControl.MouseDoubleClickEvent, new MouseButtonEventHandler(ExpanderHeader_MouseDoubleClick), true);
}
}
private void ExpanderHeader_MouseDoubleClick(Object sender, MouseButtonEventArgs e)
{
base.SetValue(IsMouseDoubleClickedPropertyKey, !IsMouseDoubleClicked);
base.IsExpanded= !base.IsExpanded;
}
}
the selection is not working when I click on the header of the expander but if I open it and click inside the expandedContent, it select the item ! what did I miss ? thank you for the reply.
I finally found a pretty good solution that keep my extended mode selection.
since the header seams to not receive Click event to handle.
I looked up the tree debug, and it shows that the click on header does not go through the "ListViewItem"
so I add an OnMouseLeftButtonDown Event on my border x.Name="BorderIdent" and then add RaisedEvent back to it's itemTemplate
like so :
private void BorderIndent_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListViewItem lvItem = UITools.FindAncestor<ListViewItem>(e.OriginalSource as DependencyObject);
bool isListViewItem = lvItem != null;
if (isListViewItem)
{
lvItem.RaiseEvent(e);
}
}
UITools.FindAncestor : is here
and all worked perfectly :)
I have an element and multiple styles, how do I switch between the styles at runtime either programatically or through XAML binding.
<Rectangle x:Name="fixtureControl" Style="{DynamicResource FixtureStyle_Fast}">
<!-- In the style resources. -->
<Style x:Key="FixtureStyle_Fast" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Good" TargetType="{x:Type Shape}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity=".9"
Direction="-90"
RenderingBias="Performance"
BlurRadius="50"
ShadowDepth="10" />
</Setter.Value>
</Setter>
</Style>
<Style x:Key="FixtureStyle_Best" TargetType="{x:Type Shape}">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Opacity=".9"
Direction="-90"
RenderingBias="Quality"
BlurRadius="50"
ShadowDepth="10" />
</Setter.Value>
</Setter>
</Style>
Then I have some radio buttons that handle changing the style
private void RadioButton_Click(object sender, RoutedEventArgs e) {
if (e.Source == rdoQualityBest) {
fixtureControl.Style = FindResource("FixtureStyle_Best") as Style;
} else if (e.Source == rdoQualityGood) {
fixtureControl.Style = FindResource("FixtureStyle_Good") as Style;
} else {
fixtureControl.Style = FindResource("FixtureStyle_Fast") as Style;
}
}
However this applies the style to the element, not replacing it, so if I apply Fast then Quality, I get both the border and the drop-shadow.
Something like this has worked for me in the past (a pure XAML solution):
<!-- Styles 1-4 defined somewhere else on your page -->
<ComboBox Name="AvailableStyles">
<ComboBoxItem Tag="{x:Null}" IsSelected="True">None</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style1}">1</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style2}">2</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style3}">3</ComboBoxItem>
<ComboBoxItem Tag="{StaticResource Style4}">4</ComboBoxItem>
</ComboBox>
<Button Content="Button" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
<CheckBox Content="Check Box" Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
<RadioButton Content="Radio Button"Style="{Binding ElementName=AvailableStyles, Path=SelectedItem.Tag}"/>
Hope this helps!
It is working fine for me
here is my code:
in .xaml
<Window.Resources>
<Style x:Key="FixtureStyle_Fast" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Black"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Good" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Red"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
<Style x:Key="FixtureStyle_Best" TargetType="{x:Type Shape}">
<Setter Property="Stroke" Value="Blue"/>
<Setter Property="StrokeThickness" Value="20"/>
</Style>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle x:Name="fixtureControl" Style="{DynamicResource FixtureStyle_Fast}"/>
<StackPanel Grid.Column="1">
<RadioButton Name="rdoQualityBest" Content="Best" Click="RadioButton_Click" />
<RadioButton Name="rdoQualityGood" Content="Good" Click="RadioButton_Click" />
<RadioButton Name="rdoQualityFast" Content="Fast" Click="RadioButton_Click" />
</StackPanel>
</Grid>
in .xaml.cs
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
if (e.Source == rdoQualityBest)
{
fixtureControl.Style = FindResource("FixtureStyle_Best") as Style;
}
else if (e.Source == rdoQualityGood)
{
fixtureControl.Style = FindResource("FixtureStyle_Good") as Style;
}
else
{
fixtureControl.Style = FindResource("FixtureStyle_Fast") as Style;
}
}