Toggle switch content not displayed in DataGrid

716 Views Asked by At

I'm using a custom style for my toggle switch. The style is applied and it works fine and it shows "ON" or "OFF". The problem is when I add the toggle switch to the DataGrid.

The DataGrid item source is binding to a model and the column isActive contains the toggle switch control. The toggle will take the green color if isActive is true or red color if is false, but it doesn't show the content text ( "ON" , "OFF").

Style of the toggle Switch in Resources file:

<Style x:Key="ActiveToggleSwitch" TargetType="{x:Type ToggleButton}">
    <Setter Property="IsChecked" Value="False"/>
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="Template">

        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Grid x:Name="toggleSwitch">
                    <Border x:Name="Border" CornerRadius="10"
        Background="#C2283B"
        Width="90" Height="25">
                        <Border.Effect>
                            <DropShadowEffect ShadowDepth="0.6" Direction="0" Opacity="0.3" />
                        </Border.Effect>
                        <Ellipse x:Name="Ellipse" Fill="#FFFFFFFF" Stretch="Uniform"
             Margin="2 2 2 1"
             Stroke="Gray" StrokeThickness="0.2"
             HorizontalAlignment="Left" Width="22" >
                            <Ellipse.Effect>
                                <DropShadowEffect BlurRadius="10" ShadowDepth="1" Opacity="0.3" Direction="260" />
                            </Ellipse.Effect>
                        </Ellipse>
                    </Border>

                    <TextBlock x:Name="txtOff" Text="{Binding TextOFF}" Margin="0 0 40 0" VerticalAlignment="Center" FontWeight="DemiBold" HorizontalAlignment="Right" Foreground="White" FontSize="10" />
                    <TextBlock Opacity="0" x:Name="txtOn"  Text="{Binding TextON}" Margin="40 0 0 0" VerticalAlignment="Center" FontWeight="DemiBold"  Foreground="White" HorizontalAlignment="Left" FontSize="10" />
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="ToggleButton.IsChecked" Value="True" >
                        <Trigger.EnterActions>

                            <BeginStoryboard>

                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                To="#34A543"
                                Duration="0:0:0.1" />
                                    <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                    Storyboard.TargetProperty="Margin"
                                    To="60 2 2 1"
                                    Duration="0:0:0.1" />
                                    <DoubleAnimation
                            Storyboard.TargetName="txtOff" 
                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                            From="1.0" To="0.0" Duration="0:0:0:0.1"     />
                                    <DoubleAnimation
                            Storyboard.TargetName="txtOn" 
                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                            From="0.0" To="1.0" Duration="0:0:0:0.1"  />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                        <!--  some out fading  -->
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="Border"
                                Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                To="#C2283B"
                                Duration="0:0:0.1" />
                                    <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                    Storyboard.TargetProperty="Margin"
                                    To="2 2 2 1"
                                    Duration="0:0:0.1" />
                                    <DoubleAnimation
                            Storyboard.TargetName="txtOff" 
                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                            From="0" To="1.0" Duration="0:0:0:0.1"       />
                                    <DoubleAnimation
                            Storyboard.TargetName="txtOn" 
                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                            From="1.0" To="0.0" Duration="0:0:0:0.1" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                        <Setter Property="Foreground" Value="{DynamicResource IdealForegroundColorBrush}" />
                    </Trigger>
                    <Trigger Property="IsMouseOver" Value="False">
                    </Trigger>

                    <Trigger Property="IsEnabled" Value="False">
                        <Setter Property="Foreground" Value="{DynamicResource GrayBrush7}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="VerticalContentAlignment" Value="Center" />
</Style>

MainWindow.xaml:

<telerik:GridViewDataColumn x:Name="Activetoogle" IsReadOnly="True"  HeaderCellStyle="{StaticResource CustomGridViewHeaderCellStyle}" Header="{StaticResource ActivityTriggerSwitch}" DataMemberBinding="{Binding isActive}" Width="*">
    <telerik:GridViewDataColumn.CellTemplate>
        <DataTemplate>
            <StackPanel  Height ="Auto" >
                <ToggleButton  x:Name="isActive"  VerticalAlignment="Center" IsEnabled="False"  Style="{StaticResource ActiveToggleSwitch}"
                 HorizontalAlignment="Right" Width="auto" FlowDirection="RightToLeft" IsChecked="{Binding empisActive}" Content="{Binding ToogleContent}"  />
            </StackPanel>
        </DataTemplate>
    </telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>

