How can I change an elements style at runtime? - c#

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;
}
}

Related

RadioButton with custom image and text change at runtime

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" });
}
}

WPF: Change the CornerRadius for only the last item in a ListBox

I've created a ListBox with round corners. I've also added a bottom border to all ListBoxItems except for the last one.
The ListBoxItems, however, have normal, square corners, so when hovering over or selecting the first or last items, you can see an overlap between the round ListBox corner and the square ListBoxItem corners.
I can't set CornerRadius the same way I set BorderThickness - I think that's because CornerRadius is a property of the Border property(?).
I can force ALL ListBoxItems to have all round corners which fixes the overlap, but then ALL ListBoxItems have round underlines & selections- which I'd rather not have. I only want those round corners on the bottom of the last item (and eventually top of the first item)
I would like to use a similar sort of trigger for setting CornerRadius that I do for setting BrushThickness.
Is there a way to set the corner radius of just the last item in a ListBox? (and eventually the first item)
In my test, I'm using the MaterialDesignTheme package from NuGet. Because that's non-standard, I'll add all my code here (also note: I'm new to WPF, so feel free to critique anything that looks off):
App.xaml:
<Application . . .
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml: (Note, if you uncomment the commented section, it styles all ListBoxItems like I would want only the last ListBoxItem styled)
<Window . . .
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
<Window.Resources>
<local:IsLastItemInContainerConverter x:Key="IsLastItemInContainerConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Margin="5">
<ListBox x:Name="GameListBox"
BorderBrush="{DynamicResource MaterialDesignDivider}"
BorderThickness="1">
<ListBox.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}">
<!--<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="0 0 10 10"/>
</Style>
</Style.Resources>-->
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource IsLastItemInContainerConverter}}" Value="False">
<Setter Property="BorderThickness" Value="0 0 0 1" />
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignDivider}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource IsLastItemInContainerConverter}}" Value="True">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem>
<TextBlock> Plain
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> Old
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> ListBox
</TextBlock>
</ListBoxItem>
<ListBoxItem>
<TextBlock> Full of junk
</TextBlock>
</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
</Window>
...and in MainWindow.xaml.cs, I have defined the converter to find the last item:
public class IsLastItemInContainerConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
DependencyObject item = (DependencyObject)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return ic.ItemContainerGenerator.IndexFromContainer(item)
== ic.Items.Count - 1;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
First of all, please understand that the MaterialDesignTheme package was not used in my code.
Items.cs
Instead of using Converter, I added a model class.
public class Items
{
public string Name { get; set; }
public bool IsFirst { get; set; }
public bool IsLast { get; set; }
}
App.xaml
I defined styles of ListBoxItem, ListBox like below.
<Style TargetType="{x:Type ListBoxItem}" x:Key="listboxitem">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="border"
Background="White"
BorderBrush="#AAAAAA"
BorderThickness="1 1 1 0"
CornerRadius="0">
<TextBlock Text="{Binding Name}" Foreground="Black" FontSize="13" FontWeight="Normal"
Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsFirst}" Value="True">
<Setter TargetName="border" Property="CornerRadius" Value="10 10 0 0"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsLast}" Value="True">
<Setter TargetName="border" Property="CornerRadius" Value="0 0 10 10"/>
<Setter TargetName="border" Property="BorderThickness" Value="1 1 1 1"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="#666666"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="border" Property="Background" Value="#666666"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ListBox}" x:Key="listbox">
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="200"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource listboxitem}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Background="Transparent"
BorderBrush="#AAAAAA"
BorderThickness="0 0 0 0"
CornerRadius="10">
<ItemsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml
<ListBox x:Name="lbx" Style="{StaticResource listbox}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lbx.ItemsSource = GetItems();
}
private List<Items> GetItems()
{
List<Items> source = new List<Items>();
source.Add(new Items { Name = "Plain", IsFirst = true });
source.Add(new Items { Name = "Old" });
source.Add(new Items { Name = "ListBox" });
source.Add(new Items { Name = "Full of junk", IsLast = true }); ;
return source;
}
}
It will be shown like this..

How do I select a tab with right mouse click event in WPF

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;
}
}
}

How to disable all toggle buttons except one that clicked?

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" />

Live chart using WPF Toolkit Line Series

