WPF 创建一个刻度指数增长的图表

假设有一份数据,需要用柱图或条图表示,但是,有些类型的值很小,譬如不过1000,有些类型的值很大,譬如2000万了,这个时候普通的柱图显示已经不合理,所以自己开发一个坐标刻度是以指数增长的图表,在设计这个图表的时候,我选择了一个简单的UserControl来实现。这个图表主要有三个部分组成:
纵轴刻度
横轴刻度
中间区域,显示条图和网格线
控件最终形态

  • 前端代码
<Grid Background="{Binding Path=Background,ElementName=root}">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="30" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="30" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />

            </Grid.ColumnDefinitions>
            <!--网格面板-->
            <Canvas x:Name="GrdLine"
                    Grid.Row="0"
                    Grid.Column="2" SizeChanged="GrdLine_OnSizeChanged"></Canvas>
            <!--纵轴刻度面板-->
            <Canvas  x:Name="GrdContext"
                     Grid.Row="0"
                     Grid.Column="1"
                     HorizontalAlignment="Right"
                     VerticalAlignment="Stretch"
                     MinWidth="{Binding Path=MinAxisYWidth,ElementName=root}"
                     Margin="0,0,0,0"
                     SizeChanged="GrdContext_OnSizeChanged"
                     >
            </Canvas>
            <!--横轴刻度面板-->
            <Canvas  x:Name="GrdScale"
                     Grid.Row="1"
                     Grid.Column="2"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Top"
                     MinHeight="{Binding Path=MinAxisXHeight,ElementName=root}"
                     SizeChanged="GrdScale_OnSizeChanged">
            </Canvas>
            <!--柱子面板-->
            <Canvas  x:Name="GrdBar"
                     Grid.Row="0"
                     Grid.Column="2"
                     HorizontalAlignment="Stretch"
                     VerticalAlignment="Stretch"
                     SizeChanged="GrdBar_OnSizeChanged">
            </Canvas>

            <!--纵轴单位面板-->
            <Canvas  x:Name="GrdYUnit"
                     Grid.Row="0"
                     Grid.Column="0"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
                     Margin="0,0,20,0"
                     RenderTransformOrigin="0.5,0.5">
                <Canvas.RenderTransform>
                    <RotateTransform Angle="-90" />
                </Canvas.RenderTransform>
                <TextBlock Text="{Binding Path=YUnit,ElementName=root}"
                           FontFamily="{Binding Path=AxisFontFamily,ElementName=root}"
                           FontSize="{Binding Path=AxisFontSize,ElementName=root}"
                           Foreground="{Binding Path=AxisFontground,ElementName=root}"></TextBlock>
            </Canvas>

            <!--横轴单位面板-->
            <Canvas  x:Name="GrdXUnit"
                     Grid.Row="2"
                     Grid.Column="2"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
                     RenderTransformOrigin="0.5,0.5">
                <Canvas.RenderTransform>
                    <RotateTransform Angle="0" />
                </Canvas.RenderTransform>
                <TextBlock Text="{Binding Path=XUnit,ElementName=root}"
                           FontFamily="{Binding Path=AxisFontFamily,ElementName=root}"
                           FontSize="{Binding Path=AxisFontSize,ElementName=root}"
                           Foreground="{Binding Path=AxisFontground,ElementName=root}"></TextBlock>
            </Canvas>
        </Grid>
  • 纵轴
    • 根据你纵轴面板的高度自动计算每个刻度的位置
        /// <summary>
        /// 纵轴
        /// </summary>
        /// <param name="sources">数据源</param>
        private void DrawContextAxis(IEnumerable sources)
        {
                ObservableCollection<LogarithmicModel> itemsSource = sources as ObservableCollection<LogarithmicModel>;
                if (itemsSource == null)
                    return;
                int count = itemsSource.Count;
                GrdContext.Children.Clear();

                Border bd = new Border();
                bd.HorizontalAlignment = HorizontalAlignment.Right;
                bd.VerticalAlignment = VerticalAlignment.Stretch;
                bd.Width = LineThickness;
                bd.Background = LineBrush;
                GrdContext.Children.Add(bd);

                everyheight = GrdContext.ActualHeight / count;
                for (int i = 0; i < itemsSource.Count; i++)
                {
                    TextBlock tx = new TextBlock();
                    tx.FontFamily = AxisFontFamily;
                    tx.FontSize = AxisFontSize;
                    tx.Foreground = AxisFontground;
                    tx.Text = itemsSource[i].Name;
                    tx.HorizontalAlignment = HorizontalAlignment.Right;
                    tx.VerticalAlignment = VerticalAlignment.Bottom;

                    double right = 3;
                    double bottom = everyheight * i;
                    Canvas.SetRight(tx, right);
                    Canvas.SetBottom(tx, bottom);
                    tx.Loaded += ContextLoaded;
                    GrdContext.Children.Add(tx);
                }
        }
  • 横轴
    • 根据你横轴面板宽度自动计算么个刻度的位置和显示的值
 /// <summary>
        /// 画出值刻度轴(横坐标)
        /// </summary>
        /// <param name="baseValue">底数</param>
        /// <param name="scaleCount">刻度个数</param>
        private void DrawValueScale(double baseValue, int scaleCount)
        {
                double grdScaleWidth = GrdScale.ActualWidth;
                GrdScale.Children.Clear();
                Border bd = new Border();
                bd.HorizontalAlignment = HorizontalAlignment.Stretch;
                bd.VerticalAlignment = VerticalAlignment.Top;
                bd.Height = LineThickness;
                bd.Background = LineBrush;
                bd.Width = GrdScale.ActualWidth;
                GrdScale.Children.Add(bd);
                everyWidth = grdScaleWidth / (scaleCount - 1);
                for (int i = 0; i < scaleCount; i++)
                {
                    TextBlock tx = new TextBlock();
                    tx.FontFamily = AxisFontFamily;
                    tx.FontSize = AxisFontSize;
                    tx.Foreground = AxisFontground;
                    tx.Text = Math.Pow(baseValue, i).ToString(CultureInfo.InvariantCulture);
                    tx.HorizontalAlignment = HorizontalAlignment.Left;
                    tx.VerticalAlignment = VerticalAlignment.Top;
                    double left = everyWidth * i;
                    if (double.IsNaN(left))
                    {
                        left = 0;
                    }
                    double top = 3;

                    Canvas.SetLeft(tx, left);
                    Canvas.SetTop(tx, top);
                    tx.Loaded += Tx_Loaded;
                    GrdScale.Children.Add(tx);
                }
        }
  • 绘制柱子
    • a) 理解对数函数,计算每根柱子的长度
