繁体   English   中英

WPF用户控件未更新路径

[英]WPF user control not updating path

我有一个简化的WPF示例,说明我在一个更大的项目中遇到的问题。 我有一个名为“ UserControl1”的用户控件。 数据上下文设置为self,因此我在后面的代码中定义了依赖项属性。

在控件中,我有一个ItemsControl。 在ItemsSource中,我有一个CompositeCollection,其中包含CollectionContainer和一行。 The Line只是为了向自己证明我正在绘画。

我还有一个名为“ GraphPen”的对象,其中包含PathGeometry依赖项属性。 用户控件的CollectionContainer包含这些GraphPens的ObservableCollection。

现在,我有一个“ MainWindow”来测试用户控件。 在MainWindow中,我有一个DispatchTimer,在该计时器的Tick事件中,我向LineFigure添加LineSegments,该PathFigure已添加到GraphPen单个实例的PathGeometry的Figures集合中。

我希望看到一条与现有的红线平行绘制的对角线,但是什么都没有显示。 如果在Tick事件处理程序的末尾放置一个断点,则可以检查用户控件并向下钻取,看是否存在线段。 由于某些原因,它们没有被渲染。 我怀疑我在装订中做错了什么。

我将在下面提供代码。

GraphPen.cs

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace WpfExampleControlLibrary
{
    public class GraphPen : DependencyObject
    {
        #region Constructor

        public GraphPen()
        {
            PenGeometry = new PathGeometry();
        }

        #endregion Constructor

        #region Dependency Properties

        // Line Color

        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); }
        }

        // Line Thickness

        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); }
        }

        // Pen Geometry

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

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

        #endregion Dependency Properties
    }
}

UserControl1.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"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>

        <DataTemplate DataType="{x:Type local:GraphPen}">
            <Path Stroke="{Binding Path=PenLineColor}"
                  StrokeThickness="{Binding Path=PenLineThickness}"
                  Data="{Binding Path=Geometry}">
            </Path>
        </DataTemplate>

    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <ItemsControl Grid.Column="0" Grid.Row="0">
            <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={RelativeSource Self}, 
                            Path=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 Path=DebugText}"/>
    </Grid>
</UserControl>

UserControl1.xaml.cs

namespace WpfExampleControlLibrary
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();

            GraphPens = new ObservableCollection<GraphPen>();
        }

        #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
    }
}

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 Int32 _pos = 0;
        private PathFigure _pathFigure = null;

        public MainWindow()
        {
            InitializeComponent();

            _graphPen0 = new GraphPen();
            _graphPen0.PenLineColor = Brushes.DarkGoldenrod;
            _graphPen0.PenLineThickness = 2;
            myExample.GraphPens.Add(_graphPen0);

            _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, _pos + 20);
            if (_graphPen0.PenGeometry.Figures.Count == 0)
            {
                _pathFigure = new PathFigure();
                _graphPen0.PenGeometry.Figures.Add(_pathFigure);
                _pathFigure.StartPoint = penPoint0;
            }
            else
            {
                LineSegment segment = new LineSegment(penPoint0, false);
                _pathFigure.Segments.Add(segment);
            }
            myExample.DebugText = _pos.ToString();
        }
    }
}

屏幕截图

绘制的线应与红线平行

我不需要INotifyPropertyChanged ,也不需要重新创建PenGeometry 对不起,我在这些想法上浪费了您的时间。

我有您的代码绘图...某样东西。 增长的线。 我不知道它是否在绘制您想要的东西,但是现在您可以弄清楚那部分,现在您可以看到它绘制什么。

首先, GraphPen.cs次要复制/粘贴错误:

    public static DependencyProperty PenGeometryProperty
        = DependencyProperty.Register(
            "PenGeometry",
            typeof(PathGeometry),
            typeof(UserControl1),
            PenGeometryMetadata);

所有者类型参数必须是GraphPen ,而不是UserControl1

            typeof(PathGeometry),
            typeof(GraphPen),
            PenGeometryMetadata);

第二:在UserControl1绑定。 您与Self绑定将无法正常工作,因为在这种情况下, Self是您要绑定的CollectionContainer 通常,您将使用RelativeSource={RelativeSource AncestorType=UserControl} ,但是CollectionContainer不在可视树中,因此不起作用(真正直观,是吗?)。 相反,我们使用BindingProxy (遵循源代码):

<UserControl.Resources>
    <!-- ... stuff ... -->

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

对于收集容器...

<CollectionContainer
    Collection="{
        Binding 
        Source={StaticResource UserControlBindingProxy},
        Path=Data.GraphPens,
        Mode=OneWay}"
    />

