mikeo_410


 FrameworkElementFactory(コードでTemplateを作る)

  Style に TemplateProperty として設定できるのは、ControlTemplateクラスのインスタンスです。ControlTemplate には、VisualTree プロパティがあり、FrameworkElementFactory型のオブジェクトを設定できます。

  この方法はWPFのもので、Silverlightには類似がありません。
  折れ線グラフのマーク描画の際に検討したものですが使用せず、XAMLのテンプレートを文字列で用意して置く方法にしました。「1つのStyle記述から複数のインスタンス

  折れ線グラフでは、線を区別するのに線上のマークの形状を変えることがあります。
  このマークは、一連のデータ列では同じマークが使われ、別の線では異なるマークが使われます。マークを変える場合は、一連のデータ列のマークが一度に変わることになります。

  この動作を、コントロールのStyle プロパティの設定で行うことを考えます。
  一連のデータ列には、同じインスタンスを設定することになります。
  このスタイルで表すことの必要な形状は、あまり多くはありませんが、線幅に応じてサイズが変わることと、色のバリエーションがあり、あらかじめXAMLで書いておくことは現実的ではありません。

  この解決策はいろいろ考えられますが、その一つの方法です。これが良い方法だと言うことではありません。

  1. public class DataPoints : FrameworkElement, INotifyPropertyChanged
  2. {
  3.     public PointCollection Points { get; set; }
  4.     public double LineWidh { get; set; }
  5.     Style _MarkStyle;
  6.     public Style MarkStyle
  7.     {
  8.         get { return _MarkStyle; }
  9.         set
  10.         {
  11.             _MarkStyle = value;
  12.             if (PropertyChanged != null)
  13.                 PropertyChanged.Invoke(this, new PropertyChangedEventArgs("MarkStyle"));
  14.         }
  15.     }
  16.     public event PropertyChangedEventHandler PropertyChanged;
  17.     public DataPoints()
  18.     {
  19.         Loaded += new RoutedEventHandler(DataPoints_Loaded);
  20.     }
  21.     void SetMarkStyle()
  22.     {
  23.         FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Ellipse));
  24.         factory.SetValue(Ellipse.WidthProperty, LineWidh * 10);
  25.         factory.SetValue(Ellipse.HeightProperty, LineWidh * 10);
  26.         factory.SetValue(Ellipse.StrokeProperty, new SolidColorBrush(Colors.Magenta));
  27.         factory.SetValue(Ellipse.StrokeThicknessProperty, 1.0);
  28.         factory.SetValue(Ellipse.FillProperty, new SolidColorBrush(Colors.Magenta));
  29.         ControlTemplate ct = new ControlTemplate(typeof(Mark));
  30.         ct.VisualTree = factory;
  31.         Style style = new Style(typeof(Mark));
  32.         style.Setters.Add(new Setter(Mark.TemplateProperty, ct));
  33.         MarkStyle = style;
  34.     }
  35.     void DataPoints_Loaded(object sender, RoutedEventArgs e)
  36.     {
  37.         SetMarkStyle();
  38.         foreach (Point p in Points)
  39.         {
  40.             Mark mark = new Mark();
  41.             BindingBase bind_markstyle = new Binding("MarkStyle")
  42.             {
  43.                 Source=this
  44.             };
  45.             mark.SetBinding(StyleProperty, bind_markstyle);
  46.             ((Canvas)Parent).Children.Add(mark);
  47.             Canvas.SetLeft(mark, p.X);
  48.             Canvas.SetTop(mark, p.Y);
  49.         }
  50.     }
  51. }
  52. public class Mark : Control
  53. {
  54.     public Mark()
  55.     {
  56.     }
  57. }

  DataPointsクラスは、一連のデータ列(1つの折れ線)を表します。
  Markクラスは、折れ線上に表すマークです。

  Markクラスは、それ自体では形状も色も定義していません。Styleプロパティに設定された内容で決まります。
  DataPointsクラスは、Loadedイベントで、設定されているデータポイントの数だけ、そのデータの位置にMarkクラスのインスタンスを配置します。

  初期値として、MarkのStyleには、Magentaの丸をセットしています。
  丸のサイズと位置は、XAMLの記述によって決まります。

  このクラスを使う MainWindow では、以下のXAMLを記述しています。

  1. <Grid Loaded="Grid_Loaded" >
  2.     <StackPanel>
  3.         <Button Content="Button" Click="Button_Click"/>
  4.         <Canvas xmlns:u="clr-namespace:WpfApplication1">
  5.             <u:DataPoints x:Name="dp1" LineWidh="3" Points="30,10 130,10 230,10"/>
  6.             <u:DataPoints x:Name="dp2" LineWidh="5" Points="30,100 130,100 230,100"/>
  7.         </Canvas>
  8.     </StackPanel>
  9. </Grid>

  これによって、最初の図が描かれます。
  ボタンのクリックでは、新たなスタイルを設定して、一括で下段のマークを切り替えます。

  1. Style Cross1(double lwd, Color color)
  2. {
  3.     // 線を2つ
  4.     double h = lwd * 10;
  5.     LineGeometry lg1 = new LineGeometry(new Point(0, 0), new Point(h, h));
  6.     LineGeometry lg2 = new LineGeometry(new Point(0, h), new Point(h, 0));
  7.     GeometryGroup gg = new GeometryGroup();
  8.     gg.Children.Add(lg1);
  9.     gg.Children.Add(lg2);
  10.     // Pathの生成
  11.     FrameworkElementFactory path = new FrameworkElementFactory(typeof(Path));
  12.     path.SetValue(Path.DataProperty, gg);
  13.     path.SetValue(Path.StrokeProperty, new SolidColorBrush(color));
  14.     path.SetValue(Path.StrokeThicknessProperty, 1.0);
  15.     // Borderの生成
  16.     FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
  17.     double w = lwd * 10 + 3.0;
  18.     border.SetValue(Border.WidthProperty, w);
  19.     border.SetValue(Border.HeightProperty, w);
  20.     border.SetValue(Border.BorderBrushProperty, 
  21.         new SolidColorBrush(Colors.Transparent));
  22.     border.SetValue(Border.BorderThicknessProperty, new Thickness(1));
  23.     border.AppendChild(path); // Pathを加える
  24.     // コントロールテンプレートにborderを追加
  25.     ControlTemplate ct = new ControlTemplate(typeof(Mark));
  26.     ct.VisualTree = border;
  27.     // StyleでTemplateを設定
  28.     Style style = new Style(typeof(Mark));
  29.     style.Setters.Add(new Setter(Mark.TemplateProperty, ct));
  30.     return style;
  31. }
  32. private void Button_Click(object sender, RoutedEventArgs e)
  33. {
  34.     dp2.MarkStyle = Cross1(5, Colors.Maroon);
  35. }


 


mikeo_410@hotmail.com