/// <summary>
        /// 绘制柱子
        /// </summary>
        /// <param name="sources">数据源</param>
        private void DrawValueBar(IEnumerable sources)
        {
                ObservableCollection<LogarithmicModel> itemsSource = sources as ObservableCollection<LogarithmicModel>;
                if (itemsSource == null)
                    return;
                int count = itemsSource.Count;

                GrdBar.Children.Clear();
                for (int i = 0; i < count; i++)
                {
                    double value = itemsSource[i].Value;
                    double barCount = Math.Log(value, BaseValue);
                    double barWidth = barCount * everyWidth;
                    if (Math.Abs(value) <= 0)
                    {
                        barWidth = 0;
                    }

                    Border bd = new Border();
                    bd.Height = GetBarHeight(everyheight);
                    bd.Width = barWidth;


                    if (barBrushes.Count <= i || barBrushes[i] != null)
                    {
                        Brush b = GenerateRandomBrush();
                        barBrushes.Add(b);
                    }

                    bd.Background = barBrushes[i];
                    bd.HorizontalAlignment = HorizontalAlignment.Left;
                    bd.VerticalAlignment = VerticalAlignment.Bottom;

                    //ToolTip
                    StackPanel stp = new StackPanel();
                    TextBlock tooltip = new TextBlock();
                    tooltip.Text = itemsSource[i].Name.ToString();
                    TextBlock tooltip1 = new TextBlock();
                    tooltip1.Text = value.ToString();
                    stp.Children.Add(tooltip);
                    stp.Children.Add(tooltip1);
                    bd.ToolTip = stp;

                    double left = 0;
                    double bottom = everyheight * i;
                    Canvas.SetLeft(bd, left);
                    Canvas.SetBottom(bd, bottom);
                    bd.Loaded += Bd_Loaded;
                    GrdBar.Children.Add(bd);
                }
        }

其他的譬如数据更新处理,柱子颜色加载、文字样式,网格线等,就不贴出来了。这个demo大家做个参考,希望对需要的人有用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值