如何使用WPF Toolkit数据可视化控件填充2个数据系列之间的区域?

时间:2015-11-21 06:34:05

标签: c# wpf xaml

到目前为止,我已成功使用WPF Toolkit数据可视化控件创建了一个数据系列,如下所示:

Custom area chart

如您所见,有3个数据系列(顶部,中部,底部)。我为每个人使用AreaSeries并应用此样式:

<Style x:Key="TopAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type chart:AreaSeries}">
                        <Canvas x:Name="PlotArea">
                            <Path StrokeThickness="1.5" Stroke="Black" Fill="Yellow" Opacity=".4">
                                <Path.Style>
                                    <Style TargetType="{x:Type Path}">
                                        <Setter Property="Data">
                                            <Setter.Value>
                                                <MultiBinding Converter="{StaticResource geoExclusionConverter}">
                                                    <Binding ElementName="TopAreaSeries" Path="Geometry"/>
                                                    <Binding ElementName="MiddleAreaSeries" Path="Geometry"/>
                                                </MultiBinding>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </Path.Style>
                            </Path>
                        </Canvas>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="MiddleAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}"...>
        <Style x:Key="BottomAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}"...>

我必须创建此样式,以便仅在区域系列之间呈现彩色区域。对于每个区域系列,我必须应用此样式,因此有3种样式在剪切区域发生的位置不同。例如,与中间AreaSeries区域相撞的顶部AreaSeries的区域被剪切,中间AreaSeries到底部AreaSeries也是如此。

剪切过程由转换器类处理,该类消耗了MultiBinding标记中定义的2个参数。第一个参数采用将被剪裁的几何体,第二个参数采用用于剪切的几何体。

public class GeometryExclusionConverter : IMultiValueConverter
  {
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      if (values[0] == null && values[1] == null)
        return new PathGeometry();
      else
      {
        var geo1 = values[0] as Geometry;
        var geo2 = values[1] as Geometry;
        return new CombinedGeometry(GeometryCombineMode.Exclude, geo1, geo2);
      }
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }

通过使用这种方法,我可以得到我所需要的一切。但问题是,可能有超过7个数据系列,因此我必须创建许多样式,这些样式仅在传递的参数方面有所不同。它产生了很多依赖(风格和转换器)。如果可以在一个地方处理所有这些东西,那就太好了。

我已经搜索了如何使用参数创建样式,因此我可以通过在其上传递参数来以某种方式应用样式,但它们似乎都不起作用。

更新

使用@AnjumSKhan提出的建议,我可以通过将这些代码放在OnInitialized事件上来简化样式。

public class ClippedAreaSeries : AreaSeries
{    
    public string ClippedArea
    {
      get { return (string)GetValue(ClippedAreaProperty); }
      set { SetValue(ClippedAreaProperty, value); }
    }

    public static readonly DependencyProperty ClippedAreaProperty =
    DependencyProperty.Register("ClippedArea", typeof(string), typeof(ClippedAreaSeries), new PropertyMetadata(null));

    protected override void OnInitialized(EventArgs e)
    {
      base.OnInitialized(e);

      string templateXml =
            @"
            <ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
                        xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'                             
                        >
                    <Canvas x:Name='PlotArea'>
                        <Path StrokeThickness='1.5' Stroke='Black' Fill='Yellow' Opacity='.25'>
                            <Path.Style>
                                <Style TargetType='{x:Type Path}'>
                                    <Setter Property='Data'>
                                        <Setter.Value>
                                            <MultiBinding Converter='{StaticResource geoExclusionConverter}'>
                                                <Binding ElementName='{0}' Path='Geometry'/>
                                                <Binding ElementName='{1}' Path='Geometry'/>
                                            </MultiBinding>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </Path.Style>
                        </Path>
                    </Canvas>
                </ControlTemplate>
                ";

     templateXml = templateXml.Replace("{0}", this.Name).Replace("{1}", this.ClippedArea);
     //so on...
  }
}

请注意,它仍然取决于必须在Application.Resources中定义的转换器类。如果此控件不依赖于它们将会很好,因此不需要将转换器定义为资源。任何想法都表示赞赏。

