绘制椭圆 Line/PolyLine 双
Drawing Ellipse Line/PolyLine with a double
我一直在使用 示例来绘制条形图。条形部分已完成,它代表交易中的 volume/quantity。它是 20 - 30 范围内的相关价格。我现在想要的是绘制点来表示与交易量相关的价格并连接这些点。我在链接示例的编辑部分中进行了两项更改(1)从 ItemsControl
的 DataTemplate
中删除了 TextBlock
并添加了 Ellipse
并且(2)进行了编辑canvas 添加price/volume 轴标签。这是现在的样子:
如何将 Ellipse
添加到正确的位置并将它们与 Line
/PolyLine
连接起来?
编辑
这是我现在在 ItemsControl
中的内容:
<ItemsControl ScrollViewer.CanContentScroll="True"
Height="135"
ItemsSource="{Binding RectCollection}"
Margin="50 0 50 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Width="20">
<Canvas.LayoutTransform>
<ScaleTransform ScaleY="-1"/>
</Canvas.LayoutTransform>
<Rectangle Width="18"
Margin="0 0 2 0"
VerticalAlignment="Bottom"
Opacity=".5" Fill="LightGray">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource VConverter}">
<Binding Path="ActualHeight"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="DataContext.HighestPoint"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="Volume"/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<Line Stroke="DarkGreen" StrokeThickness="1"
X1="10" X2="30"
Y2="{Binding PreviousPrice, Converter={StaticResource PConverter}}"
Y1="{Binding CurrentPrice, Converter={StaticResource PConverter}}">
<Line.Style>
<Style TargetType="Line">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=PreviousPrice}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<Ellipse Fill="Red" Width="6" Height="6" Margin="-3" Canvas.Left="10"
Canvas.Top="{Binding CurrentPrice, Converter={StaticResource PConverter}}"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer
VerticalScrollBarVisibility="Hidden"
Background="{TemplateBinding Panel.Background}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
哦!我忘了添加那些 ValueConverters,这里是那些:
public class VolumeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var height = (double)values[0];
var higest = (double)values[1];
var value = (double)values[2];
return value * height / higest;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class PriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is double)) return null;
var price = (double)value;
var remainingHeight = 90;
var priceRange = 30 - 20.0;
return 45 + ((price - 20) * remainingHeight / priceRange);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
这是它的样子:
根据 @Clemens
的建议,我必须有另一个 double?
,以防在 ObservableCollection
上使用 Insert(0, ... )
而不是 Add(...)
首先添加最后一项并删除 AternationCount/Index
内容。
以下示例使用垂直翻转 Canvas 反转 y 轴顺序,使其向上。所以 PConverter
应该 return 正 y 值。
除了 Rectangle 和 Ellipse 元素之外,它还通过值绑定中的 RelativeSource={RelativeSource PreviousData}
从前一个数据值绘制一个 Line
元素到当前数据值。它还在 AlternationIndex
上使用 DataTrigger 来隐藏第一行。
<ItemsControl ... AlternationCount="2147483647">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Width="20">
<Canvas.LayoutTransform>
<ScaleTransform ScaleY="-1"/>
</Canvas.LayoutTransform>
<Rectangle Fill="LightGray" Margin="1" Width="18"
Height="{Binding Value1, Converter={StaticResource PConverter}}"/>
<Line Stroke="DarkGreen" StrokeThickness="3"
X1="-10" X2="10"
Y1="{Binding Price,
Converter={StaticResource PConverter},
RelativeSource={RelativeSource PreviousData}}"
Y2="{Binding Price,
Converter={StaticResource PConverter}}">
<Line.Style>
<Style TargetType="Line">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=(ItemsControl.AlternationIndex),
RelativeSource={RelativeSource
AncestorType=ContentPresenter}}"
Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<Ellipse Fill="Red" Width="6" Height="6" Margin="-3" Canvas.Left="10"
Canvas.Top="{Binding Price, Converter={StaticResource PConverter}}"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
由于现在也为不存在的值调用值转换器(对于第一项的 PreviousData
),您必须确保它检查传递的值是否实际上是双精度值:
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is double)) return 0d;
...
}
我一直在使用 ItemsControl
的 DataTemplate
中删除了 TextBlock
并添加了 Ellipse
并且(2)进行了编辑canvas 添加price/volume 轴标签。这是现在的样子:
如何将 Ellipse
添加到正确的位置并将它们与 Line
/PolyLine
连接起来?
编辑
这是我现在在 ItemsControl
中的内容:
<ItemsControl ScrollViewer.CanContentScroll="True"
Height="135"
ItemsSource="{Binding RectCollection}"
Margin="50 0 50 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Width="20">
<Canvas.LayoutTransform>
<ScaleTransform ScaleY="-1"/>
</Canvas.LayoutTransform>
<Rectangle Width="18"
Margin="0 0 2 0"
VerticalAlignment="Bottom"
Opacity=".5" Fill="LightGray">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource VConverter}">
<Binding Path="ActualHeight"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="DataContext.HighestPoint"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="Volume"/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<Line Stroke="DarkGreen" StrokeThickness="1"
X1="10" X2="30"
Y2="{Binding PreviousPrice, Converter={StaticResource PConverter}}"
Y1="{Binding CurrentPrice, Converter={StaticResource PConverter}}">
<Line.Style>
<Style TargetType="Line">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=PreviousPrice}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<Ellipse Fill="Red" Width="6" Height="6" Margin="-3" Canvas.Left="10"
Canvas.Top="{Binding CurrentPrice, Converter={StaticResource PConverter}}"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer
VerticalScrollBarVisibility="Hidden"
Background="{TemplateBinding Panel.Background}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
哦!我忘了添加那些 ValueConverters,这里是那些:
public class VolumeConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var height = (double)values[0];
var higest = (double)values[1];
var value = (double)values[2];
return value * height / higest;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class PriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is double)) return null;
var price = (double)value;
var remainingHeight = 90;
var priceRange = 30 - 20.0;
return 45 + ((price - 20) * remainingHeight / priceRange);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
这是它的样子:
根据 @Clemens
的建议,我必须有另一个 double?
,以防在 ObservableCollection
上使用 Insert(0, ... )
而不是 Add(...)
首先添加最后一项并删除 AternationCount/Index
内容。
以下示例使用垂直翻转 Canvas 反转 y 轴顺序,使其向上。所以 PConverter
应该 return 正 y 值。
除了 Rectangle 和 Ellipse 元素之外,它还通过值绑定中的 RelativeSource={RelativeSource PreviousData}
从前一个数据值绘制一个 Line
元素到当前数据值。它还在 AlternationIndex
上使用 DataTrigger 来隐藏第一行。
<ItemsControl ... AlternationCount="2147483647">
...
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas Width="20">
<Canvas.LayoutTransform>
<ScaleTransform ScaleY="-1"/>
</Canvas.LayoutTransform>
<Rectangle Fill="LightGray" Margin="1" Width="18"
Height="{Binding Value1, Converter={StaticResource PConverter}}"/>
<Line Stroke="DarkGreen" StrokeThickness="3"
X1="-10" X2="10"
Y1="{Binding Price,
Converter={StaticResource PConverter},
RelativeSource={RelativeSource PreviousData}}"
Y2="{Binding Price,
Converter={StaticResource PConverter}}">
<Line.Style>
<Style TargetType="Line">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=(ItemsControl.AlternationIndex),
RelativeSource={RelativeSource
AncestorType=ContentPresenter}}"
Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Line.Style>
</Line>
<Ellipse Fill="Red" Width="6" Height="6" Margin="-3" Canvas.Left="10"
Canvas.Top="{Binding Price, Converter={StaticResource PConverter}}"/>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
由于现在也为不存在的值调用值转换器(对于第一项的 PreviousData
),您必须确保它检查传递的值是否实际上是双精度值:
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is double)) return 0d;
...
}