XAML-Abschnitt in WPF-Steuerelement umwandeln

  • WPF

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von thefiloe.

    XAML-Abschnitt in WPF-Steuerelement umwandeln

    Hallo auch,

    heute mal ein paar Fragen von mir. Vorab: Ich bin kein Freund von WPF. Kann mit MVVM nix anfangen. Und liebe den WinForm-Designer, weil ich mit ihm (bzw. seinem mir seit VB 2.0 bekannten Vorgänger) die letzten 20 Jahre groß geworden bin. Nichtsdestotrotz bin ich nun "gezwungen", mich mit WPF auseinanderzusetzen.

    Erste Ergebnisse meiner "Studien" resultierten in einem WPF Window mit einem darin enthaltenen Bereich, der nun geschickterweise zur Wiederverwendung in weiteren, späteren Anwendungen, in ein WPF-Control ausgelagert werden soll - wobei ein Großteil versiegelt werden soll (d.h. vom Control-Client nicht änderbar) und bestimmte andere Elemente in Form von Control-Eigenschaften steuerbar werden sollen.

    Sinnvollerweise liegt die XAML-Definition dessen, was durch das Control repräsentiert werden soll, und dazu zählen auch die ControlTemplates der innerhalb dieses DockPanels verwendeten Elemente - auch alles brav in einem einzigen XML-Element, in diesem Fall einem DockPanel.

    Ausschnitt des für das Control relevanten XAMLs

    Quellcode

    1. <DockPanel SnapsToDevicePixels="True" UseLayoutRounding="True" DockPanel.Dock="Top" LastChildFill="True">
    2. <Border BorderThickness="0,1" BorderBrush="{x:Static SystemColors.ControlDarkDarkBrush}">
    3. <DockPanel Name="ToolbarControl" DockPanel.Dock="Top" LastChildFill="True">
    4. <DockPanel.Background>
    5. <LinearGradientBrush EndPoint="0,1">
    6. <GradientStop Color="White" Offset="0" />
    7. <GradientStop Color="{x:Static SystemColors.ControlDarkColor}" Offset="1" />
    8. </LinearGradientBrush>
    9. </DockPanel.Background>
    10. <Image Name="HeaderImage" Source="/WpfApplication1;component/Images/leftheader.gif" DockPanel.Dock="Left" Visibility="Visible" ClipToBounds="False" Height="{Binding ElementName=ToolbarPanel, Path=ActualHeight}"/>
    11. <StackPanel Name="ToolbarPanel" DockPanel.Dock="Top" VerticalAlignment="Top" Margin="0" Focusable="False" Orientation="Vertical">
    12. <StackPanel.Resources>
    13. <Style TargetType="TabItem">
    14. <Setter Property="Template">
    15. <Setter.Value>
    16. <ControlTemplate TargetType="TabItem">
    17. <ControlTemplate.Resources>
    18. <PathGeometry x:Key="TabBottomLine">
    19. <PathFigure StartPoint="-5,20" IsClosed="False" IsFilled="False">
    20. <PathFigure.Segments>
    21. <LineSegment Point="5000,20"/>
    22. </PathFigure.Segments>
    23. </PathFigure>
    24. </PathGeometry>
    25. <PathGeometry x:Key="TabGeometryInactive">
    26. <PathFigure StartPoint="0,20" IsClosed="False" IsFilled="True">
    27. <PathFigure.Segments>
    28. <!--<LineSegment Point="0,20"/>-->
    29. <LineSegment Point="0,5"/>
    30. <ArcSegment IsSmoothJoin="True" Point="5,0" RotationAngle="45" SweepDirection="Clockwise" Size="5,5" />
    31. <LineSegment IsSmoothJoin="True" Point="70,0" />
    32. <ArcSegment IsSmoothJoin="True" Point="75,5" Size="5,5" SweepDirection="Clockwise" />
    33. <LineSegment IsSmoothJoin="True" Point="75,20" />
    34. </PathFigure.Segments>
    35. </PathFigure>
    36. <PathFigure StartPoint="-5,20" IsClosed="False" IsFilled="False">
    37. <LineSegment Point="80,20"/>
    38. </PathFigure>
    39. </PathGeometry>
    40. <PathGeometry x:Key="TabGeometryActive">
    41. <PathFigure StartPoint="-5,20" IsClosed="False" IsFilled="False">
    42. <PathFigure.Segments>
    43. <LineSegment Point="0,20"/>
    44. </PathFigure.Segments>
    45. </PathFigure>
    46. <PathFigure StartPoint="0,23" IsClosed="False" IsFilled="True">
    47. <PathFigure.Segments>
    48. <LineSegment Point="0,5"/>
    49. <ArcSegment IsSmoothJoin="True" Point="5,0" RotationAngle="45" SweepDirection="Clockwise" Size="5,5" />
    50. <LineSegment IsSmoothJoin="True" Point="70,0" />
    51. <ArcSegment IsSmoothJoin="True" Point="75,5" Size="5,5" SweepDirection="Clockwise" />
    52. <LineSegment IsSmoothJoin="True" Point="75,23" />
    53. </PathFigure.Segments>
    54. </PathFigure>
    55. </PathGeometry>
    56. </ControlTemplate.Resources>
    57. <Grid Margin="5 5 0 0" Width="76" Height="20">
    58. <Canvas Width="76" Height="20">
    59. <Path Data="{StaticResource TabBottomLine}" Visibility="Hidden" SnapsToDevicePixels="True" Name="TabBottomLine" Stroke="Black" StrokeThickness="1" />
    60. <Path Data="{StaticResource TabGeometryActive}" Visibility="Hidden" SnapsToDevicePixels="True" Name="ActiveTab" Stroke="Black" StrokeThickness="1">
    61. <Path.Fill>
    62. <LinearGradientBrush EndPoint="0,1">
    63. <GradientStop Color="White" Offset=".5"/>
    64. <GradientStop Color="Transparent" Offset="1"/>
    65. </LinearGradientBrush>
    66. </Path.Fill>
    67. </Path>
    68. <Path Data="{StaticResource TabGeometryInactive}" SnapsToDevicePixels="True" Name="InactiveTab" Stroke="Black" StrokeThickness="1" >
    69. <Path.Fill>
    70. <LinearGradientBrush EndPoint="0,1">
    71. <GradientStop Color="White" Offset="0"/>
    72. <GradientStop Color="Transparent" Offset="1"/>
    73. </LinearGradientBrush>
    74. </Path.Fill>
    75. </Path>
    76. <Path Clip="{StaticResource TabGeometryInactive}" Visibility="Hidden" Name="ActiveIndicator" Stroke="Orange" StrokeThickness="3" SnapsToDevicePixels="True">
    77. <Path.Data>
    78. <LineGeometry StartPoint="1,2" EndPoint="74,2"/>
    79. </Path.Data>
    80. </Path>
    81. </Canvas>
    82. <ContentPresenter x:Name="ContentSite" ContentSource="Header" Margin="0 2 0 0" HorizontalAlignment="Center" ClipToBounds="True" />
    83. </Grid>
    84. <ControlTemplate.Triggers>
    85. <Trigger Property="IsSelected" Value="True">
    86. <Setter Property="Panel.ZIndex" Value="100" />
    87. <Setter Property="FontWeight" Value="Bold"/>
    88. <Setter TargetName="ActiveIndicator" Property="Visibility" Value="Visible" />
    89. <Setter TargetName="InactiveTab" Property="Visibility" Value="Hidden" />
    90. <Setter TargetName="ActiveTab" Property="Visibility" Value="Visible" />
    91. </Trigger>
    92. <Trigger Property="IsEnabled" Value="False">
    93. <Setter TargetName="InactiveTab" Property="Fill" Value="LightGray" />
    94. <Setter Property="Foreground" Value="White" />
    95. <Setter TargetName="InactiveTab" Property="Visibility" Value="Visible" />
    96. <Setter TargetName="ActiveTab" Property="Visibility" Value="Hidden" />
    97. </Trigger>
    98. <Trigger Property="Header" Value="@">
    99. <Setter TargetName="ContentSite" Property="Visibility" Value="Hidden" />
    100. <Setter TargetName="TabBottomLine" Property="Visibility" Value="Visible" />
    101. <Setter TargetName="InactiveTab" Property="Visibility" Value="Hidden" />
    102. <Setter TargetName="ActiveTab" Property="Visibility" Value="Hidden" />
    103. </Trigger>
    104. </ControlTemplate.Triggers>
    105. </ControlTemplate>
    106. </Setter.Value>
    107. </Setter>
    108. </Style>
    109. <Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
    110. <Setter Property="Width" Value="38" />
    111. <Setter Property="Height" Value="38" />
    112. <Setter Property="BorderThickness" Value="1" />
    113. <Setter Property="Padding" Value="2" />
    114. <Setter Property="Focusable" Value="False"/>
    115. <Setter Property="IsTabStop" Value="False"/>
    116. <Setter Property="Command" Value="{x:Static my:MyCommands.ToolbarButton}"/>
    117. </Style>
    118. <Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}">
    119. <Setter Property="Width" Value="38" />
    120. <Setter Property="Height" Value="38" />
    121. <Setter Property="BorderThickness" Value="1" />
    122. <Setter Property="Padding" Value="2" />
    123. <Setter Property="Focusable" Value="False"/>
    124. <Setter Property="IsTabStop" Value="False"/>
    125. <Setter Property="Command" Value="{x:Static my:MyCommands.ToolbarButton}"/>
    126. </Style>
    127. <Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource {x:Type ToggleButton}}"/>
    128. <Style TargetType="{x:Type RadioButton}" BasedOn="{StaticResource {x:Static ToolBar.RadioButtonStyleKey}}">
    129. <Setter Property="Width" Value="38" />
    130. <Setter Property="Height" Value="38" />
    131. <Setter Property="BorderThickness" Value="1" />
    132. <Setter Property="Padding" Value="2" />
    133. <Setter Property="Focusable" Value="False"/>
    134. <Setter Property="IsTabStop" Value="False"/>
    135. <Setter Property="Command" Value="{x:Static my:MyCommands.ToolbarButton}"/>
    136. </Style>
    137. <Style TargetType="{x:Type Image}">
    138. <Setter Property="Height" Value="{x:Static SystemParameters.IconHeight}"/>
    139. </Style>
    140. </StackPanel.Resources>
    141. <Border BorderThickness="0,0,0,1" BorderBrush="{x:Static SystemColors.ControlDarkDarkBrush}">
    142. <DockPanel>
    143. <Image Name="Logo" Height="30" DockPanel.Dock="Right" Margin="0 0 5 0" VerticalAlignment="Center" Source="/WpfApplication1;component/Images/38.png"/>
    144. <StackPanel Margin="0 1" Height="38" Name="MainToolbar" Orientation="Horizontal">
    145. <!--########################### Main Toolbar ################################## //-->
    146. <Button Command="{x:Static my:MyCommands.Exit2}">
    147. <Image Source="/WpfApplication1;component/Images/icon1.ico" />
    148. </Button>
    149. <Button Content="Open" Command="Open"/>
    150. <Button Content="OK"/>
    151. <Button>TEST</Button>
    152. <Button Content="Add" CommandParameter="AddPart" ToolTip="Dies ist ein Tooltip"/>
    153. <Button>_Hilfe</Button>
    154. <Separator/>
    155. <ToggleButton>Check</ToggleButton>
    156. <CheckBox>Hallo</CheckBox>
    157. <Separator/>
    158. <Button Content="K1"/>
    159. <Button Content="Exit" Command="{x:Static my:MyCommands.Exit2}"/>
    160. <!--########################### Main Toolbar Ende ############################# //-->
    161. </StackPanel>
    162. </DockPanel>
    163. </Border>
    164. <TabControl Background="Transparent" IsTabStop="False" Margin="0 2 0 0" Focusable="False" Name="tabControl1" MinHeight="25" Padding="2" BorderThickness="0">
    165. <!--########################### TabMenu ################################## //-->
    166. <TabItem Header="Tab 1">
    167. <StackPanel Orientation="Horizontal">
    168. <!--###### Buttons Tab 1 ###### //-->
    169. <Button Name="btnToAdorn">K1</Button>
    170. <Button>K2</Button>
    171. <Button>K3</Button>
    172. <Button>K4</Button>
    173. <Button>K5</Button>
    174. </StackPanel>
    175. </TabItem>
    176. <TabItem Header="Tab 2">
    177. <StackPanel Orientation="Horizontal">
    178. <!--###### Buttons Tab 2 ###### //-->
    179. <Button>A1</Button>
    180. </StackPanel>
    181. </TabItem>
    182. <TabItem Header="Tab 3">
    183. <StackPanel Orientation="Horizontal">
    184. <!--###### Buttons Tab 3 ###### //-->
    185. <Button Content="K1"/>
    186. </StackPanel>
    187. </TabItem>
    188. <TabItem Header="Tab 4" IsEnabled="False">
    189. <StackPanel Orientation="Horizontal">
    190. <!--###### Buttons Tab 4 ###### //-->
    191. <Button>N1</Button>
    192. <Button>N1</Button>
    193. <Button>N1</Button>
    194. <Button>N1</Button>
    195. </StackPanel>
    196. </TabItem>
    197. <!--########################### TabMenu Ende ############################# //-->
    198. <TabItem Header="@" IsEnabled="False" />
    199. </TabControl>
    200. </StackPanel>
    201. </DockPanel>
    202. </Border>
    203. </DockPanel>

    Anmerkungen dazu
    Ja, ich weiß, dass manche Buttons "seltsame" Bezeichnungen haben bzw. einige der enthaltenen Elemente an keinen Befehl gebunden sind. Das ist so "gewollt" bzw. dient nur zu Testzwecken.
    Das letzte TabItem mit dem Header "@" soll fest sein und wurde von mir dafür "missbraucht", um neben den normalen TabItems noch eine durchgehende Linie hinzubekommen, ohne die das Gesamtbild sch***e aussehen würde. Vllt. gibts ja ne bessere Idee, wie ich diese Linie so gestalten kann, dass das Gesamtbild am Ende das gleiche ist.

    Der geübte Betrachter wird recht schnell bemerken: In diesem XAML gibt es drei Stellen, wo das Control später seinen "Content" aufnehmen soll, bzw. wo es vom Benutzer des Controls mit Leben gefüllt werden soll. Ich habe sie mit entsprechenden Kommentaren markiert und mit "Main Toolbar", "TabMenu" und "Buttons Tab x" genannt.

    Als erstes stellt sich mir die Frage: Ist nun ein "UserControl" oder ein "Userdefined Control" das richtige? Ich wage hier mal eine eigene Antwort und behaupte, ein "Userdefined Control" wäre doch der bessere (oder einzig sinnvolle) Ansatz.

    Dann ist aber die Frage: Wie gehe ich ab hier am besten vor? Wie krieg ich jetzt aus dem XAML ein Control gebaut, dabei ggf. noch diverse Eigenschaften so "exportiert", dass der Benutzer des Controls hier Einfluß nehmen oder gar das Template modifizieren kann. Ein paar kleine Schubser, die mir für den Start die richtige Richtung weisen, dürften hoffentlich ausreichend sein.

    Ach und dann noch abschließend, da ich jetzt den XAML-Code schonmal hier habe, eine Frage: Ich habe ja versucht, die Tab-Register mit einem eigenen Style zu versehen. Ich habe dazu PathGeometry-Elemente gebraucht, um den gewünschten Look zu erreichen. Dummerweise - wie auch das entsprechende Tutorial in diesem Forum bestätigte - greift bei Grafik-Pfaden die Eigenschaft "UseLayoutRounding" nicht, was leider dazu führt - obwohl ich nicht wirklich eine Erklärung dafür habe - dass manche der durch die Paths entstehenden Linien "zwischen" den Units liegen und somit verwischen. Hat jemand einen Vorschlag wie man das anders lösen kann, damit dieser Effekt nicht auftritt?
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Am besten du baust dein Control mit deinen Eigenschaften etc.(Achtung bei der Wahl der Basisklasse - es gibt sehr viele nicht nur "Control"). Anschließend verwendest du deinen XAML-Code als Template welches du als Ressource definierst. Für das Template musst du das TemplatePartAttribut verwenden, die entsprechenden Methoden überschreiben,...
    Das ist alles etwas viel zu erklären. Deshalb übernimmt dies msdn: msdn.microsoft.com/en-us/library/cc964292(v=vs.95).aspx


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Herzlichen Dank erstmal für die Antwort. Ich hatte in der Zwischenzeit noch ein wenig gebastelt und hatte schon recht erfolgversprechende aber dann doch am Ende gescheiterte Versuche mit UserControls gehabt.
    Jetzt mach ich aber erstmal Feierabend und werde mich morgen mal um den von dir vorgeschlagenen Ansatz bemühen. Ich hoffe, der Link kann das mit den Templates einigermaßen durchschaubar erklären. Mein hier vor mir liegender Buchautor "Thomas Claudius Huber" war leider nicht so richtig in der Lage, die Erstellung eigener WPF-Controls in ausreichender Tiefe zu verdeutlichen.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Das ist recht. Ich kenne seine Bücher und diese behandeln WPF eher Oberflächlich für Durchschnittsanwender. Tatsächlich gehört eigene Controls zu machen eher nicht mehr in den Durchschnittsbereich, da du in WPF über 90% durch Templates und Styles lösen kannst(hin und wieder auch durch AttachedProperties).
    Wenn du selbst Controls entwickelst gibt es einige Regeln zu befolgen(auch abhängig auf welcher Ebene bzw. welche Basisklasse du verwendest). Hatte dieses Buch mal aus der Bücherei geholt und auch großteils gelesen. Dieses geht schon ein wenig konkreter auf speziellere Themen ein: amazon.de/WPF-Control-Developm…Experiences/dp/0672330334
    Sind auch einige echt tolle Beispiele drinnen.
    EDIT: Google hat mir grad bei der Suche pdf vorgeschlagen. Erstes Ergebnis ist das hier: it-ebooks.info/book/2433/. Ob dies legal ist weiß ich nicht aber es scheint das ganze Buch zum Download zur Verfügung zu stehen. Aber wie gesagt. Ich habe mich nicht informiert ob diese Seite legal ist.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.