C# 用于位图背景图像的WPF元素,能够添加子控件

C# 用于位图背景图像的WPF元素,能够添加子控件,c#,wpf,winforms,canvas,panel,C#,Wpf,Winforms,Canvas,Panel,因此,我使用WinForms应用程序作为创建显示海图的WPF应用程序的指南。WinForms应用程序正在使用System.Windows.Forms.Panel并将背景图像设置为动态创建的位图。我需要在面板中添加一个子画布(或替代控件),以便显示覆盖图表的航路点 在WPF中,我曾尝试使用画布作为面板的替代品,但由于它有一个接受画笔而不是位图的背景属性,所以我无法使其正常工作 我还尝试使用WinFormsHost来利用System.Windows.Forms.Panel对象,但我不能使用它来在其中

因此,我使用WinForms应用程序作为创建显示海图的WPF应用程序的指南。WinForms应用程序正在使用System.Windows.Forms.Panel并将背景图像设置为动态创建的位图。我需要在面板中添加一个子画布(或替代控件),以便显示覆盖图表的航路点

在WPF中,我曾尝试使用画布作为面板的替代品,但由于它有一个接受画笔而不是位图的背景属性,所以我无法使其正常工作

我还尝试使用WinFormsHost来利用System.Windows.Forms.Panel对象,但我不能使用它来在其中容纳另一个控件


所以我需要的最好是一个WPF元素,我可以不用WinFormsHost来使用它来设置位图背景图像并添加另一个覆盖了图形的控件

您可以使用ImageBrush设置背景

然而,这样做有一个很大的缺点,因为画布的大小与图像的大小不匹配。您基本上可以在画布后面使用图像:

<Grid>
    <Image Source="..."/>
    <Canvas ...>
    </Canvas>
</Grid>

您可以使用ImageBrush设置背景

然而,这样做有一个很大的缺点,因为画布的大小与图像的大小不匹配。您基本上可以在画布后面使用图像:

<Grid>
    <Image Source="..."/>
    <Canvas ...>
    </Canvas>
</Grid>

每当需要在WPF中显示某个内容的列表时,通常使用ItemsControl,这种情况也不例外

使用ItemsControl,您可以覆盖它使用的面板,您选择画布而不是面板是正确的。将图像设置为背景很容易,只需使用ImageBrush即可

至于您的航路点,我猜您还需要显示其他对象类型,因此为每个航路点创建一个ViewModel,并使用DataTemplate根据类型选择适当的图形。创建的每个图形都将包装在ContentPresenter中,但ItemsControl还允许您通过ItemContainerStyle覆盖该图形的样式,因此您可以在此处设置Canvas.Left和Canvas.Top来定位项目

将所有这些放在一起,您的XAML需要如下所示:

<Viewbox>
    <ItemsControl ItemsSource="{Binding ChartElements}" Width="1000" Height="1000">
        <ItemsControl.Resources>
            <!-- DataTemplates here select the appropriate graphic to display for each class type -->
            <DataTemplate DataType="{x:Type local:Waypoint}">
                <Ellipse Width="50" Height="50" Fill="Yellow" Stroke="CornflowerBlue" StrokeThickness="5">
                    <Ellipse.RenderTransform>
                        <TranslateTransform X="-25" Y="-25" /> <!-- center the ellipse -->
                    </Ellipse.RenderTransform>
                </Ellipse>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:NavigationLine}">
                <Line X1="0" Y1="0" X2="{Binding Width}" Y2="{Binding Height}" Stroke="CornflowerBlue" StrokeThickness="10" StrokeDashArray="3 1" />
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <!-- Replace the default panel with a Canvas -->
            <ItemsPanelTemplate>
                <Canvas>
                    <Canvas.Background>
                        <ImageBrush ImageSource="https://images-na.ssl-images-amazon.com/images/I/A1%2Bp%2BB8wq2L._SL1500_.jpg" Stretch="Uniform" />
                    </Canvas.Background>
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <!-- Position each item on the canvas and set the ZIndex so that waypoints appear on top -->
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Canvas.Left" Value="{Binding X}" />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
                <Setter Property="Panel.ZIndex" Value="{Binding Layer}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Viewbox>
<map:Map Center="50,0" ZoomLevel="2">
    <map:Map.MapLayer>
        <local:ChartImageLayer />
    </map:Map.MapLayer>
