自定义 WPF/XAML 画布

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14752084/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 07:24:12  来源:igfitidea点击:

Custom WPF/XAML Canvas

c#wpfxamlcontrols

提问by Tommaso Belluzzo

I'm trying to create and use a custom Canvas. Here is the XAML (MyCanvas.xaml):

我正在尝试创建和使用自定义画布。这是 XAML (MyCanvas.xaml):

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamepace" xmlns:Properties="clr-namespace:MyNamepace.Properties" Core:Class="MyNamepace.MyCanvas">
    <Canvas.Resources>
        <Namespace:ImagesConverter Core:Key="ImagesConverter"/>
    </Canvas.Resources>
    <Image Source="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
</Canvas>

Here is the code declaration (MyCanvas.xaml.cs):

这是代码声明 (MyCanvas.xaml.cs):

public partial class MyCanvas : Canvas

When I try to use it like so:

当我尝试像这样使用它时:

<Namespace:MyCanvas Core:Name="Layout" Loaded="OnLoaded">
    <Namespace:MyUserControl Core:Name="Control1" Namespace:MyCanvas.Left="50" MyProperty="50">
        <Namespace:MyCanvas.Top>
            <MultiBinding Converter="{StaticResource MathConverter}" ConverterParameter="(x - y) / 2">
                <Binding ElementName="Layout" Path="ActualHeight"/>
                <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/>
            </MultiBinding>
        </Namespace:MyCanvas.Top>
    </Namespace:MyUserControl>
    <Namespace:MyUserControl Core:Name="Control2" Namespace:MyCanvas.Left="744" Namespace:MyCanvas.Top="42" MyProperty="150"/>
</Namespace:MyCanvas>

I get two different errors:

我收到两个不同的错误:

The property "Content" can only be set once. ==> Isn't it inheriting Canvas?!?!?!

The member "Top" is not recognized or is not accessible. ==> Isn't it inheriting Canvas again?!?!?! The member "Left" is not recognized or is not accessible. ==> Isn't it inheriting Canvas again?!?!?!

属性“内容”只能设置一次。==> 不是继承了Canvas吗?!?!?!

成员“Top”无法识别或无法访问。==> 不是又继承了Canvas吗?!?!?!成员“Left”无法识别或无法访问。==> 不是又继承了Canvas吗?!?!?!

EDIT: this is what I have so far... still getting the "Content" already set error!

编辑:这是我到目前为止...仍然得到“内容”已经设置的错误!

MyCanvas.xaml

我的画布.xaml

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamespace" xmlns:Properties="clr-namespace:MyNamespace.Properties" Core:Class="MyNamespace.MyCanvas">
    <Canvas.Background>
        <ImageBrush ImageSource="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
    </Canvas.Background>
    <Canvas.Resources>
        <Namespace:ImagesConverter Core:Key="ImagesConverter"/>
    </Canvas.Resources>
</Canvas>

MyCanvas.xaml.cs

我的画布.xaml.cs

public class MyCanvas : Canvas
{
    // ...
}

MainWindow.xaml

主窗口.xaml

<Namespace:MyCanvas Core:Name="MyCanvas" Loaded="OnLoaded">
    <Namespace:MyUserControl ...
    <Namespace:MyUserControl ...
    <Namespace:MyUserControl ...
</Namespace:MyCanvas>

回答by Daniel Hilgarth

Leftand Topare attached properties. That means that they are not inherited by your class.

Left并且Top附加属性。这意味着它们不会被您的类继承。

You need to change the user control declaration to use Canvas.Leftand Canvas.Topinstead:

您需要更改要使用的用户控件声明Canvas.LeftCanvas.Top而是:

<Namespace:MyUserControl Core:Name="Control2" Canvas.Left="744" Canvas.Top="42" 
                         MyProperty="150"/>

The problem with the content is that you set it twice, just like the error message says.

内容的问题是你设置了两次,就像错误信息说的那样。

  1. In MyCanvas.xamlyou set it to an Image.
  2. When using it, you set it to your user controls.
  1. MyCanvas.xaml您将其设置为Image.
  2. 使用它时,您将其设置为您的用户控件。

To fix it, you need to add an ItemsControlto MyCanvasand declare it as the control that represents the content:

要修复它,您需要添加一个ItemsControltoMyCanvas并将其声明为表示内容的控件:

