Change Images on Custom CheckBox in WPF - c#

All I have created the following custom CheckBox which uses images instead of a CheckBox. This works well however, I want to be able to change the images as required. Ideally I would like to use application resources Properties.Resources.SomeImage16 (a .png file). The XAML is
<Style x:Key="styleCustomCheckBox"
TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Image x:Name="imageCheckBox"
Width="16"
Height="16"
Source="F:\Camus\ResourceStudio\Graphics\Images\UnPinned16.png"/>
<ContentPresenter VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="imageCheckBox"
Property="Source"
Value="F:\Camus\ResourceStudio\Graphics\Images\Pinned16.png"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="imageCheckBox"
Property="Source"
Value="F:\Camus\ResourceStudio\Graphics\Images\UnPinned16.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
With implementation
<ListBox SelectionMode="Single" >
<StackPanel Orientation="Horizontal">
<CheckBox Style="{StaticResource styleCustomCheckBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="4,0,4,0"/>
<TextBlock VerticalAlignment="Top"
Text="SomeRecentDocument.resx"/>
</StackPanel>
</ListBox>
How can I change the images used for the custom CheckBox (i.e. change the pinned/un-pinned to tick/cross etc.) without having to create a new style/template?
Thanks for your time.

As already mentioned #HighCore the need for the ability to use vector graphics. In this case, to use the Path, where in Data to the specified coordinates on which the object is drawn (MSDN).
Advantages:
Do not store it in the files, smaller size
Dynamically changing color, size and the whole shape
Minuses (in my opinion):
You can not always find the right Data for the Path
About minus: There are special sites (www.modernuiicons.com) and utilities for converting the image to Data.
Change the style of CheckBox using the Path:
Style
<Style x:Key="styleCustomCheckBox" TargetType="{x:Type CheckBox}">
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Path x:Name="MyPin" Width="18" Height="18" Stretch="Fill" Fill="#FF000000"
Data="F1 M 56.1355,32.5475L 43.4466,19.8526C 42.7886,20.4988 42.298,21.2123 41.9749,21.9932C 41.6519,22.7741 41.4903,23.5729 41.4903,24.3895C 41.4903,25.1942 41.6529,25.987 41.9779,26.7679L 34.0577,34.6821C 33.3918,34.3372 32.6991,34.0776 31.9796,33.9032C 31.2601,33.7288 30.5298,33.6415 29.7885,33.6415C 28.623,33.6415 27.4953,33.8526 26.4052,34.2748C 25.315,34.697 24.3419,35.3342 23.4856,36.1865L 30.2344,42.9174L 25.9027,47.9032L 22.6532,51.8425L 20.5988,54.5836C 20.1212,55.2892 19.8823,55.753 19.8823,55.975L 19.8645,56.0701L 19.9002,56.088L 19.9002,56.1474L 19.9358,56.1058L 20.0131,56.1236C 20.2351,56.1236 20.6989,55.8888 21.4045,55.419L 24.1457,53.3765L 28.0849,50.1151L 33.0945,45.7775L 39.8016,52.5025C 40.6579,51.6462 41.2961,50.6731 41.7163,49.5829C 42.1365,48.4928 42.3466,47.367 42.3466,46.2056C 42.3466,45.4603 42.2603,44.729 42.0879,44.0115C 41.9155,43.294 41.6548,42.6003 41.3069,41.9304L 49.2202,34.0161C 50.0011,34.3372 50.7939,34.4978 51.5986,34.4978C 52.4192,34.4978 53.2189,34.3362 53.9979,34.0132C 54.7768,33.6901 55.4894,33.2015 56.1355,32.5475 Z "/>
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="MyPin" Property="Data" Value="F1 M 32.3691,30.2225L 33.2253,29.3901L 15.361,11.5258C 13.9814,12.7067 12.6951,13.9936 11.5148,15.3738L 26.6252,30.4842C 27.743,30.1631 28.8767,30.0025 30.0263,30.0025C 30.8191,30.0025 31.6,30.0759 32.3691,30.2225 Z M 45.5039,49.3629L 60.6292,64.4826C 62.0123,63.2996 63.3017,62.0101 64.4846,60.6268L 46.6218,42.7866L 45.7834,43.619L 45.9439,44.7726L 45.9915,45.9261L 45.8785,47.6713L 45.5039,49.3629 Z M 56.1355,32.5475L 43.4466,19.8526C 42.7886,20.4987 42.298,21.2123 41.9749,21.9932C 41.6519,22.7741 41.4903,23.5729 41.4903,24.3895C 41.4903,25.1942 41.6529,25.987 41.9779,26.7679L 34.0577,34.6821C 33.3918,34.3372 32.6991,34.0776 31.9796,33.9032C 31.2601,33.7288 30.5298,33.6415 29.7885,33.6415C 28.623,33.6415 27.4953,33.8526 26.4052,34.2748C 25.315,34.697 24.3419,35.3342 23.4856,36.1865L 30.2344,42.9174L 25.9027,47.9032L 22.6532,51.8425L 20.5988,54.5836C 20.1212,55.2892 19.8823,55.753 19.8823,55.975L 19.8645,56.0701L 19.9002,56.0879L 19.9002,56.1474L 19.9358,56.1058L 20.0131,56.1236C 20.2351,56.1236 20.6989,55.8888 21.4045,55.419L 24.1457,53.3765L 28.0849,50.1151L 33.0945,45.7775L 39.8016,52.5025C 40.6579,51.6462 41.2961,50.6731 41.7163,49.5829C 42.1365,48.4928 42.3466,47.367 42.3466,46.2056C 42.3466,45.4603 42.2603,44.729 42.0879,44.0115C 41.9155,43.294 41.6548,42.6003 41.306,41.9304L 49.2202,34.0161C 50.0011,34.3372 50.7939,34.4978 51.5986,34.4978C 52.4192,34.4978 53.219,34.3362 53.9979,34.0132C 54.7768,33.6901 55.4894,33.2015 56.1355,32.5475 Z " />
<Setter TargetName="MyPin" Property="Fill" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<CheckBox Height="35"
Style="{StaticResource styleCustomCheckBox}"
Content="MySolution1" />
<CheckBox Height="35"
Style="{StaticResource styleCustomCheckBox}"
Content="MySolution2" />
</StackPanel>
Output
We can also store the Path's in resources, and refer to them as like this:
<Path x:Key="MyPath" Data="F1 M 38,19C 48.4934,19 57,27.5066 ... />
...
<Setter TargetName="MainPath" Property="Data"
Value="{Binding Source={StaticResource MyPath}, Path=Data}" />
Edit
To specify arbitrary icons, I created two attached dependency properties (string type):
IsCheckedOnData
IsCheckedOffData
IsCheckedOnData contains Data value by IsChecked = "True", IsCheckedOffData value by IsChecked = "False".
Now you need only to determine the strings of icons and define such a resource (for example).
Full example:
XAML
<Window x:Class="CustomCheckBoxHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomCheckBoxHelp"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<sys:String x:Key="Up">
F1 M 37.8516,35.625L 34.6849,38.7917L 23.6016,50.2708L
23.6016,39.9792L 37.8516,24.9375L 52.1016,39.9792L 52.1016,
50.2708L 41.0182,38.7917L 37.8516,35.625 Z
</sys:String>
<sys:String x:Key="Down">
F1 M 37.8516,39.5833L 52.1016,24.9375L 52.1016,35.2292L
37.8516,50.2708L 23.6016,35.2292L 23.6016,24.9375L 37.8516,39.5833 Z
</sys:String>
<Style x:Key="styleCustomCheckBox" TargetType="{x:Type CheckBox}">
<Setter Property="FontFamily" Value="Verdana" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Path x:Name="MyPin" Width="18" Height="18" Stretch="Fill" Fill="#FF000000"
Data="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:CustomCheckBoxClass.IsCheckedOnData)}" />
<ContentPresenter VerticalAlignment="Center" Margin="10,0,0,0" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="MyPin" Property="Data"
Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:CustomCheckBoxClass.IsCheckedOffData)}" />
<Setter TargetName="MyPin" Property="Fill" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Background="Beige">
<CheckBox Height="35"
local:CustomCheckBoxClass.IsCheckedOnData="{StaticResource Up}"
local:CustomCheckBoxClass.IsCheckedOffData="{StaticResource Down}"
Style="{StaticResource styleCustomCheckBox}"
Content="MySolution1" />
<CheckBox Height="35"
local:CustomCheckBoxClass.IsCheckedOnData="{StaticResource Up}"
local:CustomCheckBoxClass.IsCheckedOffData="{StaticResource Down}"
Style="{StaticResource styleCustomCheckBox}"
Content="MySolution2" />
</StackPanel>
</Grid>
</Window>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class CustomCheckBoxClass : DependencyObject
{
#region IsCheckedOnDataProperty
public static readonly DependencyProperty IsCheckedOnDataProperty;
public static void SetIsCheckedOnData(DependencyObject DepObject, string value)
{
DepObject.SetValue(IsCheckedOnDataProperty, value);
}
public static string GetIsCheckedOnData(DependencyObject DepObject)
{
return (string)DepObject.GetValue(IsCheckedOnDataProperty);
}
#endregion
#region IsCheckedOffDataProperty
public static readonly DependencyProperty IsCheckedOffDataProperty;
public static void SetIsCheckedOffData(DependencyObject DepObject, string value)
{
DepObject.SetValue(IsCheckedOffDataProperty, value);
}
public static string GetIsCheckedOffData(DependencyObject DepObject)
{
return (string)DepObject.GetValue(IsCheckedOffDataProperty);
}
#endregion
static CustomCheckBoxClass()
{
PropertyMetadata MyPropertyMetadata = new PropertyMetadata(string.Empty);
IsCheckedOnDataProperty = DependencyProperty.RegisterAttached("IsCheckedOnData",
typeof(string),
typeof(CustomCheckBoxClass),
MyPropertyMetadata);
IsCheckedOffDataProperty = DependencyProperty.RegisterAttached("IsCheckedOffData",
typeof(string),
typeof(CustomCheckBoxClass),
MyPropertyMetadata);
}
}
Note: In the style I have not used TemplateBinding because TemplateBinding doesn’t work outside a template or outside its VisualTree property, so you can’t even use TemplateBinding inside a template’s trigger. Therefore, we must use the construction {RelativeSource TemplatedParent} and a Path equal to the dependency property whose value you want to retrieve.