I have a Chart control from WPF Toolkit. The data collection bound to the line series used in the chart is dynamic, i.e., new points are added to the collection when I receive data. I noticed that because of this, the memory consumption was increasing. So, I limited the number of points displayed in the line series to 20. Following is the code.
Code Behind:
public partial class MainWindow : Window
{
ObservableCollection<ChartData> lineSeries1Data;
Thread MyThread;
LineSeries lineSeries;
ChartData objChartData;
Random r = new Random();
LineSeries temp;
public MainWindow()
{
InitializeComponent();
objChartData = new ChartData();
lineSeries1Data = new ObservableCollection<ChartData>();
AddLineSeries();
InitializeDataPoints();
MyThread = new Thread(new ThreadStart(StartChartDataSimulation));
}
private void AddLineSeries()
{
lineSeries = AddLineSeries(Brushes.Tomato);
lineSeries.DependentValuePath = "Value";
lineSeries.IndependentValuePath = "Name";
lineSeries.ItemsSource = lineSeries1Data;
simChart.Series.Add(lineSeries);
}
private LineSeries AddLineSeries(Brush lineColor)
{
temp = new LineSeries()
{
PolylineStyle = this.FindResource("PolylineStyle") as Style,
DataPointStyle = this.FindResource("LineDataPointStyle") as Style,
Style = this.FindResource("CommonLineSeries") as Style,
Tag = lineColor
};
return temp;
}
private void InitializeDataPoints()
{
lineSeries1Data.Add(new ChartData() { Name = DateTime.Now, Value = 0.0 });
Thread.Sleep(1000);
for (int i = 0; i < 2; i++)
{
lineSeries1Data.Add(new ChartData() { Name = DateTime.Now, Value = new Random().NextDouble() * 10 });
Thread.Sleep(1000);
}
}
public void StartChartDataSimulation()
{
int i = 0;
while (true)
{
Dispatcher.Invoke(new Action(() =>
{
objChartData.Name = DateTime.Now;
objChartData.Value = r.NextDouble() * 50;
lineSeries1Data.Add(objChartData);
if (lineSeries1Data.Count > 20)
{
lineSeries1Data.RemoveAt(0);
}
}));
Thread.Sleep(500);
}
}
private void btnStartCharting_Click(object sender, RoutedEventArgs e)
{
if (MyThread.ThreadState != ThreadState.Running)
{
MyThread.Start();
}
}
}
public class ChartData
{
DateTime _Name;
double _Value;
public DateTime Name
{
get
{
return _Name;
}
set
{
_Name = value;
}
}
public double Value
{
get
{
return _Value;
}
set
{
_Value = value;
}
}
}
XAML:
<Window x:Class="WpfChartExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:chrt="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
xmlns:datavis="clr-namespace:System.Windows.Controls.DataVisualization;assembly=System.Windows.Controls.DataVisualization.Toolkit"
Title="WPF Toolkit Dynamic Line Chart Example" Height="333" Width="1000" Closing="Window_Closing">
<Window.Resources>
<Style x:Key="PolylineStyle" TargetType="Polyline">
<Setter Property="StrokeThickness" Value="5" />
</Style>
<Style x:Key="LineDataPointStyle" TargetType="chrt:LineDataPoint">
<!--<Setter Property="Background" Value="#0077CC" />-->
<Setter Property="Background" >
<Setter.Value>
<Binding Path="Tag" RelativeSource="{RelativeSource AncestorType={x:Type chrt:LineSeries}}" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="White" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Height" Value="10" />
<Setter Property="Width" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chrt:LineDataPoint">
<Grid x:Name="Root" Opacity="1">
<ToolTipService.ToolTip>
<StackPanel Margin="2,2,2,2">
<ContentControl Content="{TemplateBinding IndependentValue}" ContentStringFormat="X-Value: {0:HH:mm:ss}"/>
<ContentControl Content="{TemplateBinding DependentValue}" ContentStringFormat="Y-Value: {0:###.###}"/>
</StackPanel>
</ToolTipService.ToolTip>
<Ellipse StrokeThickness="{TemplateBinding BorderThickness}" Stroke="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="CommonLineSeries" TargetType="chrt:LineSeries" BasedOn="{StaticResource {x:Type chrt:LineSeries}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chrt:LineSeries">
<Canvas x:Name="PlotArea">
<Polyline Points="{TemplateBinding Points}" Stroke="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type chrt:LineSeries}}}" Style="{TemplateBinding PolylineStyle}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="LinearAxisStyle" TargetType="chrt:LinearAxis">
<Setter Property="GridLineStyle">
<Setter.Value>
<Style TargetType="Line">
<Setter Property="Stroke" Value="#20000000" />
</Style>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="NonLinearAxisStyle" TargetType="chrt:DateTimeAxis">
<Setter Property="GridLineStyle">
<Setter.Value>
<Style TargetType="Line">
<Setter Property="Stroke" Value="#20000000" />
</Style>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="DateTimeAxisLabelStyle" TargetType="chrt:DateTimeAxisLabel">
<Setter Property="StringFormat" Value="{}{0:mm:ss}" />
</Style>
<Style x:Key="ChartTitleStyle" TargetType="datavis:Title">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</Window.Resources>
<Grid Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition Height="6*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<chrt:Chart x:Name="simChart" TitleStyle="{StaticResource ResourceKey=ChartTitleStyle}">
<chrt:Chart.LegendStyle>
<Style TargetType="datavis:Legend">
<Setter Property="Width" Value="0" />
</Style>
</chrt:Chart.LegendStyle>
<chrt:Chart.Axes>
<chrt:DateTimeAxis Name="xAxis" ShowGridLines="True" Orientation="X" Style="{StaticResource ResourceKey=NonLinearAxisStyle}" AxisLabelStyle="{StaticResource ResourceKey=DateTimeAxisLabelStyle}" />
<chrt:LinearAxis Orientation="Y" ShowGridLines="True" Style="{StaticResource ResourceKey=LinearAxisStyle}" />
</chrt:Chart.Axes>
<!--<chrt:LineSeries IsSelectionEnabled="True" PolylineStyle="{StaticResource ResourceKey=PolylineStyle}"
DataPointStyle="{StaticResource ResourceKey=LineDataPointStyle}"
ItemsSource="{Binding}"
IndependentValueBinding="{Binding Name}"
DependentValueBinding="{Binding Value}"
Tag="Red" Style="{StaticResource CommonLineSeries}">
</chrt:LineSeries>-->
</chrt:Chart>
<Button Name="btnStartCharting"
Grid.Row="1"
Width="Auto"
HorizontalAlignment="Right"
Margin="5"
Click="btnStartCharting_Click">
Start Charting
</Button>
</Grid>
In the above code, when I remove the first point, the graph refresh is affected. The refresh become slow and sometimes it stops refreshing. The older datapoints are also visible on the graph. Can someone guide me on making dynamic charts with WPF Toolkit?

Categories