■ UIElement 엘리먼트의 Effect 속성을 사용해 히트맵을 그리는 방법을 보여준다.
▶ AttachedTitle.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
using System.Windows; namespace TestProject { /// <summary> /// 첨부 제목 /// </summary> public static class AttachedTitle { //////////////////////////////////////////////////////////////////////////////////////////////////// Dependency Property ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 제목 첨부 속성 - TitleProperty /// <summary> /// 제목 첨부 속성 /// </summary> public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached ( "Title", typeof(string), typeof(AttachedTitle), new PropertyMetadata(string.Empty) ); #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Static //////////////////////////////////////////////////////////////////////////////// Public #region 제목 설정하기 - SetTitle(d, value) /// <summary> /// 제목 설정하기 /// </summary> /// <param name="d">의존 객체</param> /// <param name="value">값</param> public static void SetTitle(DependencyObject d, string value) { d.SetValue(TitleProperty, value); } #endregion #region 제목 구하기 - GetTitle(d) /// <summary> /// 제목 구하기 /// </summary> /// <param name="d">의존 객체</param> /// <returns>제목</returns> public static string GetTitle(DependencyObject d) { return (string)d.GetValue(TitleProperty); } #endregion } } |
▶ HeatPoint.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
namespace TestProject { /// <summary> /// 히트 포인트 /// </summary> public struct HeatPoint { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Public #region Field /// <summary> /// X /// </summary> public int X; /// <summary> /// Y /// </summary> public int Y; /// <summary> /// 강도 /// </summary> public byte Intensity; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - HeatPoint(x, y, intensity) /// <summary> /// 생성자 /// </summary> /// <param name="x">X</param> /// <param name="y">Y</param> /// <param name="intensity">강도</param> public HeatPoint(int x, int y, byte intensity) { X = x; Y = y; Intensity = intensity; } #endregion } } |
▶ HeatMap.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Media; namespace TestProject { /// <summary> /// 히트맵 /// </summary> public class HeatMap : FrameworkElement { //////////////////////////////////////////////////////////////////////////////////////////////////// Field ////////////////////////////////////////////////////////////////////////////////////////// Private #region Field /// <summary> /// 색상 그라디언트 수 /// </summary> private const int ColorGradientNumber = 256; /// <summary> /// 히트맵 비주얼 컬렉션 /// </summary> private VisualCollection heatMapVisualCollection; /// <summary> /// 히트 포인트 리스트 /// </summary> private List<HeatPoint> heatPointList = new List<HeatPoint>(); /// <summary> /// 라디얼 그라디언트 브러시 배열 /// </summary> private readonly RadialGradientBrush[] radialGradientBrushArray; #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Property ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 비주얼 자식 수 - VisualChildrenCount /// <summary> /// 비주얼 자식 수 /// </summary> protected override int VisualChildrenCount { get { return this.heatMapVisualCollection.Count; } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - HeatMap() /// <summary> /// 생성자 /// </summary> public HeatMap() { this.heatMapVisualCollection = new VisualCollection(this); this.radialGradientBrushArray = new RadialGradientBrush[ColorGradientNumber]; for(int i = 0; i < this.radialGradientBrushArray.Length; i++) { this.radialGradientBrushArray[i] = new RadialGradientBrush ( Color.FromArgb((byte)i, 255, 255, 255), Color.FromArgb(0, 255, 255, 255) ); } } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Public #region 히트 포인트 추가하기 - AddHeatPoint(heatPoint) /// <summary> /// 히트 포인트 추가하기 /// </summary> /// <param name="heatPoint">히트 포인트</param> public void AddHeatPoint(HeatPoint heatPoint) { this.heatPointList.Add(heatPoint); } #endregion #region 지우기 - Clear() /// <summary> /// 지우기 /// </summary> public void Clear() { this.heatPointList.Clear(); } #endregion #region 렌더링하기 - Render() /// <summary> /// 렌더링하기 /// </summary> public void Render() { double size; this.heatMapVisualCollection.Clear(); DrawingVisual drawingVisual = new DrawingVisual(); using(DrawingContext drawingContext = drawingVisual.RenderOpen()) { foreach(HeatPoint point in this.heatPointList) { size = point.Intensity / 5; drawingContext.DrawRectangle ( this.radialGradientBrushArray[point.Intensity], null, new Rect(point.X - size / 2, point.Y - size / 2, size, size) ); } this.heatMapVisualCollection.Add(drawingVisual); } } #endregion ////////////////////////////////////////////////////////////////////////////////////////// Protected #region 비주얼 자식 구하기 - GetVisualChild(index) /// <summary> /// 비주얼 자식 구하기 /// </summary> /// <param name="index">인덱스</param> /// <returns>비주얼 자식</returns> protected override Visual GetVisualChild(int index) { if(index < 0 || index >= this.heatMapVisualCollection.Count) { throw new ArgumentOutOfRangeException("index"); } return this.heatMapVisualCollection[index]; } #endregion } } |
▶ MainWindow.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
<Window x:Class="TestProject.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" xmlns:local="clr-namespace:TestProject" xmlns:effects="clr-namespace:TestProject.Effects" Title="TestProject" Width="800" Height="600" FontFamily="나눔고딕코딩" FontSize="16"> <Window.Resources> <x:Array x:Key="LinearGradientBrushArrayKey" Type="LinearGradientBrush"> <!-- Black Body Radiation --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Black Body Radiation"> <GradientStop Offset="0.00000" Color="#000000" /> <GradientStop Offset="0.11111" Color="#f82600" /> <GradientStop Offset="0.22222" Color="#fc7700" /> <GradientStop Offset="0.33333" Color="#ffab1f" /> <GradientStop Offset="0.44444" Color="#ffc360" /> <GradientStop Offset="0.55555" Color="#ffdda4" /> <GradientStop Offset="0.66666" Color="#fff4e4" /> <GradientStop Offset="0.77777" Color="#e8edff" /> <GradientStop Offset="0.88888" Color="#dbe3ff" /> <GradientStop Offset="0.99999" Color="#cbd7ff" /> </LinearGradientBrush> <!-- Aqua --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Aqua"> <GradientStop Offset="0" Color="Black" /> <GradientStop Offset="0.5" Color="Aqua" /> <GradientStop Offset="1" Color="White" /> </LinearGradientBrush> <!-- Blue & Red --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Blue and Red"> <GradientStop Offset="0" Color="Blue" /> <GradientStop Offset="1" Color="Red" /> </LinearGradientBrush> <!-- Deep sea --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Deep sea"> <GradientStop Offset="0" Color="#000000" /> <GradientStop Offset="0.6" Color="#183567" /> <GradientStop Offset="0.75" Color="#2e649e" /> <GradientStop Offset="0.9" Color="#17adcb" /> <GradientStop Offset="1.0" Color="#00fafa" /> </LinearGradientBrush> <!-- Color Spectrum --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Color Spectrum"> <GradientStop Offset="0" Color="Navy" /> <GradientStop Offset="0.25" Color="Blue" /> <GradientStop Offset="0.5" Color="Green" /> <GradientStop Offset="0.75" Color="Yellow" /> <GradientStop Offset="1.0" Color="Red" /> </LinearGradientBrush> <!-- Incadescent --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Incadescent"> <GradientStop Offset="0" Color="Black" /> <GradientStop Offset="0.33" Color="DarkRed" /> <GradientStop Offset="0.66" Color="Yellow" /> <GradientStop Offset="1.0" Color="White" /> </LinearGradientBrush> <!-- Heated Metal --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Heated Metal"> <GradientStop Offset="0" Color="Black" /> <GradientStop Offset="0.4" Color="Purple" /> <GradientStop Offset="0.6" Color="Red" /> <GradientStop Offset="0.8" Color="Yellow" /> <GradientStop Offset="1.0" Color="White" /> </LinearGradientBrush> <!-- Sunrise --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Sunrise"> <GradientStop Offset="0" Color="Red" /> <GradientStop Offset="0.66" Color="Yellow" /> <GradientStop Offset="1.0" Color="White" /> </LinearGradientBrush> <!-- Stepped Colors --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Stepped Colors"> <GradientStop Color="Navy" Offset="0" /> <GradientStop Color="Navy" Offset="0.25" /> <GradientStop Color="Green" Offset="0.26" /> <GradientStop Color="Green" Offset="0.5" /> <GradientStop Color="Yellow" Offset="0.51" /> <GradientStop Color="Yellow" Offset="0.75" /> <GradientStop Color="Red" Offset="0.76" /> <GradientStop Color="Red" Offset="1.0" /> </LinearGradientBrush> <!-- Visible Spectrum --> <LinearGradientBrush PresentationOptions:Freeze="True" StartPoint="0 0" EndPoint="1 0" local:AttachedTitle.Title="Visible Spectrum"> <GradientStop Color="#ff00ff" Offset="0" /> <GradientStop Color="#0000ff" Offset="0.25" /> <GradientStop Color="#00ff00" Offset="0.5" /> <GradientStop Color="#ffff00" Offset="0.75" /> <GradientStop Color="#ff0000" Offset="1.0" /> </LinearGradientBrush> </x:Array> <VisualBrush x:Key="PaletteVisualBrushKey"> <VisualBrush.Visual> <Rectangle Width="256" Height="1" Fill="{Binding SelectedItem, ElementName=colorComboBox}" /> </VisualBrush.Visual> </VisualBrush> </Window.Resources> <Grid Name="rootGrid" Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Grid.Row="0" Orientation="Horizontal"> <TextBlock VerticalAlignment="Center" Text="Color schema" /> <ComboBox Name="colorComboBox" Margin="10 0 0 0" Width="320" Height="25" ItemsSource="{StaticResource LinearGradientBrushArrayKey}"> <ComboBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <Rectangle Width="120" Height="10" Fill="{Binding}" /> <TextBlock Margin="10 0 0 0" Text="{Binding (local:AttachedTitle.Title)}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> </StackPanel> <local:HeatMap x:Name="heatMap" Grid.Row="1" Margin="0 10 0 0" ClipToBounds="True"> <local:HeatMap.Effect> <effects:HeatColorizer Palette="{StaticResource PaletteVisualBrushKey}" /> </local:HeatMap.Effect> </local:HeatMap> </Grid> </Window> |
▶ MainWindow.xaml.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
using System; using System.Windows; namespace TestProject { /// <summary> /// 메인 윈도우 /// </summary> public partial class MainWindow : Window { //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor ////////////////////////////////////////////////////////////////////////////////////////// Public #region 생성자 - MainWindow() /// <summary> /// 생성자 /// </summary> public MainWindow() { InitializeComponent(); this.colorComboBox.SelectedIndex = 0; Loaded += Window_Loaded; this.rootGrid.SizeChanged += rootGrid_SizeChanged; } #endregion //////////////////////////////////////////////////////////////////////////////////////////////////// Method ////////////////////////////////////////////////////////////////////////////////////////// Private //////////////////////////////////////////////////////////////////////////////// Event #region 윈도우 로드시 처리하기 - Window_Loaded(sender, e) /// <summary> /// 윈도우 로드시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void Window_Loaded(object sender, RoutedEventArgs e) { RenderContent(); } #endregion #region 루트 그리드 크기 변경시 처리하기 - rootGrid_SizeChanged(sender, e) /// <summary> /// 루트 그리드 크기 변경시 처리하기 /// </summary> /// <param name="sender">이벤트 발생자</param> /// <param name="e">이벤트 인자</param> private void rootGrid_SizeChanged(object sender, SizeChangedEventArgs e) { RenderContent(); } #endregion //////////////////////////////////////////////////////////////////////////////// Function #region 컨텐트 렌더링하기 - RenderContent() /// <summary> /// 컨텐트 렌더링하기 /// </summary> private void RenderContent() { this.heatMap.Clear(); Random random = new Random(); int x; int y; byte intense; for(int i = 0; i < 1000; i++) { x = random.Next(0, (int)(this.heatMap.ActualWidth - 1)); y = random.Next(0, (int)(this.heatMap.ActualHeight - 1)); intense = (byte)random.Next(0, 255); this.heatMap.AddHeatPoint(new HeatPoint(x, y, intense)); } this.heatMap.Render(); } #endregion } } |