Sorry, I don't yet know how to reference an image in those resources, but if you can add the images into a folder named Images in your application root directory, then you will be able to reference the images simply like this:
<Image Source="/ApplicationName;component/Images/SomeImage16.png" />

As you mention you can change the checkbox trigger by checked and unchecked. And the image will display corresponding trigger. Your xml code is good for me.I just remove the trigger true portion.because the false portion by default in focus and after click the checkbox image UnPinned16.png is visible. And agan click image Pinned16.png is visibale .
<Style x:Key="styleCustomCheckBox"
TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal">
<Image x:Name="imageCheckBox"
Width="16"
Height="16" Source="F:\Camus\ResourceStudio\Graphics\Images\UnPinned16.png"/>
<ContentPresenter VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="imageCheckBox"
Property="Source"
Value="F:\Camus\ResourceStudio\Graphics\Images\Pinned16.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I am using this checkbox under a textblock
<CheckBox Style="{StaticResource styleCustomCheckBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="4,0,4,0"/>

Related

Why is Style lost for cloned UIElement or FrameworkElement

I'm trying to load a Grid with UIElements and/or FrameworkElements that have Style and DataTriggers.
The following is a simple native XAML which works as expected; toggling the check box through the 3 states give three different styles to the Rectangle (see image at bottom).
<Grid
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:XamlVsLoad"
mc:Ignorable="d"
Height="450" Width="800">
<CheckBox Name="MyCheck" IsChecked="True" Content="Checkbox" IsThreeState="True" Margin="10,10,0,0" Width="200"/>
<Rectangle Height="100" Width="100" StrokeThickness="5" Margin="10,50,100,100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="White"/>
<Setter Property="Stroke" Value="Black"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=MyCheck, Path=IsChecked}" Value="True" >
<Setter Property="Fill" Value="Orange"/>
<Setter Property="Stroke" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=MyCheck, Path=IsChecked}" Value="False" >
<Setter Property="Fill" Value="Blue"/>
<Setter Property="Stroke" Value="Orange"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
It also works when I save the same text to a file and load it dynamically into Window's Viewboxs with XAML like this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<DockPanel>
<Label Content="Native" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="NativeViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
<!-- Native Grid Omitted for brevity 3 uniquely named checkboxes -->
</Viewbox>
</DockPanel>
<DockPanel Grid.Column="1">
<DockPanel>
<Label Content="Dynamic" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="DynamicViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
</DockPanel>
</DockPanel>
<DockPanel Grid.Column="2">
<DockPanel>
<Label Content="Clone" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="CloneViewbox" Height="auto" Width="200" DockPanel.Dock="Top" />
</DockPanel>
</DockPanel>
<DockPanel Grid.Column="3">
<DockPanel>
<Label Content="Saved" DockPanel.Dock="Top" HorizontalAlignment="Center" Margin="10,10,10,10" />
<Viewbox Name="SavedViewbox" Height="auto" Width="200" DockPanel.Dock="Top"/>
</DockPanel>
</DockPanel>
However, if I try to copy/clone/deepcopy (I've tried several different methods) the FrameworkElements/UIElements from one Grid/container to a new one, the style no longer works. Here are the various ways I'm currently loading and cloning them:
public partial class ReadCopy : Window {
public ReadCopy() {
InitializeComponent();
// test the dynamic load
Grid NativeXaml = ReadGrid("C:\\XamlFiles\\LoadXaml.xaml");
DynamicViewbox.Child = NativeXaml; // honors style
// test the Clone load
Grid CloneXaml = new Grid();
foreach (FrameworkElement fe in NativeXaml.Children) CloneXaml.Children.Add(Clone(fe));
CloneViewbox.Child = CloneXaml; // doesn't honor style
// test the save Clone and then load
StringBuilder outstr = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true, IndentChars = " ", NewLineChars = "\r\n", NewLineHandling = NewLineHandling.Replace };
XamlDesignerSerializationManager dsm = new XamlDesignerSerializationManager(XmlWriter.Create(outstr, settings)) { XamlWriterMode = XamlWriterMode.Expression };
XamlWriter.Save(CloneXaml, dsm);
File.WriteAllText("C:\\XamlFiles\\SavedXaml.xaml", outstr.ToString());
Grid SavedXaml = ReadGrid("C:\\XamlFiles\\SavedXaml.xaml");
SavedViewbox.Child = SavedXaml; // honors style and triggers again...
}
public Grid ReadGrid(string fn) {
FileStream fs = new FileStream(fn, FileMode.Open);
return XamlReader.Load(fs) as Grid;
}
public FrameworkElement Clone(FrameworkElement it) {
FrameworkElement clone;
using (var stream = new MemoryStream()) {
XamlWriter.Save(it, stream);
stream.Seek(0, SeekOrigin.Begin);
clone = (FrameworkElement)XamlReader.Load(stream);
}
clone.Style = it.Style; // setting it or not has no effect
return clone;
}
}
The output of the beginning simple example grid's cloned XAML via XamlWriter.Save:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CheckBox IsChecked="True" IsThreeState="True" Style="{x:Null}" Name="MyCheck" Width="200" Margin="10,10,0,0">Checkbox</CheckBox>
<Rectangle StrokeThickness="5" Width="100" Height="100" Margin="10,50,100,100">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=MyCheck}" Value="True">
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FFFFA500</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FF0000FF</SolidColorBrush>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsChecked, ElementName=MyCheck}" Value="False">
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FF0000FF</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FFFFA500</SolidColorBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
<Style.Resources>
<ResourceDictionary />
</Style.Resources>
<Setter Property="Shape.Fill">
<Setter.Value>
<SolidColorBrush>#FFFFFFFF</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke">
<Setter.Value>
<SolidColorBrush>#FF000000</SolidColorBrush>
</Setter.Value>
</Setter>
</Style>
</Rectangle.Style>
</Rectangle>
</Grid>
While I can see it reformatted the Style, I don't understand why it no longer works when I set the Viewbox.Child with the clone. What's even more confusing is that the loading the saved file of the clone then works. Here's what all four (Native, Dynamic, Cloned, Saved Clone Reloaded) look like:
Can anyone explain how to properly preserve the style through a copy/clone?
The Style on the Rectangle is triggered by the CheckBox. Because of this, Clone() will not work loading the child elements separately. To load them at the same time, clone their parent, the Grid itself. I reproduced your problem and this worked for me:
Grid NativeXaml = ReadGrid();
DynamicViewbox.Child = NativeXaml;
Grid CloneXaml = (Grid)Clone(NativeXaml);
CloneViewbox.Child = CloneXaml;