<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:Core="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Namespace="clr-namespace:MyNamepace" xmlns:Properties="clr-namespace:MyNamepace.Properties" Core:Class="MyNamepace.MyCanvas">
    <Canvas.Resources>
        <Namespace:ImagesConverter Core:Key="ImagesConverter"/>
    </Canvas.Resources>
    <Image Source="{Binding Source={Core:Static Properties:Resources.Background}, Converter={StaticResource ImagesConverter}}" Stretch="Fill"/>
    <ItemsControl Content="{Binding Path=LocalContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Namespace:MyCanvas}}}" />
</Canvas>

In your class file:

在您的类文件中:

[ContentProperty("LocalContent")]
public partial class MyCanvas : Canvas
{
    public static readonly DependencyProperty LocalContentProperty =
        DependencyProperty.Register(
            "LocalContent", 
            typeof(UIElementCollection), 
            typeof(MyCanvas ), 
            new PropertyMetadata(default(UIElementCollection)));
}

回答by sircodesalot

Adding to Daniel's answer, you have to understand that WPF introduces a concept called 'Dependency Properties' and 'Dependency Objects'. In short, just as Objects in C# can have properties, Dependency Objects in WPF can have dependency properties. The way this works is a bit different though that the c# method.

添加到 Daniel 的回答中,您必须了解 WPF 引入了一个称为“依赖属性”和“依赖对象”的概念。简而言之,就像 C# 中的对象可以具有属性一样,WPF 中的依赖对象也可以具有依赖属性。尽管它的工作方式与 c# 方法有所不同。

For dependency properties (such as attached properties), WPF manages a seperate table with records indicating which objects have which properties. Which in other words, means it's possible for any object to have any property, WPF just adds a record to the table giving said object said property. So for example, Canvas.Left, while being definedby Canvas, can be implemented by any control. WPF simply inserts a record into the dependency table, and voila now your image has Canvas.Left / Canvas.Top properties. This means a much smaller memory footprint, because object opt intohaving a property (when the record is added), rather than having a property just because it derives from a certain class.

对于依赖属性(例如附加属性),WPF 管理一个单独的表,其中包含指示哪些对象具有哪些属性的记录。换句话说,这意味着任何对象都可能具有任何属性,WPF 只是向表中添加一条记录,为该对象提供所述属性。所以比如Canvas.Left,虽然是由Canvas定义的,但是可以通过任何控件来实现。WPF 只是在依赖表中插入一条记录,瞧,现在您的图像具有 Canvas.Left / Canvas.Top 属性。这意味着内存占用要小得多,因为对象选择拥有一个属性(当添加记录时),而不是仅仅因为它来自某个类而拥有一个属性。

The way this works programatically, is you use DependencyObject.SetValue / GetValue (all WPF objects derive from DependencyObject see: http://miteshsureja.blogspot.com/2011/06/wpf-class-hierarchy.html- in fact, memorize this graph because it will help you better to understand how WPF works under the hood), and this adds / reads from a record to the aforementioned table. When you define a dependency property, you your c# accessors should really redirect to these methods, because WPF (and not the object itself) should be managing these values (this is how WPF is able to do data-binding and what not, because it manages the values and just shifts them to other objects as neccesasry). For an example of creating a dependency property, see: http://msdn.microsoft.com/en-us/library/ms752914.aspx. The concept behind dependency properties is very simple, but you have to at least be aware of it in order to understand a lot of WPFs functionality.

以编程方式工作的方式是您使用 DependencyObject.SetValue / GetValue(所有 WPF 对象都从 DependencyObject 派生,请参阅:http: //miteshsureja.blogspot.com/2011/06/wpf-class-hierarchy.html- 事实上,记住这一点图表,因为它将帮助您更好地了解 WPF 在后台是如何工作的),并且这会将记录添加/读取到上述表中。当您定义依赖项属性时,您的 c# 访问器应该真正重定向到这些方法,因为 WPF(而不是对象本身)应该管理这些值(这就是 WPF 能够进行数据绑定的方式,而不能,因为它管理这些值,并根据需要将它们转移到其他对象)。有关创建依赖项属性的示例,请参见:http: //msdn.microsoft.com/en-us/library/ms752914.aspx. 依赖属性背后的概念非常简单,但您至少必须了解它才能理解许多 WPF 功能。