WPF - 用图钉标记图像

WPF - Marking an image with pushpins

是否可以通过创建自定义控件来实现? 请向我建议您实施此解决方案的想法。 提供示例将不胜感激。

回答你的问题:是的,这是可能的。

在使用 WPF 时,我强烈推荐 MVVM 体系结构模式。您需要的是:

  1. 一个canvas控件为了使用绝对定位
  2. 将显示背景图像的 image 控件
  3. 将显示图钉图像的自定义图钉控件。此控件还可以包含将用于生成描述控件的 DataTemplate。
  4. 将显示有关 pin 的信息的自定义控件(将在弹出窗口中使用)
  5. 一个 adorner 将在装饰层中呈现 pin 信息弹出窗口。将装饰器装饰器放置在与 canvas.
  6. 相同的位置

您需要存储的关于 pin 的信息:

  1. 它的 Canvas.Top 和 Canvas.Left 值
  2. 影响其视觉特征(例如图像、颜色等)的属性
  3. 弹出窗口中显示的信息(例如描述、图片)

然后您可以从数据库中读取所有条目并为每个条目创建一个 pin 视图模型并将视图模型绑定到 canvas 中的项目控件。不要忘记将 pin 控件的属性绑定到其视图模型的相应值(例如 Canvas.Left、Canvas.Top、描述等)。

至于弹出窗口,一旦您创建了装饰器 class,当您需要显示弹出窗口时,将其实例添加到 canvas 的装饰层,并在需要时将其删除关闭弹出窗口。

地图控件样式的示例如下所示(假设地图控件的视图模型包含可观察的引脚集合):

<Style TargetType="{x:Type local:Map}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:Map}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">

                    <Grid>
                        <AdornerDecorator></AdornerDecorator>

                        <ItemsControl ItemsSource="{Binding Path=Items}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <Canvas Background="White">

                                    </Canvas>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <local:Pin></local:Pin>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                            <ItemsControl.ItemContainerStyle>
                                <Style>
                                    <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
                                    <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
                                </Style>
                            </ItemsControl.ItemContainerStyle>
                        </ItemsControl>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这是一个简单呈现给定 FrameworkElement 的装饰器控件示例:

public class ControlAdorner : Adorner {

    FrameworkElement _control;
    public FrameworkElement Control {
        get {
            return (_control);
        }
        set {
            _control = value;
        }
    }

    public ControlAdorner(UIElement Element, FrameworkElement Control)
        : base(Element) {
        this.Control = Control;
        this.AddVisualChild(this.Control);
        this.IsHitTestVisible = false;
    }

    protected override Visual GetVisualChild(int index) {
        if (index != 0) throw new ArgumentOutOfRangeException();
        return _control;
    }

    protected override int VisualChildrenCount {
        get {
            return 1;
        }
    }

    public void UpdatePosition(Point point) {
        VisualOffset = new Vector(point.X, point.Y);
        this.InvalidateVisual();
    }

    protected override Size MeasureOverride(Size constraint) {
        Control.Measure(constraint);
        return Control.DesiredSize;
    }

    protected override Size ArrangeOverride(Size finalSize) {
        Control.Arrange(new Rect(new Point(VisualOffset.X, VisualOffset.Y - 20), finalSize));
        return new Size(Control.ActualWidth, Control.ActualHeight);
    }
}

下面是如何让 Pin 控件在鼠标悬停时显示装饰器:

public class Pin : Control {
    public DataTemplate DescriptionItemTemplate {
        get { return (DataTemplate)GetValue(DescriptionItemTemplateProperty); }
        set { SetValue(DescriptionItemTemplateProperty, value); }
    }
    public static readonly DependencyProperty DescriptionItemTemplateProperty =
        DependencyProperty.Register("DescriptionItemTemplate", typeof(DataTemplate), typeof(Pin), new PropertyMetadata(null));




    ControlAdorner _adorner;
    AdornerLayer _adornerLayer;

    static Pin() {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(Pin), new FrameworkPropertyMetadata(typeof(Pin)));
    }

    public Pin() {
        this.MouseEnter += Pin_MouseEnter;
        this.MouseLeave += Pin_MouseLeave;
    }

    private void Pin_MouseEnter(object sender, MouseEventArgs e) {
        _adornerLayer = AdornerLayer.GetAdornerLayer(this);

        FrameworkElement element = DescriptionItemTemplate.LoadContent() as FrameworkElement;
        if (element == null) { return; }
        element.DataContext = this.DataContext;

        _adorner = new ControlAdorner(this, element);
        _adornerLayer.Add(_adorner);
    }


    private void Pin_MouseLeave(object sender, MouseEventArgs e) {
        _adornerLayer.Remove(_adorner);
        _adorner = null;
    }
}