因為最近的一個項目里需要用到柱狀圖,找了一些第三方的控件,UI部分定制化不強,很難保證與現有界面的統一。項目當中用到的圖形也比較簡單,所以干脆自己動手實現。
下面是最終的效果。
首先講一下設計思路
圖形控件主要分為四部分:一水平文本,垂直文本,背景線條,以及最主要的柱形部分。
第一步先繪制背景,因為水平文本和垂直文本都是根據數據源動態計算的,所以內容無法固定。為了方便計算這里以Grid控件為基類,在Grid的基礎上進行繪制,因為通過添加Grid的行和列能夠很好的控制控件內容的布局。
1 class WpfChart : Grid 2 { 3 4 5 ....... 6 7 8 }
接下來要定義控件的數據源。柱狀圖的數據源比較簡單,可以通過簡單的鍵值對進行設計。為了方便綁定,這里將數據源設計為依賴屬性
1 public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable<KeyValuePair<string, double>>), typeof(WpfChart), new PropertyMetadata(new List<KeyValuePair<string,double>>(), OnItemsSourcePropertyChanged)); 2 3 public IEnumerable<KeyValuePair<string,double>> ItemsSource 4 { 5 get { return (List<KeyValuePair<string, double>>)GetValue(ItemsSourceProperty); } 6 set { SetValue(ItemsSourceProperty, value); } 7 }
為了方便布局,這里將整個控件划分成三個區域,分別用三個Grid控件進行管理這三塊區域的內容。
1 private readonly Grid _gridTextH = new Grid(); 2 private readonly Grid _gridTextV = new Grid(); 3 private readonly Grid _gridContent = new Grid();
下面進行垂直、水平內容的添加 ,垂直文本通通放到_gridTextH控件內,水平文本通通放到_gridTextV控件內。
1、通過獲取數據源中的最大值,計算控件中需要顯示的行數。
2、添加對應的表格線到_gridTextH中。
3、通過獲取數據源的長度,添加控件對應的水平文本。
1 void DrawHTextCollection() 2 { 3 _gridTextH.ColumnDefinitions.Clear(); 4 _gridTextH.RowDefinitions.Clear(); 5 _gridTextH.Children.Clear(); 6 _gridTextH.Margin = new Thickness(_viewPortPad.Left, 0, 0, 0); 7 _gridTextH.RowDefinitions.Add(new RowDefinition() {Height = new GridLength(1, GridUnitType.Star)}); 8 _gridTextH.RowDefinitions.Add(new RowDefinition() 9 { 10 Height = new GridLength(_viewPortPad.Bottom, GridUnitType.Pixel) 11 }); 12 13 TextBlock tb; 14 15 foreach (var v in ItemsSource) 16 { 17 _gridTextH.Children.Add( 18 tb = 19 new TextBlock() 20 { 21 Text = v.Key, 22 HorizontalAlignment = HorizontalAlignment.Center, 23 VerticalAlignment = VerticalAlignment.Top, 24 Margin = new Thickness(0, 10, 0, 0) 25 26 }); 27 _gridTextH.ColumnDefinitions.Add(new ColumnDefinition()); 28 Grid.SetRow(tb, 1); 29 Grid.SetColumn(tb, _gridTextH.ColumnDefinitions.Count - 1); 30 } 31 } 32 33 34 35 36 void DrawVTextCollection() 37 { 38 TextBlock tb; 39 Border border; 40 _gridTextV.RowDefinitions.Clear(); 41 _gridTextV.Children.Clear(); 42 _gridTextV.ColumnDefinitions.Clear(); 43 _gridTextV.Margin = new Thickness(0, _viewPortPad.Top, 0, _viewPortPad.Bottom); 44 _gridTextV.ColumnDefinitions.Add(new ColumnDefinition() 45 { 46 Width = new GridLength(_viewPortPad.Left, GridUnitType.Pixel) 47 }); 48 _gridTextV.ColumnDefinitions.Add(new ColumnDefinition() 49 { 50 Width = new GridLength(1, GridUnitType.Star) 51 }); 52 foreach (var v in GetVTextCollection()) 53 { 54 _gridTextV.Children.Add( 55 tb = 56 new TextBlock() 57 { 58 Text = v.ToString(), 59 Margin = new Thickness(0, 0, 5, 0), 60 HorizontalAlignment = HorizontalAlignment.Right, 61 VerticalAlignment = VerticalAlignment.Bottom 62 }); 63 _gridTextV.Children.Add( 64 border = 65 new Border() 66 { 67 BorderBrush = new SolidColorBrush((Color) ColorConverter.ConvertFromString("#FFc0c0c0")), 68 BorderThickness = new Thickness(0, 0, 0, 1), 69 VerticalAlignment = VerticalAlignment.Bottom 70 }); 71 _gridTextV.RowDefinitions.Add(new RowDefinition()); 72 Grid.SetRow(tb, _gridTextV.RowDefinitions.Count - 1); 73 Grid.SetColumn(border, 1); 74 Grid.SetRow(border, _gridTextV.RowDefinitions.Count - 1); 75 } 76 }
下面是背景生成后的效果圖。
最后就是最重要的內容區域了。通通放到_gridContent中。
1 void DrawCoordinate() 2 { 3 Rectangle rectangle; 4 TextBlock tb; 5 _gridContent.ColumnDefinitions.Clear(); 6 _gridContent.RowDefinitions.Clear(); 7 _gridContent.Children.Clear(); 8 9 //_gridContent.Margin = new Thickness(_viewPortPad.Left, 0, 0, _viewPortPad.Bottom); 10 _gridContent.ColumnDefinitions.Add(new ColumnDefinition() 11 { 12 Width = new GridLength(_viewPortPad.Left, GridUnitType.Pixel) 13 }); 14 _gridContent.RowDefinitions.Add(new RowDefinition() 15 { 16 Height = new GridLength(1, GridUnitType.Star) 17 }); 18 _gridContent.RowDefinitions.Add(new RowDefinition() 19 { 20 Height = new GridLength(_viewPortPad.Bottom, GridUnitType.Pixel) 21 }); 22 23 var maxHeight = _gridContent.ActualHeight - _viewPortPad.Top - _viewPortPad.Bottom; 24 var maxWidth = _gridContent.ActualWidth - _viewPortPad.Left; 25 26 var list = ItemsSource; 27 foreach (var v in list) 28 { 29 var xx = maxHeight*(v.Value/_chartMaxNumber); 30 _gridContent.Children.Add( 31 rectangle = 32 new Rectangle() 33 { 34 Fill = _defaultBrush, 35 Height = maxHeight*(v.Value/_chartMaxNumber), 36 Width = maxWidth/list.Count()*0.6, 37 VerticalAlignment = VerticalAlignment.Bottom 38 }); 39 var doubleAnimation = new DoubleAnimation(0, maxHeight*(v.Value/_chartMaxNumber), 40 new Duration(new TimeSpan(0, 0, 0, 0, 1000))); 41 42 rectangle.BeginAnimation(Rectangle.HeightProperty, doubleAnimation); 43 44 rectangle.Tag = v.Value; 45 46 if (v.Value == this._maxValue) 47 rectangle.Fill = _maxValueBrush; 48 _gridContent.Children.Add( 49 tb = 50 new TextBlock() 51 { 52 Text = v.Value.ToString("f"), 53 Margin = new Thickness(0, 0, 0, maxHeight*(v.Value/_chartMaxNumber) + 5), 54 VerticalAlignment = VerticalAlignment.Bottom, 55 HorizontalAlignment = HorizontalAlignment.Center, 56 57 } 58 ); 59 60 61 var thicknessAnimation = new ThicknessAnimation(new Thickness(0, 0, 0, 0), 62 new Thickness(0, 0, 0, maxHeight*(v.Value/_chartMaxNumber) + 5), 63 new Duration(new TimeSpan(0, 0, 0, 0, 1000))); 64 tb.BeginAnimation(TextBlock.MarginProperty, thicknessAnimation); 65 66 rectangle.MouseEnter += Rectangle_MouseEnter; 67 rectangle.MouseLeave += Rectangle_MouseLeave; 68 _gridContent.ColumnDefinitions.Add(new ColumnDefinition()); 69 Grid.SetColumn(rectangle, _gridContent.ColumnDefinitions.Count - 1); 70 Grid.SetColumn(tb, _gridContent.ColumnDefinitions.Count - 1); 71 } 72 }
生成的控件功能雖然簡單,但定制化很強,任何地方都能修改,擺脫了使用第三方控件的束縛。
代碼位置:http://download.csdn.net/detail/shushukui/9512216