如何在我的项目中重用XAML命名空间定义

3

我重新修订了这个问题,以更详细地展示我所说的重用xaml定义的含义。

<UserControl x:Class="XamlDemo.ControlA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XamlDemo"
             xmlns:foo="clr-namespace:XamlDemo.Foo"
             xmlns:bar="clr-namespace:XamlDemo.Bar"
             mc:Ignorable="d">
<!--
    xmlns:foo="clr-namespace:XamlDemo.Foo"
    xmlns:bar="clr-namespace:XamlDemo.Bar"
    foo and bar will tend to repeat in exactly this constellation all over the project.
    If one of these namespaces changes all xaml files need to be edited.
    I would like to include a different file as a component where i would only write foo and bar once
-->
    <StackPanel>
        <foo:ExtTextBlock></foo:ExtTextBlock>
        <bar:ExtLabel></bar:ExtLabel>
    </StackPanel>
</UserControl>


<UserControl x:Class="XamlDemo.ControlB"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:XamlDemo"
             xmlns:foo="clr-namespace:XamlDemo.Foo"
             xmlns:bar="clr-namespace:XamlDemo.Bar"
             mc:Ignorable="d" >
    <StackPanel>
        <foo:ExtTextBox></foo:ExtTextBox>
        <bar:ExtButton></bar:ExtButton>
    </StackPanel>
</UserControl>

使用

using System.Windows.Controls;

namespace XamlDemo.Bar
{
    public class ExtButton : Button { }
    public class ExtLabel : Label { }
}
namespace XamlDemo.Foo
{
    public class ExtTextBlock : TextBlock { }
    public class ExtTextBox : TextBox { }
}

两个都使用了我的本地命名空间声明。我希望它们能够包含对不同xaml的引用,并从那里获取命名空间。

我没有找到任何方法来实现这一点 - 这里有一些概念代码,说明了我想象中的样子。显然,这不会编译。

<magic
             xmlns:foo="clr-namespace:XamlDemo.Foo"
             xmlns:bar="clr-namespace:XamlDemo.Bar">
</magic>

<UserControl x:Class="..."
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns="get it from magic">
</UserControl>

XAML 命名空间有什么特殊之处吗?如果它遵循正常的 XML 规则(我认为应该是这样),您应该能够将这些命名空间声明放置在任何包含这些用户控件的元素上(例如,找到两者的最近公共祖先)。然后就不需要以后显式引用它们了。 - Damien_The_Unbeliever
2个回答

1

正如Freeman所述,XAML继承是不可能的。但是你可以考虑使用XmlnsDefinitionAttribute来减少和清理命名空间定义。

在CodeProject上可以找到一篇有趣的文章here

实际上,如果您想要包含在XAML中的命名空间在引用的程序集中,您可以轻松地将它们映射到单个URI中。只需按照以下方式在引用的程序集中添加XmlnsDefinition属性:

[assembly: XmlnsDefinition("urn:johannes-ui-controls", "XamlDemo.Foo")]
[assembly: XmlnsDefinition("urn:johannes-ui-controls", "XamlDemo.Bar")]

Then in your XAML you can use them in this way:
然后在你的XAML中可以这样使用它们:
<UserControl x:Class="..."
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:uiControls="urn:johannes-ui-controls">
    <StackPanel>
        <uiControls:ExtTextBox />
        <uiControls:ExtButton />
    </StackPanel>
</UserControl>

这个解决方案的限制是,您不能在包含XAML的程序集中使用XmlnsDefinition属性。 也许这并不完全符合您的意思,但也许可以帮助您。

0

基础控件:

namespace WpfApplication9
{
    public class BaseControl : UserControl
    {
        public BaseControl()
        {

        }
    public override void EndInit()
    {
        base.EndInit();
        ExtTextBlock block = new ExtTextBlock { Width = 100 , Height = 20 , Text = "Test Block" };
        ExtButton button = new ExtButton { Width = 100, Height = 20 , Content = "ClickMe"};
        ExtLabel label = new ExtLabel { Width = 100, Height = 30 ,Content = "Test Label"};
        ExtTextBox txtBox = new ExtTextBox { Width = 100, Height = 20 ,Text= "Hi There"};
        Grid g = (Grid)BaseControl.FindChild(this, "gridMain");
        g.Children.Add(button);
        g.Children.Add(block);
        g.Children.Add(label);
        g.Children.Add(txtBox);
        Grid.SetRow(block, 0);
        Grid.SetRow(button, 1);
        Grid.SetRow(label, 2);
        Grid.SetRow(txtBox, 3);

        button.Click += button_Click;
    }

    void button_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Hi There");
    }

    public static DependencyObject FindChild(DependencyObject parent, string name)
    {
        // confirm parent and name are valid.
        if (parent == null || string.IsNullOrEmpty(name)) return null;

        if (parent is FrameworkElement && (parent as FrameworkElement).Name == name) return parent;

        DependencyObject result = null;

        if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            result = FindChild(child, name);
            if (result != null) break;
        }

        return result;
    }

    /// <summary>
    /// Looks for a child control within a parent by type
    /// </summary>
    public static T FindChild<T>(DependencyObject parent)
        where T : DependencyObject
    {
        // confirm parent is valid.
        if (parent == null) return null;
        if (parent is T) return parent as T;

        DependencyObject foundChild = null;

        if (parent is FrameworkElement) (parent as FrameworkElement).ApplyTemplate();

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            foundChild = FindChild<T>(child);
            if (foundChild != null) break;
        }

        return foundChild as T;
    }
}
}

namespace WpfApplication9.Foo
{
    public class ExtTextBlock : TextBlock { }
    public class ExtTextBox : TextBox { }
}

namespace WpfApplication9.Bar
{
    public class ExtButton : Button { }
    public class ExtLabel : Label { }
}

控制1.xaml

<base:BaseControl x:Class="WpfApplication9.Control1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:base="clr-namespace:WpfApplication9"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="gridMain">
    <Grid.RowDefinitions>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>
</Grid>

你可以像创建 Control1 一样创建 Control2 类。 正如我所说,XAML 继承是不可能的。

感谢您的回复,我已经编辑了问题以便更清晰地表达。但我仍然不明白如何让您的回答起作用 - 或许我的问题并不清楚。您能否详细解释一下? - Johannes
啊,我明白了。所以你基本上是在代码后台解决这个问题。虽然不完全是我希望的,但这确实使重命名依赖项变得容易。 - Johannes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接