</map:Map>
using MapControl;
...

public class Waypoint
{
    public string Label { get; set; }
    public Location Location { get; set; }
}

public class ViewModel
{
    public ObservableCollection<Waypoint> Waypoints { get; }
        = new ObservableCollection<Waypoint>();
}
<map:Map Center="50,0" ZoomLevel="2">
    ...
    <map:MapItemsControl ItemsSource="{Binding Waypoints}">
        <map:MapItemsControl.ItemContainerStyle>
            <Style TargetType="map:MapItem">
                <Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
            </Style>
        </map:MapItemsControl.ItemContainerStyle>
        <map:MapItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <Path Fill="Red">
                        <Path.Data>
                            <EllipseGeometry RadiusX="5" RadiusY="5"/>
                        </Path.Data>
                    </Path>
                    <TextBlock Margin="5,-5" Text="{Binding Label}"/>
                </Canvas>
            </DataTemplate>
        </map:MapItemsControl.ItemTemplate>
    </map:MapItemsControl>
</map:Map>

您应该始终创建MainViewModel并执行适当的MVVM,但以下示例代码将帮助您开始:

public partial class MainWindow : Window
{
    public List<object> ChartElements { get; } = new List<object>
    {
        new Waypoint{X=100, Y=100 },
        new Waypoint{X=500, Y=300 },
        new Waypoint{X=300, Y=500 },
        new Waypoint{X=800, Y=700 },

        new NavigationLine{X1=100, Y1=100, X2=500, Y2=300},
        new NavigationLine{X1=500, Y1=300, X2=300, Y2=500},
        new NavigationLine{X1=300, Y1=500, X2=800, Y2=700}
    };

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }
}

public class Waypoint
{
    public int Layer { get; } = 1; // waypoint circles should always appear on top
    public double X { get; set; }
    public double Y { get; set; }
}

public class NavigationLine
{
    public int Layer { get; } = 0;
    public double X1 { get; set; }
    public double Y1 { get; set; }
    public double X2 { get; set; }
    public double Y2 { get; set; }

    public double X => this.X1;
    public double Y => this.Y1;
    public double Width => this.X2 - this.X1;
    public double Height => this.Y2 - this.Y1;
}
公共部分类主窗口:窗口
{
公共列表ChartElements{get;}=新列表
{
新航路点{X=100,Y=100},
新航路点{X=500,Y=300},
新航路点{X=300,Y=500},
新航路点{X=800,Y=700},
新导航线{X1=100,Y1=100,X2=500,Y2=300},
新导航线{X1=500,Y1=300,X2=300,Y2=500},
新导航线{X1=300,Y1=500,X2=800,Y2=700}
};
公共主窗口()
{
初始化组件();
this.DataContext=this;
}
}
公共类航路点
{
公共整数层{get;}=1;//航路点圆应始终显示在顶部
公共双X{get;set;}
公共双Y{get;set;}
}
公共类导航线
{
公共int层{get;}=0;
公共双X1{get;set;}
公共双Y1{get;set;}
公共双X2{get;set;}
公共双Y2{get;set;}
公共双X=>this.X1;
公共双Y=>this.Y1;
公共双宽度=>this.X2-this.X1;
公共双倍高度=>this.Y2-this.Y1;
}
加载热链接图像可能需要几秒钟的时间,但您应该以以下方式结束:


每当需要在WPF中显示某个内容的列表时,通常使用ItemsControl,这种情况也不例外

使用ItemsControl,您可以覆盖它使用的面板,您选择画布而不是面板是正确的。将图像设置为背景很容易,只需使用ImageBrush即可

至于您的航路点,我猜您还需要显示其他对象类型,因此为每个航路点创建一个ViewModel,并使用DataTemplate根据类型选择适当的图形。创建的每个图形都将包装在ContentPresenter中,但ItemsControl还允许您通过ItemContainerStyle覆盖该图形的样式,因此您可以在此处设置Canvas.Left和Canvas.Top来定位项目

将所有这些放在一起,您的XAML需要如下所示:

