到目前为止,我已成功使用WPF Toolkit数据可视化控件创建了一个数据系列,如下所示:
如您所见,有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
中定义的转换器类。如果此控件不依赖于它们将会很好,因此不需要将转换器定义为资源。任何想法都表示赞赏。
答案 0 :(得分:1)
System.Windows.Markup.XamlReader.Load(System.IO.Stream stream) method
下面的代码显示了如何更改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;
您可以在场景中使用此方法,并替换字符串中的必要值。
但是现在如果我们尝试应用转换器,上述方法会产生问题。所以,我们现在采用纯代码方法,并自己构建所有元素。此方法涉及使用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>
我们将编写相应的代码如下:
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;