2 个答案:

答案 0 :(得分:1)

  1. 您可以使用System.Windows.Markup.XamlReader.Load(System.IO.Stream stream) method
  2. 轻松地使用代码设置ControlTemplate

    下面的代码显示了如何更改Button的控件模板:

    string templateXml =
             @"<ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
                                xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
                <Canvas Background='Purple'>
                    <TextBlock Text='custom btn'/>
                </Canvas>
                </ControlTemplate>";
    
                ControlTemplate template;
                using (var stringReader = new System.IO.StringReader(templateXml))
                using (XmlReader xmlReader = XmlReader.Create(stringReader))
                {
                    template = (ControlTemplate)XamlReader.Load(xmlReader);
                }
    
                Btn1.Template = template;
    

    您可以在场景中使用此方法,并替换字符串中的必要值。

    1. 但是现在如果我们尝试应用转换器,上述方法会产生问题。所以,我们现在采用纯代码方法,并自己构建所有元素。此方法涉及使用FrameworkElementFactory类。例如,如果我们想要更改Button的ControlTemplate并将转换器应用于TextBlock的Text属性,如下所示:

      <ControlTemplate TargetType="Button">
              <Canvas Background='Red'>
                  <TextBlock>
                      <TextBlock.Text>
                          <Binding Path='Content' RelativeSource="{RelativeSource Mode=TemplatedParent}" >
                              <Binding.Converter>
                                  <converter:ContentConverter/>
                              </Binding.Converter>
                          </Binding>
                      </TextBlock.Text>
                  </TextBlock>
              </Canvas>
          </ControlTemplate>
      
    2. 我们将编写相应的代码如下:

      FrameworkElementFactory canvas = new FrameworkElementFactory(typeof(Canvas));
      canvas.SetValue(Canvas.BackgroundProperty, new SolidColorBrush(Colors.Red));
      
      FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
      Binding binding = new Binding("Content");
      binding.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
      binding.Converter = new Converters.ContentConverter();
      tb.SetBinding(TextBlock.TextProperty, binding);
      
      canvas.AppendChild(tb);
      
      ControlTemplate ct = new ControlTemplate(typeof(Button));
      ct.VisualTree = canvas;
      
      Btn1.Template = ct;
      

      根据上述方法,您发布的控件模板样式将写为:

      FrameworkElementFactory canvas = new FrameworkElementFactory(typeof(Canvas));
                  canvas.Name = "PlotArea";
      
                  FrameworkElementFactory path = new FrameworkElementFactory(typeof(Path));
                  {                
                      path.SetValue(Path.StrokeThicknessProperty, 1.5);
                      path.SetValue(Path.StrokeProperty, new SolidColorBrush(Colors.Green));
                      path.SetValue(Path.OpacityProperty, 0.25);
      
                      MultiBinding binding = new MultiBinding();
                      // create your converter properly below
                      binding.Converter = new Converters.GeoConverter();
      
                      Binding bindingItem1 = new Binding();
                      bindingItem1.ElementName = "AreaPlus1SD";
                      bindingItem1.Path = new PropertyPath("Geometry");
      
                      Binding bindingItem2 = new Binding();
                      bindingItem2.ElementName = "AreaMedian";
                      bindingItem2.Path = new PropertyPath("Geometry");
      
                      binding.Bindings.Add(bindingItem1);
                      binding.Bindings.Add(bindingItem2);
      
                      path.SetBinding(Path.DataProperty, binding);
                  }
      
                  canvas.AppendChild(path);
      
                  ControlTemplate ct = new ControlTemplate(typeof(ChartingToolkit.AreaSeries));
                  ct.VisualTree = canvas;
      
                 AreaSeries1.Template = ct;
      

答案 1 :(得分:0)

你可以这样做:

    var customStyle = new Style(typeof (Button));
    customStyle.Setters.Add(new Setter{Property = Button.BackgroundProperty, Value = new SolidColorBrush(){Color = Colors.Red}});
    ButtonTest.Style = customStyle;