【问题标题】:WPF Custom LED CheckboxWPF 自定义 LED 复选框
【发布时间】:2017-03-17 10:36:39
【问题描述】:

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

我搜索了很多,但找不到我的问题/问题的解决方案。

这就是 winforms 控件的样子

彩色圆圈的大小取决于控件的大小。 颜色是用户可定义的。颜色用于圆圈和文本。 如果选中它是明亮的,当它未选中时它会变暗/灰色。 diark 和 highlight 颜色是根据控制颜色(较浅/较深)计算得出的。

到目前为止,我在 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>

这是我的 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>

以及背后的代码:

       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

}

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

文本“Some Option”也没有显示出来..

我还没有弄清楚如何让渐变颜色成为我想要的颜色的更深和更亮的版本。

led的外观也没有winforms那么酷, 但我不知道将代码翻译成 wpf。

这是在 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);
     }

  }

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

感谢任何帮助!

【问题讨论】:

    标签: c# .net wpf winforms


    【解决方案1】:

    这是一个从 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); }
        }
    }
    

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

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

    这是一个示例:

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

    【讨论】:

    • 感谢您分享您的知识。我做了一些修改以满足我的需要,但实际上这正是我所需要的。
    【解决方案2】:

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

    代码

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

    【讨论】:

    • 这看起来很酷。我会在假期后检查一下。所以给我一些时间。
    • 我以为您要在 Windows 窗体中创建一个 LED 复选框,所以我发布了答案,但似乎您需要反之亦然。但我更喜欢保留代码,因为我认为它是一个优雅的 Windows Forms LED CheckBox。
    • 别介意。我首先认为这是要走的路。但正如你提到的,它是 Windows 窗体。但我需要它用于 wpf。
    • 现在你的答案由@ASh 发布:)
    猜你喜欢
    • 2011-02-07
    • 2021-05-20
    • 2011-09-23
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    相关资源
    最近更新 更多