{"id":162,"date":"2016-01-23T16:20:48","date_gmt":"2016-01-23T14:20:48","guid":{"rendered":"http:\/\/blog.rollnut.com\/?p=162"},"modified":"2021-04-11T09:25:34","modified_gmt":"2021-04-11T07:25:34","slug":"content-unterstuetzung-via-xaml-fuer-usercontrols","status":"publish","type":"post","link":"http:\/\/blog.rollnut.com\/content-unterstuetzung-via-xaml-fuer-usercontrols\/","title":{"rendered":"Content-Unterst\u00fctzung via XAML f\u00fcr UserControls"},"content":{"rendered":"

UserControls sind modulare bzw. fertige (Programm)-Komponenten die nach au\u00dfen gekapselt sind. Manchmal w\u00e4hre es praktisch beim Verwenden eines UserControls, neben den \u00fcblichen Properties, den Inhalt direkt im XAML-Code zu definieren und einzubetten.<\/p>\n

<\/p>\n

Ich habe zur Demonstration ein UserControl erstellt, welches ein Grid mit blauen Hintergrund enth\u00e4lt. In diesem Grid soll der Inhalt gezeigt werden, welcher vom Verwender definiert wird (<ContentPresenter \/>).<\/p>\n

<UserControl \n    x:Class=\"ContentWPF.MyUserControl\"\n    xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n    xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n>\n    <Grid Background=\"Blue\">\n        <ContentPresenter \/>\n    <\/Grid>\n<\/UserControl><\/pre>\n

Die Verwendung von dem UserControl wird wie \u00fcblich in XAML deklariert\u00a0mit dem Unterschied das man diesmal direkt im XAML den Content angibt.<\/p>\n

<local:MyUserControl>\n    Hello World\n<\/local:MyUserControl><\/pre>\n

\u00a0Das Problem<\/h1>\n

Nun ist es so das am Ende nichts von der Definition des Controls (\u201aMyUserControl.xaml\u2018) gezeigt wird. Es wird nur noch\u00a0\u201eHello World\u201c ohne Grid und ohne Blau dargestellt.<\/p>\n

Der Grund ist, dass das Content-Property von UserControl mit dem Content des Verwenders \u00fcberschrieben wird und dabei die eigentliche Definition verloren geht.<\/p>\n

Mit diesem Wissen ist der n\u00e4chste Schritt nun zu verhindern, dass das Content-Property \u00fcberschrieben wird. Das machen wir einfach indem wir im CodeBehind von \u201aMyUserControl\u2018 ein separates Content-Property anlegen.<\/p>\n

public static readonly DependencyProperty MyContentProperty \n    = DependencyProperty.Register(\"MyContent\", typeof(object), typeof(MyUserControl), null);\npublic object MyContent\n{\n    get { return (object)GetValue(MyContentProperty); }\n    set { SetValue(MyContentProperty, value); }\n}<\/pre>\n
<UserControl \n    x:Class=\"ContentWPF.MyUserControl\"\n    xmlns=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\/presentation\"\n    xmlns:x=\"http:\/\/schemas.microsoft.com\/winfx\/2006\/xaml\"\n>\n    <Grid Background=\"Blue\">\n        <ContentPresenter Content=\"{Binding Path=MyContent,\n          RelativeSource={RelativeSource  AncestorType=UserControl}}\" \/>\n    <\/Grid>\n<\/UserControl><\/pre>\n

Mit dem neuen Property kann 'MyUserControl' folgenderwei\u00dfe verwendet werden:<\/p>\n

<local:MyUserControl MyContent=\"Hello World\" \/>\n\nOder\n\n<local:MyUserControl>\n    <local:MyUserControl.MyContent>\n        Hello World\n    <\/local:MyUserControl.MyContent>\n<\/local:MyUserControl><\/pre>\n

(zweite Variante ist besser wenn kein Text sondern ein kompletter Control-Tree eingef\u00fcgt wird).<\/i><\/p>\n

Jetzt funktioniert es zwar, aber nur indem man beim Verwenden des Control mehr Code schreibt als n\u00f6tig. Wer (so wie ich) sich daran st\u00f6rt unn\u00f6tigen Code zu produzieren f\u00fcr den habe ich folgende ...<\/p>\n

\u00a0L\u00f6sung \ud83d\ude00<\/h1>\n

Die L\u00f6sung ist so Simpel, allerdings wenn man es nicht wei\u00df kann man nicht darauf kommen.<\/p>\n

Man muss lediglich im CodeBehind von \u201aMyUserControl\u2018 das Klassen-Attribute 'ContentPropertyAttribute'<\/strong> deklarieren und als Parameter das gew\u00fcnschte Property als string angeben.<\/p>\n

namespace ContentWPF\n{\n    [ContentProperty(\"MyContent\")] \n    public partial class MyUserControl : UserControl\n    {\n        public MyUserControl()\n        {\n            InitializeComponent();\n        }\n        \n        public static readonly DependencyProperty MyContentProperty\n            = DependencyProperty.Register(\"MyContent\", typeof(object), typeof(MyUserControl), null);\n        public object MyContent\n        {\n            get { return (object)GetValue(MyContentProperty); }\n            set { SetValue(MyContentProperty, value); }\n        }\n    }\n}<\/pre>\n

Damit kann man nun im XAML-Code das Einbetten von Sub-Elementen (wie anfangs demonstriert) verwenden:<\/p>\n

<local:MyUserControl>\n    Hello World\n<\/local:MyUserControl><\/pre>\n

B\u00f6ser Beigeschmack<\/h1>\n

Leider gibt es selbst jetzt noch einen Nachteil, den man wohl nicht umgehen kann, wenn man ein UserControl verwendet. Wenn ihr \u201aMyUserControl\u2018 nun verwendet und einem Element darin einem Namen gebt werdet ihr vom Compiler informiert das dies nicht erlaubt ist.<\/p>\n

<local:MyUserControl>\n    <TextBlock x:Name=\"CompilerErrorIfItemIsNamed\">Hello World<\/TextBlock>\n<\/local:MyUserControl><\/pre>\n

Folgender Fehler wird ausgegeben:<\/p>\n

Cannot set Name attribute value 'CompilerErrorIfItemIsNamed' on element 'TextBlock'. 'TextBlock' is under the scope of element 'MyUserControl', which already had a name registered when it was defined in another scope.<\/i><\/p>\n

Den Namen selbst kann man zwar nachtr\u00e4glich \u00fcber CodeBehind (oder sonstwo) angeben aber zur Designzeit kann man dieses Element im CodeBehind nicht direkt ansprechen.<\/p>\n

Wenn man damit nicht Leben kann f\u00fchrt kein weg an einem CustomControl vorbei. Bei diesem k\u00f6nnen eingebettete Elemente mit einem Namen versehen und vom CodeBehind direkt angesprochen werden.<\/p>\n

Achtung: In Silverlight ist dieses Verhalten identisch, allerdings versucht euch der Compiler zu t\u00e4uschen. Die Fehlermeldung wird dort nicht gezeigt und das Projekt l\u00e4sst sich compilieren. Allerdings wenn Ihr zur Laufzeit im CodeBehind direkt auf das Element zugreift wird eine NullReferenceException fliegen da das Property immer leer ist.<\/p>\n","protected":false},"excerpt":{"rendered":"

UserControls sind modulare bzw. fertige (Programm)-Komponenten die nach au\u00dfen gekapselt sind. Manchmal w\u00e4hre es praktisch beim Verwenden eines UserControls, neben den \u00fcblichen Properties, den Inhalt direkt im XAML-Code zu definieren und einzubetten.<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[7,25,14],"tags":[13,12],"_links":{"self":[{"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/posts\/162"}],"collection":[{"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/comments?post=162"}],"version-history":[{"count":21,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/posts\/162\/revisions"}],"predecessor-version":[{"id":922,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/posts\/162\/revisions\/922"}],"wp:attachment":[{"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/media?parent=162"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/categories?post=162"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.rollnut.com\/wp-json\/wp\/v2\/tags?post=162"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}