wpf自定义带刻度的柱状图控件

Posted chlm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了wpf自定义带刻度的柱状图控件相关的知识,希望对你有一定的参考价值。

效果图:

技术分享图片

主要代码xaml:

<UserControl x:Class="INSControls._01Conning.Steer.ConningSpeedBar"
             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:ec="http://schemas.microsoft.com/expression/2010/controls"
             xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
             mc:Ignorable="d"
             d:DesignHeight="300"
             d:DesignWidth="80" Focusable="False" FocusVisualStyle="{x:Null}" Loaded="UserControl_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>
        <Label HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Content="{Binding Title, RelativeSource={RelativeSource AncestorType=UserControl}}"
               Foreground="{Binding  LabelColor, RelativeSource={RelativeSource AncestorType=UserControl} }"
               FontFamily="微软雅黑"></Label>
        <Label HorizontalAlignment="Left"
               VerticalAlignment="Center"
               Margin="0,0,0,5"
               Foreground="#cc8800"
               FontFamily="微软雅黑"
               Content="LOG2"
               Grid.Row="1" />
        <Border Width="80"
                HorizontalAlignment="Left"
                Grid.Row="2"
                Background="#24325f">
            <Grid Margin="1,14,0,14"
                  x:Name="mainGrid">
                <!--具体的值填充的柱状图形-->
                <Grid x:Name="graphicGrid"
                      Height="10"
                      VerticalAlignment="Top"  RenderTransformOrigin="0.5,0.5">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions> 
                    
                         
                             <Path   Data="M40,0 L80,40 L0,40 z"
                              Fill="#cc0663c2" Stretch="Fill"  x:Name="path"  /> 

                    <Border Grid.Row="1"
                            Background="#cc0663c2" />
                    <Grid.RenderTransform>
                        <RotateTransform Angle="0" x:Name="polygonRotateAngel" />
                    </Grid.RenderTransform>
                </Grid>
                <!--短刻度-->
                <ec:PathListBox Background="Transparent"
                                x:Name="shortTicks"
                                ItemsSource="{Binding ShortTicks,RelativeSource={RelativeSource AncestorType=UserControl}}"
                                Focusable="False"
                                FocusVisualStyle="{x:Null}">
                    <ec:PathListBox.ItemTemplate>
                        <DataTemplate>
                            <Rectangle Width="1"
                                       Height="48"
                                       Margin="0,0,0,49"
                                       Fill="#b5b5b5"
                                       Focusable="False"
                                       FocusVisualStyle="{x:Null}" />
                            <!--<Border Width="1"
                                    Height="48"
                                    Background="#b5b5b5"
                                    UseLayoutRounding="True"
                                    Margin="0,0,0,49" />-->
                        </DataTemplate>
                    </ec:PathListBox.ItemTemplate>
                    <ec:PathListBox.LayoutPaths>
                        <ec:LayoutPath  Distribution="Even"
                                        Orientation="OrientToPath"
                                        SourceElement="{Binding ElementName=ShortTickPath}" >

                        </ec:LayoutPath>
                    </ec:PathListBox.LayoutPaths>
                </ec:PathListBox>
                <!--  长刻度  -->
                <ec:PathListBox x:Name="LongTick"  
                                IsHitTestVisible="False"
                                ItemsSource="{Binding LongTicks, RelativeSource={RelativeSource AncestorType=UserControl}}"
                                Focusable="False"
                                FocusVisualStyle="{x:Null}" >
                    <ec:PathListBox.ItemTemplate>
                        <DataTemplate>
                            <Rectangle Width="48"
                                       Height="3"
                                       Margin="48,0,0,0"
                                       Fill="White"
                                       Focusable="False"
                                       FocusVisualStyle="{x:Null}" />
                            <!--<Border Width="3"
                                    Height="48"
                                    Background="White"
                                    SnapsToDevicePixels="True"
                                    UseLayoutRounding="True"
                                    Margin="0,0,0,49">

                            </Border>-->
                        </DataTemplate>
                    </ec:PathListBox.ItemTemplate>
                    <ec:PathListBox.LayoutPaths>
                        <ec:LayoutPath Distribution="Even"
                                       Orientation="None"
                                       SourceElement="{Binding ElementName=LongTickPath}" />
                    </ec:PathListBox.LayoutPaths>
                </ec:PathListBox>

                <!--  刻度上显示的数字  -->
                <ec:PathListBox IsHitTestVisible="False"
                                ItemsSource="{Binding TickMarks,RelativeSource={RelativeSource AncestorType=UserControl}}">
             
                    
                    <ec:PathListBox.ItemTemplate>
                        <DataTemplate>
                            <TextBlock x:Name="tb" HorizontalAlignment="Left" Foreground="White"
                                       Text="{Binding}"  RenderTransformOrigin="0,0" Margin="0,50,0,0">
                                <TextBlock.RenderTransform>
                                    <RotateTransform Angle="-90"/>
                                </TextBlock.RenderTransform>
                            </TextBlock>
                        </DataTemplate>
                    </ec:PathListBox.ItemTemplate>
                    <ec:PathListBox.LayoutPaths>
                        <ec:LayoutPath Distribution="Even"   
                                       Orientation="OrientToPath"   
                                       SourceElement="{Binding ElementName=NumberPath}" />
                    </ec:PathListBox.LayoutPaths>
                </ec:PathListBox>

                <Path   x:Name="LongTickPath"
                        Data="M0,0 v1"
                        VerticalAlignment="Top"
                        HorizontalAlignment="Right"
                        Stretch="Fill" Fill="Red" Stroke="Red" StrokeThickness="2"
                        Grid.RowSpan="2"
                        Margin="0,0"
                        Focusable="False"
                        FocusVisualStyle="{x:Null}" />
                <Path x:Name="ShortTickPath"
                      Data="M0,0 V1"
                      VerticalAlignment="Top"
                      HorizontalAlignment="Left"
                      Stretch="Fill"
                      Grid.RowSpan="2"
                      Margin="0,0"
                      Focusable="False"
                      FocusVisualStyle="{x:Null}" />

                <Path x:Name="NumberPath"
                      Data="M0,0 V1"
                      Margin="45,0,0,0"
                      VerticalAlignment="Top"
                      HorizontalAlignment="Center"
                      Stretch="Fill"
                      Grid.RowSpan="2"
                      Focusable="False"
                      FocusVisualStyle="{x:Null}"  Stroke="Yellow" StrokeThickness="1"/>

            </Grid>
        </Border>

        <StackPanel Grid.Row="3"
                    Orientation="Horizontal">
            <TextBox Width="90"
                     Height="35"
                     IsReadOnly="True"
                     Foreground="{Binding TextboxColor,RelativeSource={RelativeSource AncestorType=UserControl}}"
                     VerticalAlignment="Center"
                     Text="{ Binding CurrentValue, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Control}}" />
            <Label VerticalAlignment="Center"
                   Content="km"
                   Foreground="{Binding LabelColor, RelativeSource={RelativeSource AncestorType=UserControl}}"></Label>
        </StackPanel>

    </Grid>
