问题描述
我有条形图的UserControl
:
<UserControl ... Name="uc">
<Grid>
<Canvas>
<Canvas.Resources>
<Style TargetType="Line">
<Setter Property="X1" Value="0"/>
<Setter Property="X2" Value="{Binding ActualWidth, ElementName=uc}"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Canvas.Right" Value="0"/>
</Style>
</Canvas.Resources>
<Line Y1="{Binding HighestPoint}" Y2="{Binding HighestPoint}"
Canvas.Bottom="{Binding HighestPoint}"
Stroke="Red"/>
<TextBlock Text="{Binding HighestPoint, StringFormat=N0}"
Canvas.Bottom="{Binding HighestPoint}"/>
<Line Y1="{Binding SecondPoint}" Y2="{Binding SecondPoint}" Stroke="Blue"
Canvas.Bottom="{Binding SecondPoint}"/>
<TextBlock Text="{Binding SecondPoint, StringFormat=N0}"
Canvas.Bottom="{Binding SecondPoint}"/>
<Line Y1="{Binding FirstPoint}" Y2="{Binding FirstPoint}" Stroke="Green"
Canvas.Bottom="{Binding FirstPoint}"/>
<TextBlock Text="{Binding FirstPoint, StringFormat=N0}"
Canvas.Bottom="{Binding FirstPoint}"/>
</Canvas>
<ItemsControl ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding RectCollection}"
Margin="0 0 20 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="20" Height="{Binding}"
Margin="0 0 2 0"
VerticalAlignment="Bottom"
Opacity=".5" Fill="Green"/>
</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>
</Grid>
</UserControl>
在我的MainWindow
中,我已经在Grid.Row
中使用了它,它通常看起来像这样:
On my MainWindow
I've used it in a Grid.Row
and it it looks like this normally:
这些行顶部的值在SizeChanged
事件的ICommand
中重新计算.当我调整窗口大小时,它变成这样:
Values on top of those lines are recalculated in an ICommand
for SizeChanged
event. When I resize the window it becomes like this:
矩形的高度不会自动更改!我可以重新计算该ICommand
中每个矩形的高度,即double
,以重新调整效率不高的BUT,对吧?有没有简单的方法可以一次转换所有这些矩形?
Height of the rectangles don't change automatically! I could recalculate the height, which is a double
, of each rectangle in that ICommand
to readjust BUT that's inefficient, right? Is there any simple way to transform all those rectangles in a single shot?
编辑
我还要在Canvas
中进行一些更改,以使其正常运行.这是我现在UserControl
中的内容:
I'd to change a few things in Canvas
as well to make it work. Here's what I've in UserControl
now:
<UserControl .." Name="uc">
<UserControl.Resources>
<local:MyConverter x:Key="converter"/>
</UserControl.Resources>
<Grid VerticalAlignment="Bottom">
<Canvas>
<Canvas.Resources>
<Style TargetType="Line">
<Setter Property="X1" Value="0"/>
<Setter Property="X2" Value="{Binding ActualWidth, ElementName=uc}"/>
<Setter Property="StrokeThickness" Value="1"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Canvas.Right" Value="0"/>
</Style>
</Canvas.Resources>
<Line Y1="135" Y2="135" Canvas.Bottom="135" Stroke="Red"/>
<TextBlock Text="{Binding HighestPoint, StringFormat=N0}" Canvas.Bottom="135"/>
<Line Y1="90" Y2="90" Stroke="Blue" Canvas.Bottom="90"/>
<TextBlock Text="{Binding SecondPoint, StringFormat=N0}" Canvas.Bottom="90"/>
<Line Y1="45" Y2="45" Stroke="Green" Canvas.Bottom="45"/>
<TextBlock Text="{Binding FirstPoint, StringFormat=N0}" Canvas.Bottom="45"/>
</Canvas>
<ItemsControl ScrollViewer.CanContentScroll="True"
Height="135"
ItemsSource="{Binding RectCollection}"
Margin="0 0 20 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Width="20"
Margin="0 0 2 0"
VerticalAlignment="Bottom"
Opacity=".5" Fill="Green">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="ActualHeight"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="DataContext.HighestPoint"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding />
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<TextBlock Text="{Binding StringFormat=N2}"
Margin="0 0 0 20">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</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>
</Grid>
</UserControl>
在MainWindow
中,我有这些:
<Window ...>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding SizeChanged}"
CommandParameter="{Binding ElementName=uc}"/>
</i:EventTrigger>
<i:EventTrigger EventName="SizeChanged">
<i:InvokeCommandAction Command="{Binding SizeChanged}"
CommandParameter="{Binding ElementName=uc}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window.DataContext>
<local:VM/>
</Window.DataContext>
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:TestControl Grid.Row="1" x:Name="uc"/>
<Button Grid.Row="2" Content="Add" Margin="50" Command="{Binding Add}"/>
</Grid>
</Window>
和ViewModel中的这些是添加矩形并设置范围的相关函数:
and in ViewModel These are the relevant functions that adds rectangles and sets range:
public VM()
{
Add = new Command(AddRect, (o) => true);
SizeChanged = new Command(sizeChanged, (o) => true);
}
void sizeChanged(object obj)
{
var c = obj as TestControl;
CalculateLineText(c.ActualHeight);
}
void AddRect(object obj)
{
var value = rand.NextDouble() * 500;
if (max < value) CalculateLineText(value);
RectCollection.Insert(0, value);
}
void CalculateLineText(double d)
{
max = d;
HighestPoint = Math.Round(d);
FirstPoint = Math.Round(d / 3);
SecondPoint = Math.Round(d / 3 * 2);
}
和值转换器中的
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var height = (double)values[0];
var highest = (double)values[1];
var value = (double)values[2];
return value * height / highest;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
调整窗口大小时仍然存在一个小问题,这可能是因为我已经硬编码了ItemsControl
的高度.
There's still a little issue when I resize the window and it probably because I've hardcoded the height of the ItemsControl
.
编辑
此:
void CalculateLineText(double d)
{
if (max < d) max = d;
HighestPoint = Math.Round(max);
FirstPoint = Math.Round(max / 3);
SecondPoint = Math.Round(max / 3 * 2);
}
解决了调整大小的问题.
solves the issue on resize.
推荐答案
作为我对您的其他问题的回答的扩展 >,则当值范围为0..1
时,可以将ItemsControl的ActualHeight用作比例因子:
As an extension of my answer to your other question, you could use the ActualHeight of the ItemsControl as a scale factor when the value range is 0..1
:
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="20" Margin="2">
<Rectangle VerticalAlignment="Bottom"
Height="{Binding}" Fill="LightGray">
<Rectangle.LayoutTransform>
<ScaleTransform ScaleY="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
</Rectangle.LayoutTransform>
</Rectangle>
<TextBlock Text="{Binding StringFormat={}{0:N2}}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
在后面的代码中加上这个:
with this in the code behind:
Random r = new Random();
DataContext = Enumerable.Range(0, 20).Select(i => r.NextDouble());
除了使用ScaleTransform之外,您还可以对矩形的Height属性使用MultiBinding.以下示例假定在视图模型中除Values
集合外,还有一个Range
属性,例如像这样:
Instead of a ScaleTransform you may also use a MultiBinding for the Rectangle's Height property. The following example assumes that besides a Values
collection there is also a Range
property in the view model, e.g. like this:
Random r = new Random();
DataContext = new
{
Range = 100d,
Values = Enumerable.Range(0, 20).Select(i => r.NextDouble() * 100)
};
然后XAML将如下所示:
The XAML would then look like this:
<ItemsControl ItemsSource="{Binding Values}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="20" Margin="2">
<Rectangle VerticalAlignment="Bottom" Fill="LightGray">
<Rectangle.Height>
<MultiBinding Converter="{StaticResource HeightConverter}">
<Binding Path="ActualHeight"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="DataContext.Range"
RelativeSource="{RelativeSource AncestorType=ItemsControl}"/>
<Binding Path="."/>
</MultiBinding>
</Rectangle.Height>
</Rectangle>
<TextBlock Text="{Binding StringFormat={}{0:N2}}">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
使用此IValueConverter:
with this IValueConverter:
public class HeightConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (double)values[0] / (double)values[1] * (double)values[2];
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
这篇关于条形图中的矩形的自动ScaleTransform的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!