MainWindow.xaml.cs

public MainWindow(){
    InitializeComponent();
    TextOFF = (string)Application.Current.Resources["NotActive"];
    TextON = (string)Application.Current.Resources["IsActive"];
}
// ...

I'm trying to get the text displayed in the column of the toggle switch, how can I do that?

Note : This works if the toggle switch is not created inside the data template.

The toggle button should contain the text ("ON" or "OFF").

DataGrid with toggle switches.

2

There are 2 best solutions below

3
On

You can use string resource directly in xaml, using ViewModel in your case is unnecessary step

xmlns:stringResources="clr-namespace:YourApp.Properties"

<TextBlock x:Name="txtOff" Text="{x:Static stringResources:Resources.NotActive}" Margin="0 0 40 0" VerticalAlignment="Center" FontWeight="DemiBold" HorizontalAlignment="Right" Foreground="White" FontSize="10" />
9
On

The TextOFF and TextON properties are defined in your MainWindow, not in the data context of the current item in the grid view column. Since your bindings are hard-wired to the current data context, this does not work, the properties are not defined there.

Dirty Workaround

You could change the data context like below, which is a really dirty workaround and I do not recommend it. It sets the data context to the MainWindow, so the ToggleButton works and exploits the StackPanel data context to bind items.

<ToggleButton  x:Name="isActive"
               VerticalAlignment="Center"
               IsEnabled="False"
               Style="{StaticResource ActiveToggleSwitch}"
               HorizontalAlignment="Right"
               Width="auto"
               FlowDirection="RightToLeft"
               DataContext="{Binding RelativeSource={RelativeSource AncestorType={x:Type YourProject:MainWindow}}}"
               IsChecked="{Binding DataContext.empisActive, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}"
               Content="{Binding DataContext.ToogleContent, RelativeSource={RelativeSource AncestorType={x:Type StackPanel}}}" />

Solutions with Dependency Properties

You should not use a binding on a property path directly in a control template. This binding will always rely on the exact property names TextOFF and TextON and the current data context, instead use one of these options:

  • Create a custom control derived from ToggleButton and add dependency properties for the On and Off texts.
  • Create custom attached properties to for the On and Off texts.

In both cases, you can bind the texts from outside. I prefer the first option, because your switch button might have other properties that it could expose which fit its special behavior or appearance.

public class SwitchButton : ToggleButton
{
   static SwitchButton()
   {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(SwitchButton),
         new FrameworkPropertyMetadata(typeof(SwitchButton)));
   }

   public static readonly DependencyProperty OffTextProperty = DependencyProperty.Register(
      nameof(OffText), typeof(string), typeof(SwitchButton));

   public static readonly DependencyProperty OnTextProperty = DependencyProperty.Register(
      nameof(OnText), typeof(string), typeof(SwitchButton));

   public string OffText
   {
      get => (string)GetValue(OffTextProperty);
      set => SetValue(OffTextProperty, value);
   }

   public string OnText
   {
      get => (string)GetValue(OnTextProperty);
      set => SetValue(OnTextProperty, value);
   }
}

Now, remove the x:Key from your template and change the types to SwitchButton. Without the key, the style will be implicit and applied to all SwitchButton controls in scope automatically. Please also notice that the bindings are now TemplateBindings that bind to the dependency properties of the SwitchButton.

