0

I created a UserControl. It contains several elements of specific style and have a DockPanel to contain children which added later on MainWindow.xaml. It works properly and can have it's children on MainWindow.xaml. But when you set Name property on the any of children, It produces 'the name is aleady used' exception on compiling. What do I have to think more about? Without setting Name property, it works properly.

Exception: Cannot set Name attribute value 'ThisProduceAnError' on element 'TextBlock'. 'TextBlock' is under the scope of element 'PropertyPanel', which already had a name registered when it was defined in another scope.

UserControl.xaml

<UserControl x:Class="TrainingWpf1.PropertyPanel"
    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:TrainingWpf1"
    mc:Ignorable="d"
    d:DesignHeight="120" d:DesignWidth="260">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" Text="Title"/>
        <Border Padding="10" BorderThickness="1" CornerRadius="6" Background="CadetBlue">
            <DockPanel x:Name="panContent" />
        </Border>
    </DockPanel>
</UserControl>

UserControl.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace TrainingWpf1
{
        [ContentProperty(nameof(Children))]
        public partial class PropertyPanel : UserControl
        {
                public PropertyPanel()
                {
                        InitializeComponent();
                        this.Children = panContent.Children;
                }

        public UIElementCollection Children
        {
                get => (UIElementCollection)GetValue(ChildrenProperty.DependencyProperty);
                private set => SetValue(ChildrenProperty, value);
        }

        public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
                nameof(Children),
                typeof(UIElementCollection),
                typeof(PropertyPanel),
                new PropertyMetadata());
        }
}

MainWindow.xaml

<Window x:Class="TrainingWpf1.MainWindow"
        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:TrainingWpf1"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="400">
        <Grid>
                <local:PropertyPanel>
                        <TextBlock Name="ThisProduceAnError" Text="Hello"/>
                </local:PropertyPanel>
        </Grid>
</Window>
Wisebee
  • 390
  • 2
  • 10

2 Answers2

0

Do not use XAML:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;

namespace TrainingWpf1
{
    [ContentProperty(nameof(Children))]
    public partial class PropertyPanel : UserControl
    {

        public PropertyPanel()
        {
            //InitializeComponent();

            DockPanel panContent = new DockPanel();
            this.Children = panContent.Children;
            Border border = new Border()
            {
                Padding = new Thickness(10),
                BorderThickness = new Thickness(1),
                CornerRadius = new CornerRadius(6),
                Background = Brushes.CadetBlue,
                Child = panContent
            };

            DockPanel dockPanel = new DockPanel();
            TextBlock textBlock = new TextBlock() { Text = "Title" };
            textBlock.SetValue(DockPanel.DockProperty, Dock.Top);
            dockPanel.Children.Add(textBlock);
            dockPanel.Children.Add(border);
            Content = dockPanel;
        }

        public UIElementCollection Children
        {
            get => (UIElementCollection)GetValue(ChildrenProperty.DependencyProperty);
            private set => SetValue(ChildrenProperty, value);
        }

        public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
                nameof(Children),
                typeof(UIElementCollection),
                typeof(PropertyPanel),
                new PropertyMetadata());
    }

}
EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • Ok. It works now with your solution. Thanks. Would you explane why the xaml way causes the problem? If you create a complex UserControl having hundreds elements and many depths of layers, it will be too hard to handle those complexes without a xaml. Isn't there a way to use xaml? – Wisebee Jun 28 '20 at 02:31
  • Using XAML creates its own namespace. But particular element cannot belong to two namespaces. For complex elements - take an example from default elements. Please note that there is no padding in XAML. XAML is used to create the template. Templates can be very complex. Template specificity - their namespace does not conflict with the namespace where the template is applied. – EldHasp Jun 28 '20 at 02:37
  • Pardon me. So, you can use XAML to create a UseControl by using Style Templetes? – Wisebee Jun 28 '20 at 03:36
  • I wrote the previous message incorrectly. I tried several options, including a template. If XAML is used, an error occurs in any case. How to solve this - I could not find. – EldHasp Jun 28 '20 at 08:32
  • Right. Thank you for all your sharings. – Wisebee Jun 28 '20 at 10:53
-1

I made the mistake I wrote about above. I installed the template in XAML created by Studio. When you add a user control, it creates code from two files: XAML and a partial class in Code-Benind.

For default elements, the implementation is different. A separate class in C # with properties and a separate XAML theme with a template for this class. See how custom elements are implemented.

Having done this, I got a working code. To simplify the example, I did not set a theme, but created a default style in the window resources.

C# for UserControl

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;

namespace TrainingWpf1
{
    [ContentProperty(nameof(Children))]
    public class PropertyPanel : UserControl
    {
        public PropertyPanel()
        {
            Children = new ObservableCollection<UIElement>();
        }

        public ObservableCollection<UIElement> Children
        {
            get => (ObservableCollection<UIElement>)GetValue(ChildrenProperty.DependencyProperty);
            private set => SetValue(ChildrenProperty, value);
        }

        public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
                nameof(Children),
                typeof(ObservableCollection<UIElement>),
                typeof(PropertyPanel),
                new PropertyMetadata(null));
    }

}

XAML for Window

<Window x:Class="TrainingWpf1.MainWindow"
        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:TrainingWpf1"
        mc:Ignorable="d"
        Title="MainWindow" Height="400" Width="400"
        >
    <Window.Resources>
        <Style TargetType="{x:Type local:PropertyPanel}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel>
                            <TextBlock DockPanel.Dock="Top" Text="Title"/>
                            <Border Padding="10" BorderThickness="1" CornerRadius="6" Background="CadetBlue">
                                <ItemsControl ItemsSource="{Binding Children, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:PropertyPanel}}}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <DockPanel/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                </ItemsControl>
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <local:PropertyPanel>
            <TextBlock x:Name="ThisProduceAnError" Text="Hello"/>
        </local:PropertyPanel>
    </Grid>
</Window>
EldHasp
  • 6,079
  • 2
  • 9
  • 24