Why does a custom control "ImageButton" not display it's image?

I'm writing an image button custom control with highlighting effects, based on MahApps' AccentedSquareButtonStyle. ImageButton.xaml:
<UserControl x:Class="NQR_GUI_WPF.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:NQR_GUI_WPF"
mc:Ignorable="d" >
<Button Style="{StaticResource AccentedSquareButtonStyle}" Background="Transparent" Foreground="Transparent" BorderThickness="0" Width="24" Height="24" TouchDown="Button_TouchDown">
<Grid Background="Transparent">
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{Binding Image, RelativeSource={RelativeSource TemplatedParent}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value="True" >
<Setter Property="Content" Value="{Binding HighlightedImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsPressed}" Value="True" >
<Setter Property="Content" Value="{Binding ClickedImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Button>
ImageButton.xaml.cs:
namespace NQR_GUI_WPF
{
/// <summary>
/// Interaction logic for ImageButton.xaml
/// </summary>
public partial class ImageButton : UserControl
{
public static DependencyProperty ImageProperty = DependencyProperty.Register("Image", typeof(Canvas), typeof(ImageButton));
public static DependencyProperty ClickedImageProperty = DependencyProperty.Register("ClickedImage", typeof(Canvas), typeof(ImageButton));
public static DependencyProperty HighlightedImageProperty = DependencyProperty.Register("HighlightedImage", typeof(Canvas), typeof(ImageButton));
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public Canvas Image
{
get { return (Canvas)base.GetValue(ImageProperty); }
set { base.SetValue(ImageProperty, value); }
}
public Canvas ClickedImage
{
get { return (Canvas)base.GetValue(ClickedImageProperty); }
set { base.SetValue(ClickedImageProperty, value); }
}
public Canvas HighlightedImage
{
get { return (Canvas)base.GetValue(HighlightedImageProperty); }
set { base.SetValue(HighlightedImageProperty, value); }
}
private void Button_TouchDown(object sender, TouchEventArgs e)
{
Keyboard.ClearFocus();
}
}
}
Example icon:
<Canvas x:Key="printIcon" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="appbar_printer_text" Width="76" Height="76" Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Path Width="44" Height="45" Canvas.Left="16" Canvas.Top="17" Stretch="Fill" Fill="{Binding Source={x:Static prop:Settings.Default}, Path=theme, Converter={StaticResource idealForegroundConverter}}" Data="F1 M 25,27L 25,17L 51,17L 51,27L 47,27L 47,21L 29,21L 29,27L 25,27 Z M 16,28L 60,28L 60,51L 52,51L 52,46L 55,46L 55,33L 21,33L 21,46L 24,46L 24,51L 16,51L 16,28 Z M 25,39L 28,39L 28,52L 35,52L 35,59L 48,59L 48,39L 51,39L 51,62L 33,62L 25,54L 25,39 Z M 46,55L 38,55L 38,52L 46,52L 46,55 Z M 46,49L 30,49L 30,46L 46,46L 46,49 Z M 46,43L 30,43L 30,40L 46,40L 46,43 Z "/>
</Canvas>
The problem is that in MainWindow, after adding the images stored in App.xaml, the control is empty (no images are shown).
<local:ImageButton Image="{StaticResource printIcon}" HighlightedImage="{StaticResource printIconHighlighted}" ClickedImage="{StaticResource printIconClicked}" Grid.Column="1" HorizontalAlignment="Left" Height="46" Margin="36,10,0,0" VerticalAlignment="Top" Width="100"/>
I have tried binding the images directly into the control template, but without success (although in the control designer view the image is shown). Why aren't the control images displayed?
A UserControl isn't your best option for this. UserControls aren't meant for writing general-purpose WPF controls. You can do it, but it's not the simplest way. The simplest way is to subclass a regular control (often just ContentControl or HeaderedContentControl), then write a style and a template for it. Once you get this technique nailed down you can just bang 'em out as needed. Often you can just write a specialized template for an existing control, but in your case you do need your own subclass of Button.
I would write ImageButton as a subclass of Button, with the additional dependency properties pretty much as you've defined them, but I'd make them of type Object so a consumer can stuff anything in there that XAML can render. No reason not to give them all the rope they can use. And I'll use the Content property instead of the Image property, because that simplifies things.
If for some reason you have a requirement to prevent non-image content, you could use a more specialized content type than Object, but you didn't mention any particular reason for introducing that limitation.
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace NQR_GUI_WPF
{
/// <summary>
/// Interaction logic for ImageButton.xaml
/// </summary>
public class ImageButton : Button
{
public ImageButton()
{
TouchDown += ImageButton_TouchDown;
}
private void ImageButton_TouchDown(object sender, TouchEventArgs e)
{
Keyboard.ClearFocus();
}
#region Dependency Properties
public static DependencyProperty ClickedContentProperty = DependencyProperty.Register("ClickedContent", typeof(Object), typeof(ImageButton));
public static DependencyProperty HighlightedContentProperty = DependencyProperty.Register("HighlightedContent", typeof(Object), typeof(ImageButton));
public Object ClickedContent
{
get { return (Object)base.GetValue(ClickedContentProperty); }
set { base.SetValue(ClickedContentProperty, value); }
}
public Object HighlightedContent
{
get { return (Object)base.GetValue(HighlightedContentProperty); }
set { base.SetValue(HighlightedContentProperty, value); }
}
#endregion Dependency Properties
}
}
XAML resoure dictionary ImageButton.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nqrgui="clr-namespace:NQR_GUI_WPF"
>
<Style TargetType="{x:Type nqrgui:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type nqrgui:ImageButton}">
<Grid>
<ContentControl
Content="{TemplateBinding Content}"
x:Name="PART_Content"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter
TargetName="PART_Content"
Property="Content"
Value="{Binding HighlightedContent, RelativeSource={RelativeSource TemplatedParent}}"
/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter
TargetName="PART_Content"
Property="Content"
Value="{Binding ClickedContent, RelativeSource={RelativeSource TemplatedParent}}"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And here's how you'd use it:
<Window
...
xmlns:nqrgui="clr-namespace:NQR_GUI_WPF"
...
>
<!-- Or better yet, merge ImageButton.xaml in App.xaml so everybody can see it -->
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ImageButton.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
...
<!-- As noted, Content, HighlightedContent, and ClickedContent
can be images -- or also paths, text, ANYTHING XAML can render.
-->
<nqrgui:ImageButton
Content="Content"
HighlightedContent="Highlighted"
ClickedContent="Clicked"
/>
And you really can go absolutely berserk with the content:
<!-- Don't try this in a UI anybody will have to use! -->
<nqrgui:ImageButton
Content="Content"
ClickedContent="Clicked"
>
<nqrgui:ImageButton.HighlightedContent>
<StackPanel Orientation="Horizontal">
<Border
BorderBrush="Gray"
Background="GhostWhite"
BorderThickness="1">
<Path
Width="20"
Height="20"
Stroke="Black"
StrokeThickness="2"
Data="M 0,0 L 20,20 M 0,20 L 20,0"
Margin="2"
/>
</Border>
<nqrgui:ImageButton
Content="LOL"
ClickedContent="Don't Click Me, Bro!"
HighlightedContent="I heard you like buttons"
/>
</StackPanel>
</nqrgui:ImageButton.HighlightedContent>
</nqrgui:ImageButton>
You are using TemplateParent incorrectly
instead of this
{Binding Image, RelativeSource={RelativeSource TemplatedParent}}
it should be something like this
{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=ImageButton}, Path=Image}
I have done that like this below,
<Controls:MetroWindow.Resources>
<ImageBrush Stretch="Fill" x:Key="CloseImage" ImageSource="../images/Close.png" />
<ImageBrush x:Key="CloseImageRed" ImageSource="../images/CloseRed.jpg" />
</Controls:MetroWindow.Resources>
<Button>
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource CloseImageRed}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource CloseImage}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Have a look.
You are setting UserControl.Content to your customized Button, and I think what you want to be setting is UserControl.ContentTemplate.
From within .Content, there is no "TemplatedParent" to bind to. However if this were a Template, then TemplatedParent would point to the UserControl that the Template is defined for. In this case, it would refer to your ImageButton UserControl, which would correctly give you access to the Image properties.
<UserControl ..>
<UserControl.ContentTemplate>
<ControlTemplate>
<!-- TemplatedParent bindings should refer to UserControl from here -->
<Button ... />
</ControlTemplate>
</UserControl.ContentTemplate>
</UserControl>
This also allows you to write something like
<local:ImageButton Content="Some Text" />
without completely replacing your Button XAML code with a Text element containing "Some Text"
For an example, what you have right now would render as
<UserControl>
<Button /> <!-- Button is .Content, and can be replaced by XAML using the control -->
</UserControl>
While if it were a ContentTemplate, it would render as
<UserControl>
<Button> <!-- Button is ContentTemplate, so wraps any content given by external XAML -->
<Content />
</Button>
</UserControl>