<Viewbox>
    <ItemsControl ItemsSource="{Binding ChartElements}" Width="1000" Height="1000">
        <ItemsControl.Resources>
            <!-- DataTemplates here select the appropriate graphic to display for each class type -->
            <DataTemplate DataType="{x:Type local:Waypoint}">
                <Ellipse Width="50" Height="50" Fill="Yellow" Stroke="CornflowerBlue" StrokeThickness="5">
                    <Ellipse.RenderTransform>
                        <TranslateTransform X="-25" Y="-25" /> <!-- center the ellipse -->
                    </Ellipse.RenderTransform>
                </Ellipse>
            </DataTemplate>
            <DataTemplate DataType="{x:Type local:NavigationLine}">
                <Line X1="0" Y1="0" X2="{Binding Width}" Y2="{Binding Height}" Stroke="CornflowerBlue" StrokeThickness="10" StrokeDashArray="3 1" />
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemsPanel>
            <!-- Replace the default panel with a Canvas -->
            <ItemsPanelTemplate>
                <Canvas>
                    <Canvas.Background>
                        <ImageBrush ImageSource="https://images-na.ssl-images-amazon.com/images/I/A1%2Bp%2BB8wq2L._SL1500_.jpg" Stretch="Uniform" />
                    </Canvas.Background>
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <!-- Position each item on the canvas and set the ZIndex so that waypoints appear on top -->
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Canvas.Left" Value="{Binding X}" />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
                <Setter Property="Panel.ZIndex" Value="{Binding Layer}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Viewbox>
<map:Map Center="50,0" ZoomLevel="2">
    <map:Map.MapLayer>
        <local:ChartImageLayer />
    </map:Map.MapLayer>
</map:Map>
using MapControl;
...

public class Waypoint
{
    public string Label { get; set; }
    public Location Location { get; set; }
}

public class ViewModel
{
    public ObservableCollection<Waypoint> Waypoints { get; }
        = new ObservableCollection<Waypoint>();
}
<map:Map Center="50,0" ZoomLevel="2">
    ...
    <map:MapItemsControl ItemsSource="{Binding Waypoints}">
        <map:MapItemsControl.ItemContainerStyle>
            <Style TargetType="map:MapItem">
                <Setter Property="map:MapPanel.Location" Value="{Binding Location}"/>
            </Style>
        </map:MapItemsControl.ItemContainerStyle>
        <map:MapItemsControl.ItemTemplate>
            <DataTemplate>
                <Canvas>
                    <Path Fill="Red">
                        <Path.Data>
                            <EllipseGeometry RadiusX="5" RadiusY="5"/>
                        </Path.Data>
                    </Path>
                    <TextBlock Margin="5,-5" Text="{Binding Label}"/>
                </Canvas>
            </DataTemplate>
        </map:MapItemsControl.ItemTemplate>
    </map:MapItemsControl>
</map:Map>

您应该始终创建MainViewModel并执行适当的MVVM,但以下示例代码将帮助您开始:

public partial class MainWindow : Window
{
    public List<object> ChartElements { get; } = new List<object>
    {
        new Waypoint{X=100, Y=100 },
        new Waypoint{X=500, Y=300 },
        new Waypoint{X=300, Y=500 },
        new Waypoint{X=800, Y=700 },

        new NavigationLine{X1=100, Y1=100, X2=500, Y2=300},
        new NavigationLine{X1=500, Y1=300, X2=300, Y2=500},
        new NavigationLine{X1=300, Y1=500, X2=800, Y2=700}
    };

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }
}

public class Waypoint
{
    public int Layer { get; } = 1; // waypoint circles should always appear on top
    public double X { get; set; }
    public double Y { get; set; }
}

public class NavigationLine
{
    public int Layer { get; } = 0;
    public double X1 { get; set; }
    public double Y1 { get; set; }
    public double X2 { get; set; }
    public double Y2 { get; set; }

    public double X => this.X1;
    public double Y => this.Y1;
    public double Width => this.X2 - this.X1;
    public double Height => this.Y2 - this.Y1;
}
公共部分类主窗口:窗口
{
公共列表ChartElements{get;}=新列表
{
新航路点{X=100,Y=100},
新航路点{X=500,Y=300},
新航路点{X=300,Y=500},
新航路点{X=800,Y=700},
新导航线{X1=100,Y1=100,X2=500,Y2=300},
新导航线{X1=500,Y1=300,X2=300,Y2=500},
新导航线{X1=300,Y1=5