Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
351 views
in Technique[技术] by (71.8m points)

c# - WPF Custom LED Checkbox

I currently trying to "port" some control from WindowsForms to WPF. I Have this stylish led checkbox and try to achieve the same visual appearance in wpf. but I'm unable to get it done.

I've searched a lot butt cannot find a solution to my questions/problems.

This is how the winforms Control looks like enter image description here

The colored Circle size depends on the size of the control. The color is user definable. The color is used for the circle and the Text. It's bright if it ich checked and dimmed / gray when it's unchecked. The diark and highlight colors are calculated from the control color (lighter/darker).

All my tries to do the same in wpf pretty much failed up to now. :-( I fist tried to do it with an usercontrol, but decided it would be easier to have it derived from checkbox with just an extra option to set the color.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
    xmlns:test="clr-namespace:LedTest"
    xmlns:uc="clr-namespace:WPFTest;assembly=LedControl"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    x:Class="LedTest.MainWindow"
    Title="MainWindow" Height="285" Width="566">
   <Window.Resources>
      <ResourceDictionary x:Key="ResDict2"  Source="Dictionary2.xaml"/>
   </Window.Resources>
   <Grid Margin="0">
      <Grid.RowDefinitions>
         <RowDefinition Height="Auto" MinHeight="27" />
         <RowDefinition Height="75"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="10*" />
         <ColumnDefinition Width="179*"/>
      </Grid.ColumnDefinitions>
      <uc:LedControl x:Name="led1" 
            Color="ForestGreen" Text="Some Option"
         Grid.Column="1" Grid.Row="1" Height="39" VerticalAlignment="Bottom" Margin="0,0,0,36"/>
      <CheckBox Content="Some Option" Style="{DynamicResource TestStyle}" Margin="0,0,31,0" Grid.Column="1"/>
   </Grid>
</Window>

This is my LedControl code:

<UserControl x:Class="LedControl"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="100" d:DesignWidth="300">
   <UserControl.Resources>
   </UserControl.Resources>
   <StackPanel x:Name="gridBigLed" Orientation="Horizontal" >
      <Border x:Name="border1"
                BorderThickness="1" 
                Width="{Binding ActualHeight, ElementName=gridBigLed, Mode=OneWay}" 
                CornerRadius="{Binding ActualWidth, ElementName=gridBigLed, Mode=OneWay}"
                HorizontalAlignment="Left">
         <Border.Background>
            <RadialGradientBrush GradientOrigin="0.2,0.2">
               <GradientStop Color="#FFFFAAAA"/>
               <GradientStop x:Name="backgroundColor" Color="Red" Offset="1.2"/>
            </RadialGradientBrush>
         </Border.Background>
         <Border.BorderBrush>
            <RadialGradientBrush>
               <GradientStop x:Name="GradientColorLow" Color="#FF660000" Offset="0.383"/>
               <GradientStop x:Name="GradientColorHigh" Color="#330000" Offset="0.5"/>
            </RadialGradientBrush>
         </Border.BorderBrush>
      </Border>
      <Label Content="{Binding Text}" x:Name="LEDText" Foreground="Red"    HorizontalContentAlignment="Left" VerticalContentAlignment="Center"/>
   </StackPanel>
</UserControl>

and the code behind:

       public partial class LedControl : UserControl
   {
      #region Dependency properties
      /// <summary>Dependency property to Get/Set the current IsActive (True/False)</summary>
      public static readonly DependencyProperty IsCheckedProperty =
          DependencyProperty.Register("IsChecked", typeof(bool?), typeof(LedControl),
              new PropertyMetadata(null, new PropertyChangedCallback(LedControl.IsCheckedPropertyChanced)));

      /// <summary>Dependency property to Get/Set Color when IsActive is true</summary>
      public static readonly DependencyProperty ColorProperty =
          DependencyProperty.Register("Color", typeof(Color), typeof(LedControl),
              new PropertyMetadata(Colors.Green, new PropertyChangedCallback(LedControl.OnColorPropertyChanged)));

        public static readonly DependencyProperty TextProperty =
          DependencyProperty.Register("Text", typeof(string), typeof(LedControl),
              new PropertyMetadata("ButtonText", new PropertyChangedCallback(LedControl.OnTextPropertyChanged)));
      #endregion

      #region Properties
      /// <summary>Gets/Sets Text Value</summary>
      public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
      /// <summary>Gets/Sets Value</summary>
      public bool? IsChecked { get { return (bool?)GetValue(IsCheckedProperty); } set { SetValue(IsCheckedProperty, value); } }
      /// <summary>Gets/Sets Color</summary>
      public Color Color { get { return (Color)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } }
      #endregion

      #region Constructor
      public LedControl()
      {
         InitializeComponent();
         if (this.IsChecked == true)
         {
            this.LEDColor.Color = this.Color;
            this.LEDText.Foreground = new SolidColorBrush(this.Color);
         }
         else if (this.IsChecked == false)
         {
            this.LEDColor.Color = Colors.Gray;
            this.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
         }
      }

      #endregion

  #region Callbacks

  private static void IsCheckedPropertyChanced(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
     LedControl led = (LedControl)d;

     if (led.IsChecked == true)
     {
        led.LEDColor.Color = led.Color;
        led.LEDText.Foreground = new SolidColorBrush(led.Color);
     }
     else
     {
        led.LEDColor.Color = Colors.Gray;   // TODO calculate dark/gray color
        led.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
     }
  }

  private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
     LedControl led = (LedControl)d;
     led.Color = (Color)e.NewValue;
     if (led.IsChecked == true)
     {   
        led.LEDColor.Color = led.Color;
        led.LEDText.Foreground = new SolidColorBrush( led.Color );
     }
  }

  private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
     LedControl led = (LedControl)d;
     led.Text = (String)e.NewValue;
  }

  #endregion

}