</UserControl>

  .cs文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace INSControls._01Conning.Steer
{
    /// <summary>
    /// ConningSpeedBar.xaml 的交互逻辑
    /// </summary>
    public partial class ConningSpeedBar : UserControl
    {
        public ConningSpeedBar()
        {
            InitializeComponent();
        }

        public List<string> TickMarks
        {
            get { return (List<string>)GetValue(TickMarksProperty); }
            set { SetValue(TickMarksProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TickMarks.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TickMarksProperty =
            DependencyProperty.Register("TickMarks", typeof(List<string>), typeof(ConningSpeedBar), new PropertyMetadata(null));




        public List<object> LongTicks
        {
            get { return (List<object>)GetValue(LongTicksProperty); }
            set { SetValue(LongTicksProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LongTicks.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LongTicksProperty =
            DependencyProperty.Register("LongTicks", typeof(List<object>), typeof(ConningSpeedBar), new PropertyMetadata(null));



        public List<object> ShortTicks
        {
            get { return (List<object>)GetValue(ShortTicksProperty); }
            set { SetValue(ShortTicksProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShortTicks.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShortTicksProperty =
            DependencyProperty.Register("ShortTicks", typeof(List<object>), typeof(ConningSpeedBar), new PropertyMetadata(null));




        public double CurrentValue
        {
            get { return (double)GetValue(CurrentValueProperty); }
            set { SetValue(CurrentValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurrentValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentValueProperty =
            DependencyProperty.Register("CurrentValue", typeof(double), typeof(ConningSpeedBar), new PropertyMetadata(0.0, CurrentValueChangeCallback));

        private static void CurrentValueChangeCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ConningSpeedBar c = d as ConningSpeedBar;
            UpdateUICurrentvalue((double)e.NewValue, c);
        }

        private static void UpdateUICurrentvalue(double currentValue, ConningSpeedBar c)
        {
            if (c.mainGrid.ActualHeight==0)
            {
                return;
            }
            if (currentValue > 0)
            {
                c.polygonRotateAngel.Angle = 0;
                double totalValue = c.MaxValue - c.MinValue;
                //计算显示图形位置
                double top = (c.MaxValue - currentValue) * c.mainGrid.ActualHeight / totalValue;
                //计算显示图形大小
                double height = currentValue * c.mainGrid.ActualHeight / totalValue;
                c.graphicGrid.Margin = new Thickness(0, top, 0, 0);
                if (height > 40)
                {
                    c.graphicGrid.Height = height;
                }
                else
                {
                    c.graphicGrid.Height = c.path.Height = height;
                }

            }
            else
            {
                c.polygonRotateAngel.Angle = 180;
                double totalValue = c.MaxValue - c.MinValue;
                //计算显示图形位置
                double top = (c.MaxValue) * c.mainGrid.ActualHeight / totalValue;
                //计算显示图形大小
                double height = -currentValue * c.mainGrid.ActualHeight / totalValue;
                c.graphicGrid.Margin = new Thickness(0, top, 0, 0);
                if (height > 40)
                {
                    c.graphicGrid.Height = height;
                }
                else
                {
                    c.graphicGrid.Height = c.path.Height = height;
                }
            }

        }

        public SolidColorBrush LabelColor
        {
            get { return (SolidColorBrush)GetValue(LabelColorProperty); }
            set { SetValue(LabelColorProperty, value); }
        }

        // Using a DependencyProperty as the backing store for LabelColor.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty LabelColorProperty =
            DependencyProperty.Register("LabelColor", typeof(SolidColorBrush), typeof(ConningSpeedBar), new PropertyMetadata(Brushes.Black));



        public SolidColorBrush TextboxColor
        {
            get { return (SolidColorBrush)GetValue(TextboxColorProperty); }
            set { SetValue(TextboxColorProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TextboxColor.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextboxColorProperty =
            DependencyProperty.Register("TextboxColor", typeof(SolidColorBrush), typeof(ConningSpeedBar), new PropertyMetadata(new SolidColorBrush(Color.FromRgb(214, 214, 214))));




        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(ConningSpeedBar), new PropertyMetadata(""));




        public double MaxValue
        {
            get { return (double)GetValue(MaxValueProperty); }
            set { SetValue(MaxValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaxValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxValueProperty =
            DependencyProperty.Register("MaxValue", typeof(double), typeof(ConningSpeedBar), new PropertyMetadata(30d));




        public double MinValue
        {
            get { return (double)GetValue(MinValueProperty); }
            set { SetValue(MinValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MinValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MinValueProperty =
            DependencyProperty.Register("MinValue", typeof(double), typeof(ConningSpeedBar), new PropertyMetadata(-10d));

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            //短刻度
            List<object> shortticks = new List<object>();
            for (int i = 0; i < 50; i++)
            {
                shortticks.Add(new object());
            }
            ShortTicks = shortticks;
            //显示刻度文字
            List<string> numbers = new List<string>();
            //长刻度
            List<object> longticks = new List<object>();
            for (int i = 0; i < 5; i++)
            {
                //计算长度信息,等比例地减去间隔值
                string tickInfo = (MaxValue - i * (MaxValue - MinValue) / 4).ToString();
                numbers.Add(tickInfo+new string(‘&‘,i+1));
                longticks.Add(new object());
            }
            LongTicks = longticks;
            TickMarks = numbers;
           UpdateUICurrentvalue(CurrentValue, this) ;
        }
         
    }
}

  源码地址:

https://files.cnblogs.com/files/chlm/%E5%88%BB%E5%BA%A6%E7%BA%BF.rar

 

以上是关于wpf自定义带刻度的柱状图控件的主要内容,如果未能解决你的问题,请参考以下文章

C# wpf 如何实现自定义控件,布局时,大小发生变化,内部绘制的曲线跟随变化?

Qt编写自定义控件45-柱状标尺控件

Qt编写自定义控件5-柱状温度计

WPF 自定义柱状图 BarChart

excel里做柱状图怎么做俩个Y轴刻度啊

WPF 自定义控件并使用(例如带水印和字体图标的文本框)