<Style TargetType="{x:Type YourProject:SwitchButton}">
   <Setter Property="IsChecked"
           Value="False" />
   <Setter Property="HorizontalAlignment"
           Value="Left" />
   <Setter Property="VerticalAlignment"
           Value="Center" />
   <Setter Property="Template">

      <Setter.Value>
         <ControlTemplate TargetType="{x:Type YourProject:SwitchButton}">
            <Grid x:Name="toggleSwitch">
               <Border x:Name="Border"
                       CornerRadius="10"
                       Background="#C2283B"
                       Width="90"
                       Height="25">
                  <Border.Effect>
                     <DropShadowEffect ShadowDepth="0.6"
                                       Direction="0"
                                       Opacity="0.3" />
                  </Border.Effect>
                  <Ellipse x:Name="Ellipse"
                           Fill="#FFFFFFFF"
                           Stretch="Uniform"
                           Margin="2 2 2 1"
                           Stroke="Gray"
                           StrokeThickness="0.2"
                           HorizontalAlignment="Left"
                           Width="22">
                     <Ellipse.Effect>
                        <DropShadowEffect BlurRadius="10"
                                          ShadowDepth="1"
                                          Opacity="0.3"
                                          Direction="260" />
                     </Ellipse.Effect>
                  </Ellipse>
               </Border>

               <TextBlock x:Name="txtOff"
                          Text="{TemplateBinding OffText}"
                          Margin="0 0 40 0"
                          VerticalAlignment="Center"
                          FontWeight="DemiBold"
                          HorizontalAlignment="Right"
                          Foreground="White"
                          FontSize="10" />
               <TextBlock Opacity="0"
                          x:Name="txtOn"
                          Text="{TemplateBinding OnText}"
                          Margin="40 0 0 0"
                          VerticalAlignment="Center"
                          FontWeight="DemiBold"
                          Foreground="White"
                          HorizontalAlignment="Left"
                          FontSize="10" />
            </Grid>

            <ControlTemplate.Triggers>
               <Trigger Property="ToggleButton.IsChecked"
                        Value="True">
                  <Trigger.EnterActions>

                     <BeginStoryboard>

                        <Storyboard>
                           <ColorAnimation Storyboard.TargetName="Border"
                                           Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                           To="#34A543"
                                           Duration="0:0:0.1" />
                           <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                               Storyboard.TargetProperty="Margin"
                                               To="60 2 2 1"
                                               Duration="0:0:0.1" />
                           <DoubleAnimation Storyboard.TargetName="txtOff"
                                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                                            From="1.0"
                                            To="0.0"
                                            Duration="0:0:0:0.1" />
                           <DoubleAnimation Storyboard.TargetName="txtOn"
                                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                                            From="0.0"
                                            To="1.0"
                                            Duration="0:0:0:0.1" />
                        </Storyboard>
                     </BeginStoryboard>
                  </Trigger.EnterActions>
                  <!--  some out fading  -->
                  <Trigger.ExitActions>
                     <BeginStoryboard>
                        <Storyboard>
                           <ColorAnimation Storyboard.TargetName="Border"
                                           Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)"
                                           To="#C2283B"
                                           Duration="0:0:0.1" />
                           <ThicknessAnimation Storyboard.TargetName="Ellipse"
                                               Storyboard.TargetProperty="Margin"
                                               To="2 2 2 1"
                                               Duration="0:0:0.1" />
                           <DoubleAnimation Storyboard.TargetName="txtOff"
                                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                                            From="0"
                                            To="1.0"
                                            Duration="0:0:0:0.1" />
                           <DoubleAnimation Storyboard.TargetName="txtOn"
                                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                                            From="1.0"
                                            To="0.0"
                                            Duration="0:0:0:0.1" />
                        </Storyboard>
                     </BeginStoryboard>
                  </Trigger.ExitActions>
                  <Setter Property="Foreground"
                          Value="{DynamicResource IdealForegroundColorBrush}" />
               </Trigger>
               <Trigger Property="IsMouseOver"
                        Value="False">
               </Trigger>

               <Trigger Property="IsEnabled"
                        Value="False">
                  <Setter Property="Foreground"
                          Value="{DynamicResource GrayBrush7}" />
               </Trigger>
            </ControlTemplate.Triggers>
         </ControlTemplate>
      </Setter.Value>
   </Setter>
   <Setter Property="VerticalContentAlignment"
           Value="Center" />
</Style>

This allows you to bind the Off and On texts from outside. In your case, you would need to use a RelativeSource binding to access the TextOFF and TextON properties in your MainWindow.

<YourProject:SwitchButton  x:Name="isActive"
                           VerticalAlignment="Center"
                           IsEnabled="False"
                           HorizontalAlignment="Right"
                           Width="auto"
                           FlowDirection="RightToLeft"
                           IsChecked="{Binding empisActive}"
                           Content="{Binding ToogleContent}"
                           OffText="{Binding TextOFF, RelativeSource={RelativeSource AncestorType={x:Type YourProject:MainWindow}}}"
                           OnText="{Binding TextON, RelativeSource={RelativeSource AncestorType={x:Type YourProject:MainWindow}}}"/>