Using three different ComboBoxItem styles in one WPF ComboBox

So I'm trying to create a user control for an application I'm working on. It's basically a ToggleButton next to a ComboBox. I was able to pretty much mock the ComboBox portion of the user control up in VS2015 the way the designers want it, but I feel like the way I'm going about it is not exactly the best way.
First, here is a link to a screenshot of what it looks like:
https://www.dropbox.com/s/019f4xqgu8r4i0e/DropDown.png
To do this, I ended up creating 3 different ComboBoxItem styles. The first puts together a CheckBox, a TextBlock with the ContentPresenter, and a Rectangle. The second just has a Separator, and the last just has the TextBlock with the ContentPresenter. Here is my XAML, which is declared in the UserControl.Resources section:
<Style x:Key="cbTestStyle" 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="2"
BorderThickness="2"
CornerRadius="0"
BorderBrush="Transparent">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="15"/>
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0"
IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource TemplatedParent}}"/>
<TextBlock Grid.Column="1"
TextAlignment="Left"
Foreground="Black">
<ContentPresenter/>
</TextBlock>
<Rectangle Grid.Column="2"
Stroke="Black"
Width="15"
Height="15"
Fill="{TemplateBinding Foreground}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="Gray"/>
<Setter TargetName="Border" Property="Background" Value="LightGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="cbSeparatorStyle" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Separator/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="cbResetStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border x:Name="Border"
Padding="5"
Margin="2"
BorderThickness="2"
CornerRadius="0"
BorderBrush="Transparent">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1">
<ContentPresenter/>
</TextBlock>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="Gray"/>
<Setter TargetName="Border" Property="Background" Value="LightGray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I guess my first question would be, is this the best way to make my ComboBox look like the screenshot I have presented?
Of course, there are deeper issues that I have yet to address. Firstly, the cbTestStyle of ComboBoxItem I want to be able to populate dynamically. Databinding would be my obvious go-to, but with the separator and "Reset" styles at the end, I'm not sure how to do this. I currently have the ComboBoxItems "hard-coded" in XAML:
<ComboBox x:Name="cbTestSelect"
Height="34"
Width="18"
IsEnabled="False">
<ComboBoxItem Style="{StaticResource cbTestStyle}" Content="Test 1" Foreground="#7FFF0000" Selected="ComboBoxItem_Selected"/>
<ComboBoxItem Style="{StaticResource cbTestStyle}" Content="Test 2" Foreground="#7F00FF00" Selected="ComboBoxItem_Selected"/>
<ComboBoxItem Style="{StaticResource cbTestStyle}" Content="Test 3" Foreground="#7F0000FF" Selected="ComboBoxItem_Selected"/>
<ComboBoxItem Style="{StaticResource cbSeparatorStyle}"/>
<ComboBoxItem Style="{StaticResource cbResetStyle}" Content="Reset all"/>
</ComboBox>
In this example, I would ideally like to dynamically create the first three items and have the separator and "reset" items remain static. I'm still relatively new to WPF. I felt like trying to create this control in WinForms (which the application this user control would be used in is) would be a lot more complicated. Plus I'm trying to steer us towards using WPF more anyway.
Any help or links to other questions or tutorials online would be greatly appreciated.
Solution 1:
Use a CompositeCollection so that you can bring up your data items with DataBinding, and use regular XAML to define the hard-coded items:
<Window x:Class="WpfApplication31.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication31"
Title="MainWindow" Height="350" Width="525"
x:Name="view">
<Window.Resources>
<DataTemplate DataType="{x:Type local:DataItem}">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"/>
<TextBlock Text="{Binding Text}"/>
<Rectangle Stroke="Black" StrokeThickness="1"
Fill="{Binding Color}" Width="20"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ComboBox VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="100" x:Name="Combo">
<ComboBox.Resources>
<CompositeCollection x:Key="ItemsSource">
<CollectionContainer Collection="{Binding DataContext,Source={x:Reference view}}"/>
<Separator Height="10"/>
<Button Content="Clear All"/>
</CompositeCollection>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<StaticResource ResourceKey="ItemsSource"/>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</Window>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var colors = new[] {"Red", "Green", "Blue", "Brown", "Cyan", "Magenta"};
this.DataContext =
Enumerable.Range(0, 5)
.Select(x => new DataItem
{
Text = "Test" + x.ToString(),
Color = colors[x],
IsChecked = x%2 == 0
});
}
}
Data Item:
public class DataItem
{
public bool IsChecked { get; set; }
public string Text { get; set; }
public string Color { get; set; }
}
Result:
Solution 2:
Using Expression Blend, you can get the XAML for the default Template for the ComboBox control, and modify this XAML to accomodate your extra visuals.
The XAML you get is rather long, and I'm not going to post it here. You will have to put that in a ResourceDictionary and reference that in the XAML where you define this ComboBox.
The relevant part you need to edit is the Popup:
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<DockPanel>
<Button Content="Clear All" DockPanel.Dock="Bottom"/>
<Separator Height="2" DockPanel.Dock="Bottom"/>
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</DockPanel>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
Notice that I added a DockPanel, the Button and a Separator.
Then you can bind your ItemsSource to the DataItem collection normally:
<ComboBox ItemsSource="{Binding}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Width="100"/>
Result:
Notice that this approach is a lot better than my previous solution, and other answers posted here, because it does not wrap the extra visuals in ComboBoxItems, and therefore you don't get the selection highlight for them, which is rather weird.
You could use a DataTemplateSelector with the DataTemplates defined in the XAML and some item type variable it the data you're binding to.
public class StyleSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate
{ get; set; }
public DataTemplate SeparatorTemplate
{ get; set; }
public DataTemplate ResetTemplate
{ get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var type = item as SomeType;
if (type != null)
{
switch (type.SomeItemTypeField)
{
case TypeENum.Separator: return SeparatorTemplate;
case TypeENum.Reset: return ResetTemplate;
default:
return DefaultTemplate;
}
}
return base.SelectTemplate(item, container);
}
}
Check out this more detailed example.
I think your best bet is to learn about DataTemplate and DataTemplateSelector.
Here is an blog post that will show you a simple example of using a DataTemplate.
The ComboBox Control
Essentially, you could bind your ComboBox to a collection of objects, and use a DataTemplateSelector to pick which template to use based on the type of object.

