来自Java,我真的习惯于制作GUI组件时常用的做法:我通常会做一些基类,它包含GUI组件的所有常用对象,然后我扩展它。
所以,基本上,这就是我想用C#和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>
然后我们有一个扩展第一个
的类<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>
从2个XAML代码开始,我想要做的是将DerivedClass
放入BaseClass
容器中。
这将允许我在各种派生类之间共享组件,而不必在每次需要时编写代码。
例如,如果我希望我的所有组件都有这个圆形边框,我想把它放在bass类中,然后将它放在所有派生类中,而不必重写它。
当然,每个c#类都有自己的InitializeComponent()
方法,这可能意味着派生组件将通过删除基类'one来构建自己的内容。
从DerivedClass
构造函数中删除方法即使在派生类中也提供了基本内容,但是,当然,我丢失了我在DerivedClass
的XAML设计窗口中所做的一切。
从DerivedClass
调用基础构造函数无效,因为它在派生的InitializeComponent()
之前调用。
所以问题是:如何在不破坏派生类的XAML设计的情况下,将基类的XAML设计用于派生类?有没有办法简单地将内容添加到基类,同时仍然使用设计器本身?
(我知道我可以删除派生类的XAML并按代码执行我想做的事情,但我想知道我是否可以只使用设计器来执行此操作,因为我不想编写GUI当我有设计师时可用)
编辑:
根据HighCore的回复,我做了一些可以在Windows Phone上运行但我不确定我做的是正确的事情(是的,它有效,但也许是错的!)。
这就是我的所作所为:
BaseControl.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
namespace TestInheritance
{
public partial class BaseControl : UserControl
{
public Grid PresenterContent { get; set; }
public BaseControl()
{
DataContext = this;
InitializeComponent();
}
}
}
DerivedControl.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>
请注意,DerivedClass
是BaseClass
的一个实例,因为我需要它们出于其他原因而拥有一些常见的属性/方法。
您如何看待我的解决方案?它有意义吗?
答案 0 :(得分:63)
好的,让我把它分成几部分:
来自Java
忘了java。这是一种非常陈旧的语言,自90年代以来就没有发展过。 C#是一百万倍,WPF是迄今为止最好的UI框架。
从我所看到的,诸如swing之类的java UI框架在概念上类似于.Net的winforms,它也被WPF取代。WPF(以及它基于XAML的兄弟)与其他任何框架都有着根本的不同,因为它们通过Styles and Templates增强了自定义功能并支持DataBinding。
因此,在WPF上启动时需要Significant Mindshift。
我通常会做一些包含所有常见内容的基类 我的GUI组件的对象,然后我扩展它。
在WPF中,有Content Model,它通过引入将“任何内容放入任何内容”的功能,消除了对继承和其他膨胀的不必要做法的需要。
例如,可以像这样定义Button
:
<Button>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
<TextBlock Text="Click Me"/>
</StackPanel>
<Button.Content>
</Button>
导致
没有必要继承Button只是为了定义它的内容。
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 //...
{
//...
}
这意味着以下XAML在功能上与上述相同:
<Button>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
<TextBlock Text="Click Me"/>
</StackPanel>
</Button>
<Button.Content>
代码,因为ContentProperty
属性可以解决此问题。所有这一切都归功于一个名为ControlTemplates的功能,该功能定义了控件的视觉外观,与其行为无关。
我想要做的是将DerivedClass放入BaseClass 容器
有几种方法可以实现这一点,其中一种方法是利用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>
然后您可以重复使用此模板:
<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>
导致:
无需继承或任何程序代码。
在WPF中启动的另一个非常重要的方面,我在上面的“重要的Mindshift”链接中进行了详细解释,这是我在这里告诉大家的:
在WPF编写单行代码之前学习MVVM
大多数情况下不在WPF UI元素中添加任何代码,因为大多数事情都可以通过DataBinding
实现(在上面的“DataBinding”链接中介绍) ),或通过实施可重复使用 Attached Behaviors或Attached Properties。只有特定于视图的代码应放在代码后面,does not deal with Data or Business Logic
您可能习惯在其他框架中使用的样板文件,例如:
txtLastName.Text = person.LastName;
txtFirstName.Text = person.FirstName;
btnSubmit.IsEnabled = person.IsActive;
以及类似的东西,在WPF中完全不需要,因为DataBinding。
另一个在UI中显示数据时具有高度灵活性的概念是WPF的DataTemplates
,它允许您定义在屏幕上“呈现”某些数据类型时要使用的特定UI。 p>
由于上述所有原因,WPF与大多数UI框架根本不同,因此不需要在其他框架中常见的所有可怕的样板和黑客,
我建议您阅读所提供的所有链接,并在定义应用程序的结构和UI时牢记所有这些概念和实践。
如果您需要进一步的帮助,请与我们联系。
答案 1 :(得分:0)
据我所知,按原样导出UI是不可用的,除非资源XAML(样式为ex)。 也许原因是UI管理器无法猜测放置扩展XAML代码的位置: 想象一下基本UI如下:
<UserControl >
<Grid>
<Border/>
</Grid>
</UserControl>
和派生的UI:
<UserControl>
<DockPanel>
<Button/>
</DockPanel>
</UserControl>
混合后的结果是什么?
<UserControl >
<Grid>
<Border/>
</Grid>
<DockPanel>
<Button/>
</DockPanel>
</UserControl>
这是不可能的。
如何控制嵌入/兼容性问题?