注意我们正在绑定到Data.GraphPens Data是代理的目标。

另外,我们需要ItemsControlItemTemplate ,因为它不知道如何显示GraphPen

<ItemsControl.ItemTemplate>
    <DataTemplate DataType="local:GraphPen">
        <Border >
            <Path
                Data="{Binding PenGeometry}"
                StrokeThickness="{Binding PenLineThickness}"
                Stroke="{Binding PenLineColor, PresentationTraceSources.TraceLevel=None}"
                />
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

注意PresentationTraceSources.TraceLevel=None None更改为High ,它将在VS Output窗格中为您提供有关Binding的大量调试信息。 我补充说,当我试图弄清楚为什么在构造函数PenLineColor设置为Brushes.Black时,但是在运行时它DarkGoldenrod出现DarkGoldenrod 你能说对吗? h!

现在是绑定代理。 这样做是将您要用作DataContext任何随机对象,将其绑定到定义为资源的BindingProxy实例上的Data ,并且已经通过可通过StaticResource获得的资源获得了DataContext 如果您在某个地方无法通过带有RelativeSource的可视树来获得某些东西,那么可以依靠它。

绑定代理

using System.Windows;

namespace WpfExampleControlLibrary
{
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

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

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

最后,在MainWindow中,您需要在LineSegment实例上为isStroked传递true

LineSegment segment = new LineSegment(penPoint0, true);

否则,它们不会被绘制。

因此,现在又回到您的腿上,温暖的菊科植物和舒缓的海蓝宝石中。 Ave,imperator等。

由原作者编辑!

谢谢您Ed的所有努力。

  1. 我不敢相信我错过了UserControl1-> GraphPen
  2. BindingProxy将非常方便
  3. TraceLevel也将很方便,我之前从未使用过
  4. 我还纠正了DebugText绑定,现在可以正常工作了

    我什至没有注意到!

  5. 在DataTemplate中,为什么我们需要将路径包装在边框中?

    我们没有。 在显示Path之前,我补充说要确保模板中有一些东西是可见的。 它最初有一个绿色边框。 我删除了这些属性,但忘记删除Border本身。

  6. 如果您查看我的Timer_Tick,您会注意到现在我需要更新的只是添加新的细分。 希望这将有助于性能。 你的意见?

    不知道。 我实际上会将该段添加到GraphPen作为AddSegment(Point pt)AddSegment(float x, float y) => AddSegment(new Point(x,y)); 超载。 我非常想在事件处理程序中放入逻辑。 我最想做的是将一个iftry/catch抛到一个可以完成实际工作的非处理程序方法周围。 然后,我AddSegment(Point pt)两种方式编写AddSegment(Point pt) ,并以另一种方式进行基准测试。

为了完整性,我将添加我的代码:

UserControl1.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}">
            <Border>
                <Path
                    Data="{Binding PenGeometry}"
                    StrokeThickness="{Binding PenLineThickness}"
                    Stroke="{Binding
                        PenLineColor,
                        PresentationTraceSources.TraceLevel=None}"
                    />
            </Border>
        </DataTemplate>

    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <ItemsControl Grid.Column="0" Grid.Row="0">
            <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>

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
    }
}

GraphPen.cs

namespace WpfExampleControlLibrary
{
    public class GraphPen : DependencyObject
    {
        #region Constructor

        public GraphPen()
        {
            PenGeometry = new PathGeometry();
        }

        #endregion Constructor

        #region Dependency Properties

        // Line Color

        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); }
        }

        // Line Thickness

        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); }
        }

        // 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
    }
}

绑定代理

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
    }
}

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 Int32 _pos = 0;
        private PathFigure _pathFigure0 = null;
        private bool _firstTime = true;

        public MainWindow()
        {
            InitializeComponent();

            _pathFigure0 = new PathFigure();
            _graphPen0 = new GraphPen();
            _graphPen0.PenLineColor = Brushes.DarkGoldenrod;
            _graphPen0.PenLineThickness = 2;
            _graphPen0.PenGeometry = new PathGeometry();
            _graphPen0.PenGeometry.Figures.Add(_pathFigure0);
            myExample.GraphPens.Add(_graphPen0);

            _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, _pos + 20);
            if (_firstTime)
            {
                myExample.GraphPens[0].PenGeometry.Figures[0].StartPoint = penPoint0;
                _firstTime = false;
            }
            else
            {
                LineSegment segment = new LineSegment(penPoint0, true);
                myExample.GraphPens[0].PenGeometry.Figures[0].Segments.Add(segment);
            }

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM