简体   繁体   中英

How can I reuse xaml namespace definitions in my project

I reworked this question to show in more detail what I mean by reusing xaml definitions.

<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>

with

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 { }
}

Both use my local namespace declarations. I would like them to instead include a reference to a different xaml and get the namespaces from there

I did not find any way of doing this - here is some concept code that illustrates what I imagine this could look like. Obviously this would not compile.

<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>

Base Control :

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 { }
}

Control 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>

You can create Control2 class as you create Control1. As I said xaml inheretence is not possible.

As stated by Freeman , XAML inheritance is not possible. Anyway you can consider using XmlnsDefinitionAttribute for reducing and cleaning the namespace definitions.

You can find an interesting article here on CodeProject.

Pratically if the namespaces you want to include in your XAML are in a refereced assembly , you can easily map them in a single URI. Just add in the referenced assembly the XmlnsDefinition attribute in this way:

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

and so on.

Then in your XAML you can use them in this way:

<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>

The limit of this solution is that you cannot use the XmlnsDefinition attribute with the assembly which contains your XAML. Probably this is not exaclty what you meant, but maybe it can help you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM