1

在 WPF 中,我试图在 UserControl 中绘制图形线。我需要按不同的量缩放 X 和 Y 值。

我有一个示例项目,我将在下面包含。问题是,当我使用 RenderTransform 缩放图形线时,LineThickness 也会被缩放。这通常不会成为问题,因为我可以通过缩放值的倒数对线条粗细进行缩放: LineThickness = desiredLineThickness * (1/scaledValue)

不幸的是,在这个应用程序中,我需要通过非常不同的值来缩放 X 和 Y 尺寸,并且 LineThickness 没有分成 X 和 Y 值。

在下面的示例中,我正在绘制一个正弦波,该正弦波被缩放到用户控件的绘图表面的大小。您可以看到峰和谷延伸到绘图表面之外。这是因为原始数据从 1.0 变为 -1.0,并缩放到绘图表面的高度并转换到绘图表面的中间。

为了更清楚地展示它,我还画了一条从 0,0 到 90,90 的蓝线,然后从 90,90 水平向右。这条线也被缩放到绘图表面的高度。可以看到水平线很粗。

我需要一种方法来将 Y 轴上的这些线与 X 轴分开,在这种情况下不会缩放。

我尝试了几种方法,包括制作包含的线条对象。我在想我可以将线条的端点放在缩放位置,然后在绘制实际线条时,我会使用渲染变换来缩放 Y 值,但由于整个绑定代理问题,我永远无法让它工作.

任何人有任何想法或曾经面临过这个问题?我不能成为第一个遇到这个问题的人。

这是代码:

UserControl1.xaml.cs:

namespace WpfExampleControlLibrary
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        #region Constructor

        public UserControl1()
        {
            InitializeComponent();

            GraphPens = new ObservableCollection<GraphPen>();
        }

        #endregion Constructor

        #region Public Methods

        #endregion Public Methods

        #region Dependency Properties

        // Pens

        public static PropertyMetadata GraphPenMetadata = new PropertyMetadata(null);
        public static DependencyProperty GraphPensProperty
            = DependencyProperty.Register(
                "GraphPens",
                typeof(ObservableCollection<GraphPen>),
                typeof(UserControl1),
                GraphPenMetadata);

        public ObservableCollection<GraphPen> GraphPens
        {
            get { return (ObservableCollection<GraphPen>)GetValue(GraphPensProperty); }
            set { SetValue(GraphPensProperty, value); }
        }

        // Debug Text

        public static PropertyMetadata DebugTextMetadata = new PropertyMetadata(null);
        public static DependencyProperty DebugTextProperty
            = DependencyProperty.Register(
                "DebugText",
                typeof(string),
                typeof(UserControl1),
                DebugTextMetadata);

        public string DebugText
        {
            get { return (string)GetValue(DebugTextProperty); }
            set { SetValue(DebugTextProperty, value); }
        }

        #endregion Dependency Properties

        private void DrawingSurface_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            foreach (GraphPen graphPen in GraphPens)
            {
                graphPen.SetDrawingDimensions(e.NewSize.Height, e.NewSize.Width);
            }
        }
    }
}

用户控件.xaml:

