FrameworkElementFactory(コードでTemplateを作る)
Style に TemplateProperty として設定できるのは、ControlTemplateクラスのインスタンスです。ControlTemplate には、VisualTree プロパティがあり、FrameworkElementFactory型のオブジェクトを設定できます。
この方法はWPFのもので、Silverlightには類似がありません。
折れ線グラフのマーク描画の際に検討したものですが使用せず、XAMLのテンプレートを文字列で用意して置く方法にしました。「1つのStyle記述から複数のインスタンス」
折れ線グラフでは、線を区別するのに線上のマークの形状を変えることがあります。
このマークは、一連のデータ列では同じマークが使われ、別の線では異なるマークが使われます。マークを変える場合は、一連のデータ列のマークが一度に変わることになります。
この動作を、コントロールのStyle プロパティの設定で行うことを考えます。
一連のデータ列には、同じインスタンスを設定することになります。
このスタイルで表すことの必要な形状は、あまり多くはありませんが、線幅に応じてサイズが変わることと、色のバリエーションがあり、あらかじめXAMLで書いておくことは現実的ではありません。
この解決策はいろいろ考えられますが、その一つの方法です。これが良い方法だと言うことではありません。
- public class DataPoints : FrameworkElement, INotifyPropertyChanged
- {
- public PointCollection Points { get; set; }
- public double LineWidh { get; set; }
- Style _MarkStyle;
- public Style MarkStyle
- {
- get { return _MarkStyle; }
- set
- {
- _MarkStyle = value;
- if (PropertyChanged != null)
- PropertyChanged.Invoke(this, new PropertyChangedEventArgs("MarkStyle"));
- }
- }
- public event PropertyChangedEventHandler PropertyChanged;
- public DataPoints()
- {
- Loaded += new RoutedEventHandler(DataPoints_Loaded);
- }
- void SetMarkStyle()
- {
- FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Ellipse));
- factory.SetValue(Ellipse.WidthProperty, LineWidh * 10);
- factory.SetValue(Ellipse.HeightProperty, LineWidh * 10);
- factory.SetValue(Ellipse.StrokeProperty, new SolidColorBrush(Colors.Magenta));
- factory.SetValue(Ellipse.StrokeThicknessProperty, 1.0);
- factory.SetValue(Ellipse.FillProperty, new SolidColorBrush(Colors.Magenta));
- ControlTemplate ct = new ControlTemplate(typeof(Mark));
- ct.VisualTree = factory;
- Style style = new Style(typeof(Mark));
- style.Setters.Add(new Setter(Mark.TemplateProperty, ct));
- MarkStyle = style;
- }
- void DataPoints_Loaded(object sender, RoutedEventArgs e)
- {
- SetMarkStyle();
- foreach (Point p in Points)
- {
- Mark mark = new Mark();
- BindingBase bind_markstyle = new Binding("MarkStyle")
- {
- Source=this
- };
- mark.SetBinding(StyleProperty, bind_markstyle);
- ((Canvas)Parent).Children.Add(mark);
- Canvas.SetLeft(mark, p.X);
- Canvas.SetTop(mark, p.Y);
- }
- }
- }
- public class Mark : Control
- {
- public Mark()
- {
- }
- }
DataPointsクラスは、一連のデータ列(1つの折れ線)を表します。
Markクラスは、折れ線上に表すマークです。
Markクラスは、それ自体では形状も色も定義していません。Styleプロパティに設定された内容で決まります。
DataPointsクラスは、Loadedイベントで、設定されているデータポイントの数だけ、そのデータの位置にMarkクラスのインスタンスを配置します。
初期値として、MarkのStyleには、Magentaの丸をセットしています。
丸のサイズと位置は、XAMLの記述によって決まります。
このクラスを使う MainWindow では、以下のXAMLを記述しています。
- <Grid Loaded="Grid_Loaded" >
- <StackPanel>
- <Button Content="Button" Click="Button_Click"/>
- <Canvas xmlns:u="clr-namespace:WpfApplication1">
- <u:DataPoints x:Name="dp1" LineWidh="3" Points="30,10 130,10 230,10"/>
- <u:DataPoints x:Name="dp2" LineWidh="5" Points="30,100 130,100 230,100"/>
- </Canvas>
- </StackPanel>
- </Grid>
これによって、最初の図が描かれます。
ボタンのクリックでは、新たなスタイルを設定して、一括で下段のマークを切り替えます。
- Style Cross1(double lwd, Color color)
- {
- // 線を2つ
- double h = lwd * 10;
- LineGeometry lg1 = new LineGeometry(new Point(0, 0), new Point(h, h));
- LineGeometry lg2 = new LineGeometry(new Point(0, h), new Point(h, 0));
- GeometryGroup gg = new GeometryGroup();
- gg.Children.Add(lg1);
- gg.Children.Add(lg2);
- // Pathの生成
- FrameworkElementFactory path = new FrameworkElementFactory(typeof(Path));
- path.SetValue(Path.DataProperty, gg);
- path.SetValue(Path.StrokeProperty, new SolidColorBrush(color));
- path.SetValue(Path.StrokeThicknessProperty, 1.0);
- // Borderの生成
- FrameworkElementFactory border = new FrameworkElementFactory(typeof(Border));
- double w = lwd * 10 + 3.0;
- border.SetValue(Border.WidthProperty, w);
- border.SetValue(Border.HeightProperty, w);
- border.SetValue(Border.BorderBrushProperty,
- new SolidColorBrush(Colors.Transparent));
- border.SetValue(Border.BorderThicknessProperty, new Thickness(1));
- border.AppendChild(path); // Pathを加える
- // コントロールテンプレートにborderを追加
- ControlTemplate ct = new ControlTemplate(typeof(Mark));
- ct.VisualTree = border;
- // StyleでTemplateを設定
- Style style = new Style(typeof(Mark));
- style.Setters.Add(new Setter(Mark.TemplateProperty, ct));
- return style;
- }
- private void Button_Click(object sender, RoutedEventArgs e)
- {
- dp2.MarkStyle = Cross1(5, Colors.Maroon);
- }
|