wpf - 通过基本的 WPF 指导新手? (我不理解它。)

标签 wpf

问题正如其名。无论我多么努力、多么频繁地尝试理解 WPF,我都觉得我的头撞到了墙上。我喜欢 Winforms,一切都有意义。

例如,我正在尝试编写一个简单的应用程序,它允许我布置一堆二维路径(由折线表示)并拖动它们的顶点,并使顶点信息与演示者(即,我想,一个 ViewModel)

所以问题是这样的:

  • 使窗口识别 IExtendedObjectPresenter 的实例作为数据源;
  • 来自 IExtendedObject 的合集, 为每个 IExtendedObject 绘制一条多段线;
  • 对于扩展对象中的每个顶点,由集合 IExtendedObject.Points 表示, 在指定坐标处放置折线顶点。

  • IDE 在这里根本没有给我任何帮助。 都没有许多 XAML 中可用的属性对我来说很有意义。因为似乎隐含地做了这么多,所以没有明显的地方可以告诉窗口该做什么。

    在我被安排并告诉 RTFM 之前,我想再次强调我已经研究了很多次 WPF 的基本概念。我对它的了解并不比它第一次发布时多。它似乎完全无法穿透。为一种行为给出的例子在任何情况下都不适用于一种甚至略有不同的行为,所以你又回到了原点。我希望重复和有针对性的考试可能会在某个时候点亮我的脑海。

    最佳答案

    我很同情你。真正理解 WPF 需要很长时间,完成最简单的事情可能会非常令人沮丧。但是,深入研究专家们并不容易的问题只是自找麻烦。您需要处理更简单的任务并阅读大量代码,直到事情开始变得有意义。 Donald Knuth 说在你做练习之前你并不真正了解 Material 。

    我解决了你的问题,我承认有很多先进的概念可以干净利落地做到这一点,而使用 MVVM 会使它变得更加困难。对于它的值(value),这里是本着 MVVM 精神的零代码隐藏解决方案。

    这是 XAML:

    <Grid>
        <Grid.Resources>
            <local:PolylineCollection x:Key="sampleData">
                <local:Polyline>
                    <local:Coordinate X="50" Y="50"/>
                    <local:Coordinate X="100" Y="100"/>
                    <local:Coordinate X="50" Y="150"/>
                </local:Polyline>
            </local:PolylineCollection>
        </Grid.Resources>
        <Grid DataContext="{StaticResource sampleData}">
            <ItemsControl ItemsSource="{Binding Segments}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Line X1="{Binding Start.X}" Y1="{Binding Start.Y}" X2="{Binding End.X}" Y2="{Binding End.Y}" Stroke="Black" StrokeThickness="2"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <ItemsControl ItemsSource="{Binding ControlPoints}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemContainerStyle>
                    <Style TargetType="ContentPresenter">
                        <Setter Property="Canvas.Left" Value="{Binding X}"/>
                        <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Ellipse Margin="-10,-10,0,0" Width="20" Height="20" Stroke="DarkBlue" Fill="Transparent">
                            <i:Interaction.Behaviors>
                                <local:ControlPointBehavior/>
                            </i:Interaction.Behaviors>
                        </Ellipse>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </Grid>
    

    这是支持类:
    public class Coordinate : INotifyPropertyChanged
    {
        private double x;
        private double y;
    
        public double X
        {
            get { return x; }
            set { x = value; OnPropertyChanged("X", "Point"); }
        }
        public double Y
        {
            get { return y; }
            set { y = value; OnPropertyChanged("Y", "Point"); }
        }
        public Point Point
        {
            get { return new Point(x, y); }
            set { x = value.X; y = value.Y; OnPropertyChanged("X", "Y", "Point"); }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(params string[] propertyNames)
        {
            foreach (var propertyName in propertyNames)
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    public class Polyline : List<Coordinate>
    {
    }
    
    public class Segment
    {
        public Coordinate Start { get; set; }
        public Coordinate End { get; set; }
    }
    
    public class PolylineCollection : List<Polyline>
    {
        public IEnumerable<Segment> Segments
        {
            get
            {
                foreach (var polyline in this)
                {
                    var last = polyline.FirstOrDefault();
                    foreach (var coordinate in polyline.Skip(1))
                    {
                        yield return new Segment { Start = last, End = coordinate };
                        last = coordinate;
                    }
                }
            }
        }
    
        public IEnumerable<Coordinate> ControlPoints
        {
            get
            {
                foreach (var polyline in this)
                {
                    foreach (var coordinate in polyline)
                        yield return coordinate;
                }
            }
        }
    }
    
    public class ControlPointBehavior : Behavior<FrameworkElement>
    {
        private bool mouseDown;
        private Vector delta;
    
        protected override void OnAttached()
        {
            var canvas = AssociatedObject.Parent as Canvas;
            AssociatedObject.MouseLeftButtonDown += (s, e) =>
            {
                mouseDown = true;
                var mousePosition = e.GetPosition(canvas);
                var elementPosition = (AssociatedObject.DataContext as Coordinate).Point;
                delta = elementPosition - mousePosition;
                AssociatedObject.CaptureMouse();
            };
            AssociatedObject.MouseMove += (s, e) =>
            {
                if (!mouseDown) return;
                var mousePosition = e.GetPosition(canvas);
                var elementPosition = mousePosition + delta;
                (AssociatedObject.DataContext as Coordinate).Point = elementPosition;
            };
            AssociatedObject.MouseLeftButtonUp += (s, e) =>
            {
                mouseDown = false;
                AssociatedObject.ReleaseMouseCapture();
            };
        }
    }
    

    该解决方案使用了非常适合与 MVVM 实现交互的行为。

    如果您不熟悉行为,请安装 Expression Blend 4 SDK 并添加以下命名空间:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    

    并添加 System.Windows.Interactivity到您的项目。

    关于wpf - 通过基本的 WPF 指导新手? (我不理解它。),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4768029/

    相关文章:

    c# - 为什么不能在 Window 构造函数中显示两次 FolderBrowserDialog?

    wpf - 您可以使用Powershell和WPF使用PowerBoots来实现Model-View-ViewModel吗?

    c# - 以编程方式打开 Windows 8 蓝牙控制面板

    wpf - Windows 应用商店中的非 UWP 应用如何?

    wpf - 如何为多个 View 重用 Entity Framework 数据源 WPF EF MVVM

    c# - 阻止 IValueConverter 设置本地值

    wpf mvvm 客户端服务器应用程序

    c# - 进行大量 PropertyUpdates 时避免 UI 锁定

    wpf - 将 double 绑定(bind)到文本框

    c# - 以编程方式将枚举类型绑定(bind)到组合框