Custom ListBox in WPF

I am trying to create a custom ListBox control in WPF for a chat Messenger. I am using an ellipse to show the online/offline user. The ellipse is to be displayed on left and some text in center of the ListBoxItem.
I want to set the ellipse fill propert to red/green based on some variable.
This is what I have done :
<ListBox Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">
<Ellipse.Triggers>
<Trigger Property="{Binding Online}" Value="True">
<Setter TargetName="ellipse" Property="Ellipse.Fill" Value="Green"/>
</Trigger>
</Ellipse.Triggers>
</Ellipse>
<TextBlock Text="{Binding text}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in the code :
myList.Items.Add(new { text="Hello",Online="True" });
I am getting an error as
Cannot find the static member 'FillProperty' on the type 'ContentPresenter'.
What am I doing wrong here?
Obviously this is wrong: Property="{Binding Online}"
Also you should use a Style for triggers, no need to set TargetName, and you need to take precedence into consideration, and use a Setter for the default value.
you are actually misleading WPF with some of these concerns.
Binding A property on trigger will not work. you have to use DataTrigger insteed of Triggers.
Implementing Trigger on the Fly for any control. most of the times not work. So go with Styles.
While you are creating Ellipse in template make sure you have created enough size for it. So that can be visible to users.
try this.
<Window.Resources>
<Style x:Key="elstyle" TargetType="Ellipse">
<Setter Property="Height" Value="5"/>
<Setter Property="Width" Value="5"/>
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Online}" Value="true">
<Setter Property="Fill" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox x:Name="myList" HorizontalAlignment="Left" Height="232" Margin="117,74,0,0" VerticalAlignment="Top" Width="207">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Ellipse Name="ellipse" Margin="5" DockPanel.Dock="Left" Style="{DynamicResource elstyle}">
</Ellipse>
<TextBlock Text="{Binding Name}"></TextBlock>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
code behind .
public MainWindow()
{
Random r = new Random();
InitializeComponent();
for (int i = 0; i < 10; i++)
{
myList.Items.Add(new { Name = "Name" + i.ToString(), Online = Convert.ToBoolean(r.Next(-1, 1)) });
}
}
You need to set the initial colour via a Setter and not in XAML. For more information and see this question: Change the color of ellipse when mouse over
there are a few issues in your XAML
set the size of your Ellipse
you need to use Style instead of Ellipse.Triggers
set your Fill color in your Style if you wane be able to change it in XAML by some condition
here an working example for your problem
<DataTemplate>
<!--<DockPanel> juste because i like StackPanel-->
<StackPanel Orientation="Horizontal">
<!--<Ellipse Name="ellipse" Fill="Red" DockPanel.Dock="Left">-->
<Ellipse Name="ellipse" Width="15" Height="15">
<!--<Ellipse.Triggers>-->
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<!--<Trigger Property="{Binding Online}" Value="True">-->
<DataTrigger Binding="{Binding Online}" Value="True">
<Setter Property="Fill" Value="LimeGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding text}"/>
</StackPanel>
</DataTemplate>