<UserControl Name="ExampleControl"
             x:Class="WpfExampleControlLibrary.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfExampleControlLibrary"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>

        <local:BindingProxy
            x:Key="UserControlBindingProxy"
            Data="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>

        <DataTemplate DataType="{x:Type local:GraphPen}">
            <ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas>
                            <Canvas.RenderTransform>
                                <TransformGroup>
                                    <TransformGroup.Children>
                                        <ScaleTransform
                                            CenterX="0.0"
                                            CenterY="0.0"
                                            ScaleX="1.0"
                                            ScaleY="{Binding ScaleHeight}"/>
                                        <TranslateTransform 
                                            X="0.0"
                                            Y="{Binding TranslateHeight}"/>
                                    </TransformGroup.Children>
                                </TransformGroup>
                            </Canvas.RenderTransform>
                        </Canvas>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemsSource>
                    <CompositeCollection>
                        <Path
                            Data="{Binding PenGeometry}"
                            StrokeThickness="{Binding PenLineThickness}"
                            Stroke="{Binding
                                PenLineColor,
                                PresentationTraceSources.TraceLevel=None}"
                            />
                    </CompositeCollection>
                </ItemsControl.ItemsSource>
            </ItemsControl>
        </DataTemplate>

    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <ItemsControl x:Name="DrawingSurface" Grid.Column="0" Grid.Row="0" SizeChanged="DrawingSurface_SizeChanged">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="Aquamarine">
                        <Canvas.LayoutTransform>
                            <ScaleTransform ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5"/>
                        </Canvas.LayoutTransform>
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemsSource>
                <CompositeCollection>
                    <CollectionContainer
                        Collection="{Binding
                            Source={StaticResource UserControlBindingProxy}, 
                            Path=Data.GraphPens,
                            Mode=OneWay}"/>
                    <Line X1="10" Y1="0" X2="200" Y2="180" Stroke="DarkRed" StrokeThickness="2"/>
                </CompositeCollection>
            </ItemsControl.ItemsSource>
        </ItemsControl>
        <TextBox
            x:Name="debug"
            Grid.Column="0" Grid.Row="1"
            Text="{Binding
                Source={StaticResource UserControlBindingProxy},
                Path=Data.DebugText,
                Mode=OneWay}"/>
    </Grid>
</UserControl>

GraphPen.cs:

namespace WpfExampleControlLibrary
{
    public class GraphPen : DependencyObject
    {
        private double _drawingSurfaceHeight = 100;

        #region Constructor

        public GraphPen()
        {
            PenGeometry = new PathGeometry();
            PenGeometry.Figures.Add(new PathFigure());
        }

        #endregion Constructor

        #region Dependency Properties

        // LineColor

        public static PropertyMetadata PenLineColorPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty PenLineColorProperty
            = DependencyProperty.Register(
                "PenLineColor",
                typeof(Brush),
                typeof(GraphPen),
                PenLineColorPropertyMetadata);
        public Brush PenLineColor
        {
            get { return (Brush)GetValue(PenLineColorProperty); }
            set { SetValue(PenLineColorProperty, value); }
        }

        // LineThickness

        public static PropertyMetadata PenLineThicknessPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty PenLineThicknessProperty
            = DependencyProperty.Register(
                "PenLineThickness",
                typeof(Int32),
                typeof(GraphPen),
                PenLineThicknessPropertyMetadata);
        public Int32 PenLineThickness
        {
            get { return (Int32)GetValue(PenLineThicknessProperty); }
            set { SetValue(PenLineThicknessProperty, value); }
        }

        // PenYMin

        public static PropertyMetadata PenYMinPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty PenYMinProperty
            = DependencyProperty.Register(
                "PenYMin",
                typeof(Double),
                typeof(GraphPen),
                PenYMinPropertyMetadata);
        public double PenYMin
        {
            get { return (double)GetValue(PenYMinProperty); }
            set { SetValue(PenYMinProperty, value); }
        }

        // PenYMax

        public static PropertyMetadata PenYMaxPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty PenYMaxProperty
            = DependencyProperty.Register(
                "PenYMax",
                typeof(Double),
                typeof(GraphPen),
                PenYMaxPropertyMetadata);
        public double PenYMax
        {
            get { return (double)GetValue(PenYMaxProperty); }
            set { SetValue(PenYMaxProperty, value); }
        }

        // ScaleHeight

        public static PropertyMetadata ScaleHeightPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty ScaleHeightProperty
            = DependencyProperty.Register(
                "ScaleHeight",
                typeof(Double),
                typeof(GraphPen),
                ScaleHeightPropertyMetadata);
        public double ScaleHeight
        {
            get { return (double)GetValue(ScaleHeightProperty); }
            set { SetValue(ScaleHeightProperty, value); }
        }

        // TranslateHeight

        public static PropertyMetadata TranslateHeightPropertyMetadata
            = new PropertyMetadata(null);
        public static DependencyProperty TranslateHeightProperty
            = DependencyProperty.Register(
                "TranslateHeight",
                typeof(Double),
                typeof(GraphPen),
                TranslateHeightPropertyMetadata);
        public double TranslateHeight
        {
            get { return (double)GetValue(TranslateHeightProperty); }
            set { SetValue(TranslateHeightProperty, value); }
        }

        // Pen Geometry

