草庐IT

c# - XAML 用户控件继承

coder 2023-07-11 原文

来自 Java,当涉及到制作 GUI 组件时,我真的习惯了一种常见的做法:我通常会做一些基类,其中包含我的 GUI 组件的所有公共(public)对象,然后我扩展它。

所以,基本上,这就是我想使用 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() 方法,这可能意味着派生组件将通过删除基类的方法来构建自己的内容。

DerivedClass 构造函数中删除方法即使在派生类中也能为我提供基本内容,但是,当然,我会丢失在 DerivedClass< 的="" xaml="">.

DerivedClass 调用基础构造函数没有任何效果,因为它是在派生的 InitializeComponent() 之前调用的。

所以问题是:如何在不破坏派生类的 XAML 设计的情况下将基类的 XAML 设计用于派生类?有没有什么方法可以简单地向基类添加内容,同时仍然与设计器本身一起工作?

(我知道我可以删除派生类的 XAML 并通过代码执行我想执行的操作,但我想知道我是否可以只与设计器一起执行此操作,因为我不想编写我的 GUI当我有可用的设计师时)

编辑:

在 HighCore 的回复之后,我做了一些在 Windows Phone 上工作的东西,但我不确定我做的是正确的事情(是的,它工作,但也许是错误的!)。

这是我做的:

基础控制.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>

请注意 DerivedClassBaseClass 的一个实例,因为出于其他原因我需要它们具有一些通用的属性/方法。

您如何看待我的解决方案?有道理吗?

最佳答案

好的,让我把它分成几部分:

Coming from Java

忘掉java吧。这是一种真正过时的语言,自 90 年代以来一直没有发展。 C# 好一百万倍,WPF 是迄今为止最好的 UI 框架。

据我所见,swing等java UI框架在概念上类似于.Net的winforms,也被WPF取代了。

WPF(及其基于 XAML 的兄弟)与周围的任何其他框架有着根本的不同,因为它们通过 Styles and Templates 增强了自定义功能并支持 DataBinding

因此,在 WPF 上启动时需要一个 Significant Mindshift


I usually do some sort of base class which contains all the common objects for my GUI components and then I extend it.

在 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 的功能,它定义了控件的视觉外观,独立于它的行为。


what I'd like to do is to have the DerivedClass into the BaseClass container.

有几种方法可以实现这一点,其中之一是利用 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 时的另一个非常重要的方面,在上面的“重要的思维转变”链接中有广泛的解释,我在这里告诉大家:

在 WPF 中编写一行代码之前先学习 MVVM

  • 大多数时候您不要在 WPF UI 元素中放置任何代码,因为大多数事情都可以通过 DataBinding(在上面的“DataBinding”链接中介绍)或通过实现 Reusable Attached BehaviorsAttached Properties 。只有VIEW-Specific代码应该放在代码后面,which does not deal with Data or Business Logic

  • 您可能在其他框架中习惯使用的样板,例如:

    txtLastName.Text = person.LastName;
    txtFirstName.Text = person.FirstName;
    btnSubmit.IsEnabled = person.IsActive;
    

    和类似的东西在 WPF 中是完全不需要的,同样是因为数据绑定(bind)。


在 UI 中显示数据时实现高度灵 active 的另一个概念是 WPF 的 DataTemplates ,它允许您定义在屏幕上“呈现”某些数据类型时要使用的特定 UI。


由于上述所有原因,WPF 从根本上不同于大多数 UI 框架,因此消除了对其他框架中常见的所有可怕样板和 hack 的需要,

我建议您通读所有提供的链接,并在定义应用程序的结构和一般 UI 时牢记所有这些概念和实践。

如果您需要进一步的帮助,请告诉我。

关于c# - XAML 用户控件继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18546447/

有关c# - XAML 用户控件继承的更多相关文章

  1. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  2. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  3. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  4. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  5. ruby - RVM "ERROR: Unable to checkout branch ."单用户 - 2

    我在新的Debian6VirtualBoxVM上安装RVM时遇到问题。我已经安装了所有需要的包并使用下载了安装脚本(curl-shttps://rvm.beginrescueend.com/install/rvm)>rvm,但以单个用户身份运行时bashrvm我收到以下错误消息:ERROR:Unabletocheckoutbranch.安装在这里停止,并且(据我所知)没有安装RVM的任何文件。如果我以root身份运行脚本(对于多用户安装),我会收到另一条消息:Successfullycheckedoutbranch''安装程序继续并指示成功,但未添加.rvm目录,甚至在修改我的.bas

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. ruby-on-rails - Rails 单表继承 : How to override the value written to the type field - 2

    在我的系统中,我已经定义了STI。Dog继承自Animal,在animals表中有一个type列,其值为"Dog"。现在我想让SpecialDog继承自dog,只是为了在某些特殊情况下稍微修改一下行为。数据还是一样。我需要通过SpecialDog运行的所有查询,以返回数据库中类型为Dog的值。我的问题是因为我有一个type列,rails将WHERE"animals"."type"IN('SpecialDog')附加到我的查询中,所以我不能获取原始的Dog条目。所以我想要的是以某种方式覆盖rails在通过SpecialDog访问数据库时使用的值,使其表现得像Dog。有没有办法覆盖用于类型

  8. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  9. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  10. ruby - HTTP 请求中的用户代理,Ruby - 2

    我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)

随机推荐