How do I remove the X axis labels from this chart?

Keeping myself concise and to the point I wish to remove the labels or at the very least modify them from my X-Axis :
I seem to be using a modified version of this library that allows me to use some tools usually won't work in wp7.
xmlns:charting="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
This is the LineSeries definitions as of right now :
<charting:Chart x:Name="chart" Title="Humidity Readings" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Foreground="#FF3C58C8">
<chartingToolkit:LineSeries x:Name="HumidityGraph" Title="Humidity" BorderThickness="2" ItemsSource="{Binding Data}" IndependentValuePath="Time" DependentValuePath="Measurement"/>
</charting:Chart>
In response to chridam I tried implementing his suggestion - No succes.
<charting:Chart x:Name="chart" Title="Humidity Readings" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Foreground="#FF3C58C8">
<charting:Chart.LegendStyle>
<Style TargetType="visualizationToolkit:Legend">
<Setter Property="Width" Value="0"/>
<Setter Property="Height" Value="0"/>
</Style>
</charting:Chart.LegendStyle>
<!--there doesn't seem to be an AxisLabelStyle that I can create a style in.-->
<Style x:Key="EmptyStyle" TargetType="charting:NumericAxisLabel">
<Setter Property="StringFormat" Value="" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:NumericAxisLabel">
<TextBlock />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<chartingToolkit:LineSeries x:Name="HumidityGraph" Title="Humidity" BorderThickness="2" ItemsSource="{Binding Data}" IndependentValuePath="Time" DependentValuePath="Measurement" TransitionDuration="0:1:1.5" FontSize="18.667">
<charting:LinearAxis AxisLabelStyle="{StaticResource EmptyStyle}" Orientation="X" ShowGridLines="True"/>
</chartingToolkit:LineSeries>
</charting:Chart>
Try specifying a style for Axis Label that sets the StringFormat property value to empty
<chartingToolkit:CategoryAxis>
<chartingToolkit:CategoryAxis.AxisLabelStyle>
<Style TargetType="AxisLabel">
<Setter Property="StringFormat" Value=""></Setter>
</Style>
</chartingToolkit:CategoryAxis.AxisLabelStyle>
</chartingToolkit:CategoryAxis>
Update:
Another alternative is to apply the style template
<Style x:Key="EmptyStyle" TargetType="charting:NumericAxisLabel">
<Setter Property="StringFormat" Value="" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="charting:NumericAxisLabel">
<TextBlock />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
to this:
<charting:LineSeries.DependentRangeAxis>
<charting:LinearAxis AxisLabelStyle="{StaticResource EmptyStyle}"
Orientation="X"
ShowGridLines="True"/>
</charting:LineSeries.DependentRangeAxis>
For more resources on styling the chart, Styling a Silverlight Chart

Categories

Resources