        public static PropertyMetadata PenGeometryMetadata = new PropertyMetadata(null);
        public static DependencyProperty PenGeometryProperty
            = DependencyProperty.Register(
                "PenGeometry",
                typeof(PathGeometry),
                typeof(GraphPen),
                PenGeometryMetadata);

        public PathGeometry PenGeometry
        {
            get { return (PathGeometry)GetValue(PenGeometryProperty); }
            set { SetValue(PenGeometryProperty, value); }
        }

        #endregion Dependency Properties

        public void SetDrawingDimensions(double height, double width)
        {
            double dataHeight;

            _drawingSurfaceHeight = height;

            dataHeight = PenYMax - PenYMin;
            ScaleHeight = _drawingSurfaceHeight / dataHeight;
            TranslateHeight = ScaleHeight * -PenYMin;
        }
    }
}

MainWindow.xaml:

<Window x:Class="POC_WPF_UserControlExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:Exmpl="clr-namespace:WpfExampleControlLibrary;assembly=WpfExampleControlLibrary"
        xmlns:local="clr-namespace:POC_WPF_UserControlExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="550" Width="550">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition />
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="100" />
            <RowDefinition />
            <RowDefinition Height="100" />
        </Grid.RowDefinitions>
        <Exmpl:UserControl1 Grid.Column="1" Grid.Row="1" x:Name="myExample"/>
    </Grid>
</Window>

MainWindow.xaml.cs:

namespace POC_WPF_UserControlExample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private DispatcherTimer _timer = null;
        private GraphPen _graphPen0 = null;
        private GraphPen _graphPen1 = null;
        private Int32 _pos = 0;
        private bool _firstTime = true;

        public MainWindow()
        {
            InitializeComponent();

            _graphPen0 = new GraphPen();
            _graphPen0.PenLineColor = Brushes.DarkGoldenrod;
            _graphPen0.PenLineThickness = 1;
            _graphPen0.PenYMax = 1.0;
            _graphPen0.PenYMin = -1.0;
            myExample.GraphPens.Add(_graphPen0);

            _graphPen1 = new GraphPen();
            _graphPen1.PenLineColor = Brushes.DarkBlue;
            _graphPen1.PenLineThickness = 1;
            _graphPen1.PenYMax = 10.0;
            _graphPen1.PenYMin = 0.0;
            myExample.GraphPens.Add(_graphPen1);

            _timer = new DispatcherTimer();
            _timer.Tick += Timer_Tick;
            _timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            _timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            _pos++;
            Point penPoint0 = new Point(_pos, Math.Sin(_pos % 360));
            Point penPoint1 = new Point(0,0);
            if (_pos <= 9) penPoint1 = new Point(_pos, _pos);
            if (_pos > 9) penPoint1 = new Point(_pos, 9);
            if (_firstTime)
            {
                myExample.GraphPens[0].PenGeometry.Figures[0].StartPoint = penPoint0;
                myExample.GraphPens[1].PenGeometry.Figures[0].StartPoint = penPoint1;
                _firstTime = false;
            }
            else
            {
                LineSegment segment0 = new LineSegment(penPoint0, true);
                myExample.GraphPens[0].PenGeometry.Figures[0].Segments.Add(segment0);
                LineSegment segment1 = new LineSegment(penPoint1, true);
                myExample.GraphPens[1].PenGeometry.Figures[0].Segments.Add(segment1);
            }

            myExample.DebugText = _pos.ToString();
        }
    }
}

按海报编辑

我忘了显示 BindingProcy.cs

namespace WpfExampleControlLibrary
{
    public class BindingProxy : Freezable
    {
        #region Override Freezable Abstract Parts
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion Override Freezable Abstract Parts

        #region Dependency Properties

        // Using a DependencyProperty as the backing store for Data.
        // This enables animation, styling, binding, etc...
        public static PropertyMetadata DataMetadata = new PropertyMetadata(null);
        public static readonly DependencyProperty DataProperty
            = DependencyProperty.Register(
                "Data",
                typeof(object),
                typeof(BindingProxy),
                DataMetadata);

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        #endregion Dependency Properties
    }
}

按海报编辑

这是一个屏幕截图:

在此处输入图像描述

4

0 回答 0