The thing is that the control does not work. I set the Color to forrestGreen, but shows up red in designer and if I execute the program:

enter image description here

The text "Some Option" is not shown as well..

I haven't figured out how to have the gradient colors to be darker and lighter versions of the color I want.

The look of the led is also not as cool as in winforms, but I have no clue to translate the code to wpf.

here is the part of the code that draws the led in win-Forms:

private void drawControl(Graphics g, bool on) {
     // Is the bulb on or off
     Color lightColor = (on) ? this.Color : Color.FromArgb(100, this.Color);
     Color darkColor = (on) ? this.DarkColor : Color.Gray/*this.DarkDarkColor*/;

     // Calculate the dimensions of the bulb
     int width = this.Width - (this.Padding.Left + this.Padding.Right);
     int height = this.Height - (this.Padding.Top + this.Padding.Bottom);
     // Diameter is the lesser of width and height
     int diameter = Math.Min(width, height);
     // Subtract 1 pixel so ellipse doesn't get cut off
     diameter = Math.Max(diameter - 1, 1);

     SolidBrush br = new SolidBrush(BackColor);
     g.FillRectangle(br, ClientRectangle);

     // Draw the background ellipse
     var rectangle = new Rectangle(this.Padding.Left, this.Padding.Top, diameter, diameter);
     g.FillEllipse(new SolidBrush(darkColor), rectangle);

     // Draw the glow gradient
     var path = new GraphicsPath();
     path.AddEllipse(rectangle);
     var pathBrush = new PathGradientBrush(path);
     pathBrush.CenterColor = lightColor;
     pathBrush.SurroundColors = new Color[] { Color.FromArgb(0, lightColor) };
     g.FillEllipse(pathBrush, rectangle);

     // Draw the white reflection gradient
     var offset = Convert.ToInt32(diameter * .15F);
     var diameter1 = Convert.ToInt32(rectangle.Width * .8F);
     var whiteRect = new Rectangle(rectangle.X - offset, rectangle.Y - offset, diameter1, diameter1);
     var path1 = new GraphicsPath();
     path1.AddEllipse(whiteRect);
     var pathBrush1 = new PathGradientBrush(path);
     pathBrush1.CenterColor = _reflectionColor;
     pathBrush1.SurroundColors = _surroundColor;
     g.FillEllipse(pathBrush1, whiteRect);

     // Draw the border
     g.SetClip(this.ClientRectangle);
     if (this.On) 
        g.DrawEllipse(new Pen(Color.FromArgb(85, Color.Black),1F), rectangle);

     if (this.Text != string.Empty)
     {
        RectangleF textArea = this.ClientRectangle;
        textArea.X += rectangle.Width + 6;
        textArea.Width -= (diameter + 6);
        Font fon = new Font(Font.FontFamily, Font.Size-1, FontStyle.Bold);

        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Near;
        sf.LineAlignment = StringAlignment.Center;

        if (!this.On)
           g.DrawString(this.Text, fon, new SolidBrush(Color.Gray), textArea, sf);
        else
           g.DrawString(this.Text, fon, new SolidBrush(darkColor), textArea, sf);
     }

  }

