C# XAML 用户控件继承
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18546447/
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
XAML UserControl inheritance
提问by StepTNT
Coming from Java, I'm really used to a common practice when it comes to make GUI components: I usually do some sort of base class which contains all the common objects for my GUI components and then I extend it.
来自 Java,当涉及到制作 GUI 组件时,我真的习惯了一个常见的做法:我通常会做某种基类,其中包含我的 GUI 组件的所有公共对象,然后我扩展它。
So, basically, this is what I'd like to achieve with C# and XAML.
所以,基本上,这就是我想用 C# 和 XAML 实现的目标。
To make the question clear, here's an example (that is not working!) of what I'm doing:
为了让问题更清楚,这是我正在做的一个例子(那是行不通的!):
We've got a base class with its own XAML
我们有一个带有自己的 XAML 的基类
<UserControl x:Class="BaseClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Border BorderBrush="Aqua" BorderThickness="10" CornerRadius="10" x:Name="Border" HorizontalAlignment="Left" Height="480" VerticalAlignment="Top" Width="480"/>
</Grid>
</UserControl>
and then we've got a class which extends the first one
然后我们有一个扩展第一个的类
<base:BaseClass x:Class="DerivedClass"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:base="clr-namespace:BaseClass"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="60" d:DesignWidth="200">
<Grid x:Name="LayoutRoot" Margin="0" Width="200" Height="60" MaxWidth="200" MaxHeight="60" Background="{StaticResource PhoneAccentBrush}">
<TextBlock x:Name="dummyText" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Dummy Plugin" VerticalAlignment="Top" Height="40" Width="180" Foreground="White" TextAlignment="Center"/>
</Grid>
</base:BaseClass>
Starting from the 2 XAML codes, what I'd like to do is to have the DerivedClass
into the BaseClass
container.
This will allow me to share components between the various derived classes without having to write the code everytime that I need it.
从 2 个 XAML 代码开始,我想做的是将其DerivedClass
放入BaseClass
容器中。这将允许我在各种派生类之间共享组件,而不必每次需要时都编写代码。
For example, if I want all my components to have that rounded border, I'd like to just put it in the bass class and then have it in all the derived ones without having to rewrite it.
例如,如果我希望我的所有组件都具有圆形边框,我只想将它放在 bass 类中,然后将它放在所有派生类中,而不必重写它。
Of course, each c# class has its own InitializeComponent()
method and this probably means that the derived component will build its own content by removing the base class' one.
当然,每个 c# 类都有自己的InitializeComponent()
方法,这可能意味着派生组件将通过删除基类的方法来构建自己的内容。
Removing the method from the DerivedClass
constructor gives me the base content even in the derived class, but, of course, I lose everything I made in the XAML design window of the DerivedClass
.
从DerivedClass
构造函数中删除该方法即使在派生类中也为我提供了基本内容,但是,当然,我丢失了我在DerivedClass
.
Calling the base constructor from the DerivedClass
has no effect, as it's called before the derived InitializeComponent()
.
从 调用基构造函数DerivedClass
没有任何效果,因为它在派生之前调用InitializeComponent()
。
So the question is: how can I use the XAML design from a base class into the derived one without breaking the XAML design of the derived class? Is there any way to simply add content to the base class while still working with the designer itself?
所以问题是:如何在不破坏派生类的 XAML 设计的情况下,将基类的 XAML 设计用于派生类?有什么方法可以在仍然与设计器本身一起工作的同时简单地向基类添加内容?
(I know that I can remove the XAML for the derived class and do what I want to do by code, but I want to know if I can do this just with the designer as I don't want to write my GUI when I have a designer available)
(我知道我可以删除派生类的 XAML 并通过代码执行我想做的事情,但是我想知道我是否可以仅与设计器一起执行此操作,因为我不想在有 GUI 时编写 GUI有设计师可用)
EDIT:
编辑:
Following HighCore's reply, I did something that works on Windows Phone but I'm not sure that I'm doing the right thing (yeah, it works, but maybe is just wrong!).
按照 HighCore 的回复,我做了一些适用于 Windows Phone 的事情,但我不确定我是否在做正确的事情(是的,它有效,但也许只是错误的!)。
Here's what I did:
这是我所做的:
BaseControl.xaml
基础控件.xaml
<UserControl x:Class="TestInheritance.BaseControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<TextBlock HorizontalAlignment="Center">BASE</TextBlock>
<ContentPresenter Name="Presenter" Content="{Binding PresenterContent}"/>
</Grid>
</UserControl>
BaseControl.xaml.cs
基础控件.xaml.cs
namespace TestInheritance
{
public partial class BaseControl : UserControl
{
public Grid PresenterContent { get; set; }
public BaseControl()
{
DataContext = this;
InitializeComponent();
}
}
}
DerivedControl.xaml
派生控件.xaml
<local:BaseControl x:Class="TestInheritance.DerivedControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestInheritance"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480">
<local:BaseControl.PresenterContent>
<Grid>
<TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Center">DERIVED</TextBlock>
</Grid>
</local:BaseControl.PresenterContent>
</local:BaseControl>
Please note that the DerivedClass
is an instance of BaseClass
as I need them to have some common properties/methods for other reasons.
请注意,DerivedClass
是一个实例BaseClass
,因为我需要他们有其他原因一些常见的属性/方法。
What do you think about my solution? Does it make sense?
你觉得我的解决方案怎么样?是否有意义?
采纳答案by Federico Berasategui
Ok, let me split this into parts:
好的,让我把它分成几部分:
Coming from Java
来自爪哇
Forget java. It's a really antiquated language which has not evolved since the 90's. C# is a million times better and WPF is the best UI framework up to date.
忘记爪哇。这是一种非常过时的语言,自 90 年代以来就没有发展过。C# 好一百万倍,WPF 是迄今为止最好的 UI 框架。
from what I've seen, java UI frameworks such as swing are conceptually similar to .Net's winforms, which has also been replaced by WPF.
就我所见,swing 等java UI 框架在概念上与.Net 的winforms 类似,后者也已被WPF 取代。
WPF (and it's XAML-based brethren) are fundamentally different from any other frameworks around because of their enhanced capability for customization via Styles and Templatesand support for DataBinding.
WPF(及其基于 XAML 的兄弟)与周围的任何其他框架有着根本的不同,因为它们增强了通过样式和模板进行自定义的能力以及对DataBinding 的支持。
Because of this, a Significant Mindshiftis required when starting on WPF.
因此,开始使用 WPF 时需要进行重大的思维转变。
I usually do some sort of base class which contains all the common objects for my GUI components and then I extend it.
我通常做一些基类,它包含我的 GUI 组件的所有公共对象,然后我扩展它。
In WPF, there's the Content Model, which removes the need for inheritance and other bloated unnecesary practices, by introducing the capability to put "anything inside anything".
在 WPF 中,有Content Model,它通过引入将“任何东西放入任何东西”的功能,消除了对继承和其他臃肿的不必要实践的需要。
For example, a Button
can be defined like this:
例如,aButton
可以这样定义:
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
<TextBlock Text="Click Me"/>
</StackPanel>
<Button.Content>
</Button>
which results in
这导致
There's no need to inherit from Button just to define it's content.
不需要从 Button 继承来定义它的内容。
There is an additional advantage WPF provides and is really handy, the ContentProperty
Attribute which defines what the content of the XAML tags <Button> </Button>
represents. Button
is derived from ContentControl
, which is declared like this:
WPF 提供了一个额外的优势,它非常方便,ContentProperty
它定义了 XAML 标记的内容所<Button> </Button>
代表的内容。Button
派生自ContentControl
,声明如下:
//Declaration of the System.Windows.Control.ContentControl class,
//inside the PresentationFramework.dll assembly
//...
[ContentProperty("Content")]
public class ContentControl: Control //...
{
//...
}
This means that the following XAML is functionally identical to the above:
这意味着以下 XAML 在功能上与上述相同:
<Button>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
<TextBlock Text="Click Me"/>
</StackPanel>
</Button>
- Notice we have removed the
<Button.Content>
tag, because theContentProperty
attribute takes care of that.
- 请注意,我们已经删除了
<Button.Content>
标签,因为该ContentProperty
属性负责处理。
All this is made possible thanks to a feature called ControlTemplates, which define the Visual appearance of a Control, independently of it's behavior.
所有这一切都归功于一项名为ControlTemplates的功能,该功能定义了控件的视觉外观,与其行为无关。
what I'd like to do is to have the DerivedClass into the BaseClass container.
我想做的是将 DerivedClass 放入 BaseClass 容器中。
There are several ways to achieve that, one of them is to leverage ControlTemplates
and define a specific container inside the XAML that will host the content:
有几种方法可以实现这一点,其中一种方法是ControlTemplates
在 XAML 中利用和定义一个特定的容器来承载内容:
<UserControl x:Class="BaseClass">
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Text="I'm the Container"/>
<!-- This is where the Hosted Content will be placed -->
<ContentPresenter ContentSource="Content"/>
</DockPanel>
</ControlTemplate>
</UserControl.Template>
</UserControl>
Then you could reuse this template like this:
然后你可以像这样重用这个模板:
<Window>
<my:BaseClass>
<Border Background="Gray" BorderBrush="Blue" BorderThickness="2"
VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Im the Hosted Content" Foreground="AliceBlue"/>
</Border>
</my:BaseClass>
</Window>
which results in:
这导致:
No need for inheritance or any procedural code stuff.
不需要继承或任何程序代码的东西。
Another very important aspect when starting in WPF, which is extensively explained in the "Significant Mindshift" link above, is what I tell everyone here:
开始使用 WPF 时的另一个非常重要的方面,在上面的“重要的思维转变”链接中进行了广泛的解释,是我在这里告诉大家的:
Learn MVVM before you ever write a single line of code in WPF
在用 WPF 编写一行代码之前先学习 MVVM
Most of the time you don't put any code in WPF UI elements, because most things can be achieved by
DataBinding
(covered in the "DataBinding" link above), or by implementing ReusableAttached Behaviorsor Attached Properties. Only VIEW-Specificcode should be placed in code behind, which does not deal with Data or Business LogicThe boilerplate you might be used to in other frameworks, such as:
txtLastName.Text = person.LastName; txtFirstName.Text = person.FirstName; btnSubmit.IsEnabled = person.IsActive;
and stuff like that, is completely unneeded in WPF, again, because of DataBinding.
大多数情况下,您不会在 WPF UI 元素中放置任何代码,因为大多数事情都可以通过
DataBinding
(在上面的“DataBinding”链接中介绍)或通过实现Reusable Attached Behaviors或Attached Properties 来实现。只有VIEW-Specific的代码应该放在后面的代码中,不处理数据或业务逻辑您可能在其他框架中使用过的样板,例如:
txtLastName.Text = person.LastName; txtFirstName.Text = person.FirstName; btnSubmit.IsEnabled = person.IsActive;
和类似的东西,在 WPF 中完全不需要,再次,因为数据绑定。
Another concept which enables high flexibility when it comes to showing data in the UI is WPF's DataTemplates
, which allow you to define a specific UI to be used when some Data Type is "rendered" on screen.
另一个在 UI 中显示数据时具有高度灵活性的概念是 WPF 的DataTemplates
,它允许您定义在屏幕上“呈现”某些数据类型时要使用的特定 UI。
Because of all of the above, WPF is fundamentally different from most UI frameworks out there, and thus removes the need for all the horrible boilerplate and hacks which are common in other frameworks,
由于上述所有原因,WPF 与大多数 UI 框架有着根本的不同,因此不需要其他框架中常见的所有可怕的样板和黑客,
I suggest you read up on all the links provided and keep in mind all these concepts and practices when defining an application's structure and UI in general.
我建议您阅读提供的所有链接,并在定义应用程序的结构和 UI 时记住所有这些概念和实践。
Let me know if you need further assistance.
如果您需要进一步的帮助,请告诉我。
回答by HichemSeeSharp
As far as I know, deriving UI as is, is unavailable unless for resources XAML (styles for ex). Maybe the reason is the UI manager cannot guess where to put the extended XAML code: Imagine the base UI as follows:
据我所知,除非资源 XAML(例如样式),否则无法按原样派生 UI。可能原因是 UI 管理器无法猜测将扩展的 XAML 代码放在哪里:想象一下基本 UI 如下:
<UserControl >
<Grid>
<Border/>
</Grid>
</UserControl>
and the derived UI :
和派生的用户界面:
<UserControl>
<DockPanel>
<Button/>
</DockPanel>
</UserControl>
What would be the result after the mix?
混合后的结果是什么?
a straight forward answer could be:
一个直接的答案可能是:
<UserControl >
<Grid>
<Border/>
</Grid>
<DockPanel>
<Button/>
</DockPanel>
</UserControl>
which is not possible.
这是不可能的。
What about controls embedding/compatibility issues ?
控件嵌入/兼容性问题如何?