自定义 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
Custom WPF/XAML Canvas
提问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.Left,Canvas.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.
内容的问题是你设置了两次,就像错误信息说的那样。
- In
MyCanvas.xamlyou set it to anImage. - When using it, you set it to your user controls.
- 在
MyCanvas.xaml您将其设置为Image. - 使用它时,您将其设置为您的用户控件。
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 功能。