My second try with the checkbox as base is nore or less useless, but perhaps someone is keen and can substitute the checkbox with the led.

Any help is appreciated!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

here is a LedControl derived from CheckBox. LedControl itself adds OnColor and OffColor properties.

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

    public static readonly DependencyProperty OnColorProperty =
        DependencyProperty.Register("OnColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Green));

    public Brush OnColor
    {
        get { return (Brush)GetValue(OnColorProperty); }
        set { SetValue(OnColorProperty, value); }
    }

    public static readonly DependencyProperty OffColorProperty = 
        DependencyProperty.Register("OffColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Red));

    public Brush OffColor
    {
        get { return (Brush)GetValue(OffColorProperty); }
        set { SetValue(OffColorProperty, value); }
    }
}

and visual appearance is customized via Style and Template. Main template parts are LedBorder ellipse, white CenterGlow ellipse, white CornerLight shape and of course ContentPresent. LedBorder adapts to LedControl height. Depending on IsChecked LedBorder is colored with OnColor or OffColor (as well as Foreground). Disabled control is grayed.

<Style TargetType="local:LedControl">
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="BorderBrush" Value="Black"/>
    <Setter Property="Margin" Value="5"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:LedControl">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <Grid Background="Transparent" Name="grd"
                            Margin="{TemplateBinding Padding}"
                            VerticalAlignment="Stretch" 
                            Width="{Binding Path=ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">

                        <Ellipse x:Name="LedBorder" 
                                    Fill="{TemplateBinding Background}"
                                    Stroke="{TemplateBinding BorderBrush}"
                                    StrokeThickness="2"
                                    Stretch="Uniform"/>

                        <Ellipse x:Name="CenterGlow" Stretch="Uniform">
                            <Ellipse.Fill>
                                <RadialGradientBrush>
                                    <GradientStop Color="White" Offset="-0.25"/>
                                    <GradientStop Color="Transparent" Offset="0.91"/>
                                </RadialGradientBrush>
                            </Ellipse.Fill>
                        </Ellipse>

                        <Ellipse x:Name="CornerLight" Stretch="Uniform" Margin="2">
                            <Ellipse.Fill>
                                <RadialGradientBrush Center="0.15 0.15" RadiusX="0.5" RadiusY="0.5">
                                    <GradientStop Color="White" Offset="0"/>
                                    <GradientStop Color="Transparent" Offset="1"/>
                                </RadialGradientBrush>
                            </Ellipse.Fill>
                        </Ellipse>
                    </Grid>

                    <ContentPresenter x:Name="content" Grid.Column="1" Margin="4,0,0,0"
                            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                            RecognizesAccessKey="True"/>

                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="true">
                        <Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>

                    <Trigger Property="IsChecked" Value="false">
                        <Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Trigger>

                    <Trigger Property="IsEnabled" Value="false">
                        <Setter TargetName="CenterGlow" Property="Fill">
                            <Setter.Value>
                                <RadialGradientBrush Opacity="1">
                                    <GradientStop Color="Transparent" Offset="-0.5" />
                                    <GradientStop Color="#888" Offset="1" />
                                </RadialGradientBrush>
                            </Setter.Value>
                        </Setter>
                        <Setter TargetName="content" Property="TextElement.Foreground" Value="#888"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and here is a sample:

<StackPanel>
    <local:LedControl Content="Disabled OFF" Height="24" IsChecked="False" IsEnabled="False" />
    <local:LedControl Content="Disabled ON" Height="32" IsChecked="True" IsEnabled="False" />
    <local:LedControl Content="Enabled OFF" OffColor="Chocolate" IsChecked="False" Height="40" />
    <local:LedControl Content="Enabled ON" OnColor="Navy" IsChecked="True" Height="48" />
</StackPanel>

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...