WPF 自定义 LED 复选框

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42855236/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 14:02:50  来源:igfitidea点击:

WPF Custom LED Checkbox

c#.netwpfwinforms

提问by Meister Schnitzel

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.

我目前正在尝试将一些控件从 WindowsForms“移植”到 WPF。我有这个时尚的 LED 复选框,并尝试在 wpf 中实现相同的视觉外观。但我无法完成。

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

这就是 winforms 控件的样子 在此处输入图片说明

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).

彩色圆圈大小取决于控件的大小。颜色可由用户定义。颜色用于圆圈和文本。如果它被选中并且在未选中时变暗/灰色,则它是明亮的。diark 和高光颜色是根据控制颜色(较浅/较深)计算的。

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.

到目前为止,我在 wpf 中所做的所有尝试几乎都失败了。:-( 我第一次尝试用用户控件来做它,但决定从复选框派生它会更容易,只需要一个额外的选项来设置颜色。

<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:

这是我的 LedControl 代码:

<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:

问题是控件不起作用。我将颜色设置为 forrestGreen,但在设计器中显示为红色,如果我执行程序:

enter image description here

在此处输入图片说明

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

文本“Some Option”也未显示。

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.

led 的外观也不如在 winforms 中那么酷,但我不知道将代码转换为 wpf。

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

这是在win-Forms中绘制led的代码部分:

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.

我第二次尝试以复选框为基础几乎没有用处,但也许有人热衷并可以用 LED 替换复选框。

Any help is appreciated!

任何帮助表示赞赏!

回答by ASh

here is a LedControl derived from CheckBox. LedControl itself adds OnColorand OffColorproperties.

这是从 CheckBox 派生的 LedControl。LedControl 本身添加OnColorOffColor属性。

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 LedBorderellipse, white CenterGlowellipse, white CornerLightshape and of course ContentPresent. LedBorderadapts to LedControlheight. Depending on IsCheckedLedBorderis colored with OnColoror OffColor(as well as Foreground). Disabled control is grayed.

视觉外观是通过样式和模板定制的。主要模板部分是LedBorder椭圆、白色CenterGlow椭圆、白色CornerLight形状,当然还有 ContentPresent。LedBorder适应LedControl高度。取决于IsCheckedLedBorderOnColorOffColor(以及前景)着色。禁用控件呈灰色。

<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

在此处输入图片说明

回答by Reza Aghaei

You can use PathGradientBrushto draw a radial gradient. Here is the result of the code which I wrote. You can use any color as CheckedColorand UnCheckedColor, I used Redand Greento get this result:

您可以使用PathGradientBrush绘制径向渐变。这是我编写的代码的结果。你可以使用任何颜色作为CheckedColorand UnCheckedColor,我使用RedandGreen来得到这个结果:

enter image description here

在此处输入图片说明

Code

代码

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class MyCheckBox : CheckBox
{
    public MyCheckBox()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.DoubleBuffered = true;
        this.ResizeRedraw = true;
        CheckedColor = Color.Green; ;
        UnCheckedColor = Color.Red; ;
    }
    [DefaultValue(typeof(Color), "Green")]
    public Color CheckedColor { get; set; }
    [DefaultValue(typeof(Color), "Red")]
    public Color UnCheckedColor { get; set; }
    protected override void OnPaint(PaintEventArgs e)
    {
        var darkColor = Color.Black;
        var lightColor = Color.FromArgb(200, Color.White);
        var cornerAlpha = 80;
        this.OnPaintBackground(e);
        using (var path = new GraphicsPath())
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            var rect = new Rectangle(0, 0, Height, Height);
            path.AddEllipse(rect);
            rect.Inflate(-1, -1);
            using (var bgBrush = new SolidBrush(darkColor))
            {
                e.Graphics.FillEllipse(bgBrush, rect);
            }
            using (var pathGrBrush = new PathGradientBrush(path))
            {
                var color = Checked ? CheckedColor : UnCheckedColor;
                pathGrBrush.CenterColor = color; ;
                Color[] colors = { Color.FromArgb(cornerAlpha, color) };
                pathGrBrush.SurroundColors = colors;
                e.Graphics.FillEllipse(pathGrBrush, rect);
            }
            using (var pathGrBrush = new PathGradientBrush(path))
            {
                pathGrBrush.CenterColor = lightColor; ;
                Color[] colors = { Color.Transparent };
                pathGrBrush.SurroundColors = colors;
                var r = (float)(Math.Sqrt(2) * Height / 2);
                var x = r / 8;
                e.Graphics.FillEllipse(pathGrBrush, new RectangleF(-x, -x, r, r));
                e.Graphics.ResetClip();
            }
        }
        TextRenderer.DrawText(e.Graphics, Text, Font,
                new Rectangle(Height, 0, Width - Height, Height), ForeColor,
                 TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
    }
}