Eigenschaft Path Data eines Canvas via Code Behind festlegen

  • WPF

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Eigenschaft Path Data eines Canvas via Code Behind festlegen

    Einen schönen Tag zusammen,

    ich bastel mir gerade einen MediaPlayer und da hab ich einen Play/Pause-Button natürlich drin. Dieser soll entweder ein Play-, oder ein Pause-Icon anzeigen. Ich benutze dazu ein Canvas und habe in meinem XAML-Code also wie folgt die Eigenschaft gesetzt:

    XML-Quellcode

    1. <Border Name="brdPlay" Grid.Column="2" Style="{DynamicResource Navigationsbuttons}" MouseLeftButtonDown="BrdPlay_MouseLeftButtonDown" ToolTip="Spiele den ausgewählten Musiktitel oder Radiostream ab...">
    2. <Canvas Grid.Row="0" Name="icoPlay" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Width="32" Height="32" VerticalAlignment="Center" HorizontalAlignment="Center">
    3. <Path Fill="{DynamicResource VordergrundfarbeBrush}" Data="M6 4l20 12-20 12z"/>
    4. </Canvas>
    5. </Border>


    Jetzt ist die Frage: Wie kann ich per Code Behind die Path.Data-Eigenschaft setzen? Oder, wenn das nicht ohne grösseren Aufwand möglich ist, kann ich das mit einer DynamicResource regeln und wie? Besonders interessieren würde mich dabei, wie man diese Resource definiert.

    Das Icon soll sich sowohl bei einem Klick als auch über verschiedene andere Stellen im Code ändern...
    Hallo

    Leider sehe ich hier wieder einen großen Fehler. Du arbeitest wiedermal ohne Binding.

    kafffee schrieb:

    Wie kann ich per Code Behind



    Aber egal, hierfür brauchst du weder Binding noch CodeBehind.
    Arbeite mit einem DataTrigger. Dieser setzt die Eigenschaft je nach wert.

    Hier ein Beispiel in welchem der Background einer TextBox nach Status einer Checkbox gesetzt wird. Das solltest du relativ simpel auf deinen Fall umschreiben können.

    XML-Quellcode

    1. <Style TargetType="TextBox">
    2. <Setter Property="Background" Value="Transparent" />
    3. <Style.Triggers>
    4. <DataTrigger Binding="{Binding IsChecked, ElementName=WarnStatusSource}" Value="True">
    5. <Setter Property="Background" Value="{DynamicResource WarningColor}" />
    6. </DataTrigger>
    7. </Style.Triggers>
    8. </Style>


    Dann brauchst du nur für jeden Fall einen DataTrigger.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ich habe mir jetzt gedacht, am einfachsten wäre es, das ganze einfach an die Eigenschaft meiner Player-DLL (bass.dll, hast du vielleicht schon gehört von) zu binden. Diese kann man nämlich wie folgt abfragen:

    If CType(Bass.BASS_ChannelIsActive(_strm), BASSActive) = BASSActive.BASS_ACTIVE_PLAYING Then

    Aber da müsste ich mir dann so eine Konverterklasse schreiben, sehe ich das richtig?

    Bin gerade dabei das zu versuchen, aber hab jetzt schon Kopfschmerzen...

    Wäre es nicht einfacher, einfach eine Variable zu erstellen, meinetwegen "IsPlaying As Booolean = True" und diese dann zu binden?
    Folgendes funktioniert bei mir natürlich nicht:

    XML-Quellcode

    1. <Style TargetType="Canvas">
    2. <Style.Triggers>
    3. <DataTrigger Binding="{Binding ElementName=IsPlaying}" Value="True">
    4. <Setter Property="Background" Value="Aquamarine"/>
    5. </DataTrigger>
    6. </Style.Triggers>
    7. </Style>
    Hallo

    Wenn ich das richtig sehe willst du einen Player erstellen.

    Korrekt wäre es nun eine Klasse zu erstellen welche dir sowohl die Eigenschaften, als auch die Commands zur Verfügung stellt. Das ist eine Struktur wie man sie programmieren sollte.

    Gerne gebe ich dir hier unterstützung, so das du was dabei lernst und dann evtl. die Vorteile der WPF auch gleich besser zu schätzen lernst.
    Aber hierfür würde ich Infos benötigen was genau du denn machen willst. Audioplayer, Videoplayer usw. und vorallem wie. Also z.b. mittels Windows MediaPlayer Bibliothek oder wie?

    Dann kann ich dir ja ein rudimentäres Beispiel erstellen. Du kannst auch gerne das was du bereits so hast hier hochladen, da sehe ich sicher auch einiges raus.

    Grüße
    sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ja cool
    Sache ist die. Ich hab mir von letztem Dezember bis vor ca. zwei Monaten einen (für meinen Begriff) richtig komplexen Player Programms in Winforms geschrieben. Mit allen Schikanen, Equalizer etc... und komplett lauffähig. Benutzt hab ich dazu die Bass.net.dll von Un4seen Developments. Ist um einiges umfangreicher als der Windows Media Player...

    Diese DLL stellt mir ja die Eigenschaften etc. zur Verfügung (ist ja also auch eine Art Klasse, bloss halt als DLL ausgelagert) aber wenn ich dich richtig verstehe, willst du, dass ich nochmal selber dann eine Klasse schreib, die dann die DLL bedient?

    Oder ist es möglich, direkt die Eigenschaften und Methoden der DLL etc., in XAML einzubinden?
    Naja, man verwendet eh die Klassen der Bibliothek, aber man benötigt ein ViewModel. Die Klasse die ich meine ist eine abbildung der View.

    Schau mal meine Tutorialreihe, dann wirds klarer.

    Du solltest versuchen WPF zu lernen und nicht einfach drauf los zu proggen. Das klappt bei WPF nicht lange.

    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    Gerne gebe ich dir hier unterstützung, so das du was dabei lernst und dann evtl. die Vorteile der WPF auch gleich besser zu schätzen lernst.


    Und wo fange ich da konkret an? Ich habe quasi eine fertige UI in XAML und einen fertigen Code in WinForms... Bis jetzt finde ich, wie ErfinderDesRades auch schon bemerkte, ist WPF ziemlich umständlich, wenn man es falsch handhabt... Wenn du nichts dagegen hast, würde es mir für den Anfang auch reichen, einfach meinen Winforms Code möglichst ohne neues Konzept weitestgehend zu übernehmen und mich bei Fragen nochmal zu melden, denn dieses bestehende Programm war für mich ein halbes Jahr Arbeit, das will ich eigentlich nicht komplett nochmal überdenken müssen, wäre schade...

    In diesem Fall wäre das: Wie kann ich mit einer Variable das Canvas Path Data triggern anstatt mit einer Property eines anderen Controls wie in deinem Beispiel?
    OK, ich versuchs mal durchzugehen.

    kafffee schrieb:

    ist WPF ziemlich umständlich, wenn man es falsch handhabt

    Richtig! Aber auch logisch. Ich vergleiche das gerne mit Autofahren. Ist Anfangs auch etwas Übung, deshalb geht man in die Fahrschule. Wenn die Fahrschule es so lernt wie sich der Autobauer das vorstellt hat dann hat mans relativ schnell raus. Jetzt stell dir vor du versuchst von Anfang an mit der linken Hand zu schalten. Das klappt auch, ist aber verdammt umständlich. Dann kommst du mal in eine Situation in der du noch etwas stärker lenken musst und willst dabei auch schalten, dann suchst du hierfür eine Lösung weils einfach nicht mehr geht. Evtl. löst du auch dieses Problem, bis die nächste Situation kommt. Besser so fahren wie der Hersteller sich das gedacht hat.

    Microsoft hat die WPF so gebaut das alles auf Binding ausgelegt ist, also auch so verwenden. Alles andere für immer und immer wieder zu Problemen für welche Workarounds gesucht werden müssen. Um wieder zum Auto zu gehen. Wenn du einfach nicht mit links Schalten willst dann musst du bei einem Auto mit automatik beiben. Du willst aber schalten? OK, dann musst du es aber eben lernen.

    kafffee schrieb:

    einfach meinen Winforms Code möglichst ohne neues Konzept weitestgehend zu übernehmen

    Das kannst du auch zu einem SEHR großem Teil tun, denn die Logik des Codes kann weitgehend übernommen werden. Einzig der Code welcher die UI manipuliert fliegt. Ist als in deinem Fall von der Logik her Copy an Paste.
    Aber der Aufbau wie die Logik in Form von Code integriert wird ist ein anderer.

    kafffee schrieb:

    In diesem Fall wäre das: Wie kann ich mit einer Variable das Canvas Path Data triggern anstatt mit einer Property eines anderen Controls wie in deinem Beispiel?

    Das habe ich dir beantwortet und dafür benötigt es auch keinerlei Code. Du kannst ja auf Eigenschaften binden. Hast du keine Eigenschaften kannst du nicht binden, somit kannst du auch einen Trigger nicht verwenden. Und deshalb wieder kannst du eine DynamicResource NICHT ändern. Warum? Weil das MS nicht vorgesehen hat, da die WPF auf Binding aufbaut. Ich würde dir gerne etwas anderes sagen aber es ist hald so.

    Ich kann es nur immer wieder sagen und es dir ans Herz legen. Lerne WPF und verwende sie so wie es gehört.

    Gerne stelle mal dein WinForms Projekt hier ein (solange es nicht "geheim" ist) damit ich dir Code für die Playersteuerung habe und ich baue dir mal ein kleines Playerbeispiel mit deinem "Play" Button. Mehr kann ich dir hier nicht anbieten. Das ein Portierung von WinForms zur WPF nicht einfach ist wirst du sicher schon öfters als Info bekommen haben, das brauche ich dir denke ich mal nicht nochmals schreiben.

    Grüße

    PS: Sorry, ich hätte dir gerne bessere Nachrichten übermittelt 8-)
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    Du kannst ja auf Eigenschaften binden. Hast du keine Eigenschaften kannst du nicht binden, somit kannst du auch einen Trigger nicht verwenden.

    Ja Eigenschaften hat es zwar, aber nur bedingt, da müsste man dann glaube ich noch nen Konverter dazu programmieren...

    Nofear23m schrieb:

    Gerne stelle mal dein WinForms Projekt hier ein (solange es nicht "geheim" ist) damit ich dir Code für die Playersteuerung habe und ich baue dir mal ein kleines Playerbeispiel mit deinem "Play" Button.


    8o Das Angebot nehm ich gern an. Ich will das Projekt tatsächlich nicht "as is" hochladen, abgesehen davon sind das glaube ich so um die 2000 Zeilen Code, das will ich dir nicht zumuten. Ich habs also so weit wie möglich runterreduziert und lade es jetzt hoch... Müsste eigentlich selbsterklärend sein das Teil. Der XAML-Code für das Pause-Icon wäre dann:

    Data="M4 4h10v24h-10zM18 4h10v24h-10z"

    Bin mal gespannt was du draus machst :D

    Grüssle ausm Schwabenland :)
    Dateien
    • TestPlayer.zip

      (1,08 MB, 60 mal heruntergeladen, zuletzt: )
    Hallo

    Mach ich gerne. Hoffe es macht dir nichts das ich erst morgen dazu komme. Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo

    Also, ich habe dir eine rudimentäre Implementierung erstellt.

    Die ViewModel-Klasse regelt dir nun Play/Pause, Stop und das Laden eines Files. Hält den Dateinamen und regelt die Logik. Und das wie ich finde sehr übersichtlich.

    VB.NET-Quellcode

    1. Public Class PlayerViewModel
    2. Inherits ViewModelBase
    3. Private streamA As Integer
    4. Public Sub New()
    5. Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_CPSPEAKERS, IntPtr.Zero, Nothing)
    6. FileName = "\\SPSSERVER\Shared Folders\Musik\Alben\Die Ärzte - Gesammelt\Die Ärzte - Bäst Of\103_die_aerzte_-_friedenspanzer.mp3"
    7. End Sub
    8. Private _fileName As String
    9. Public Property FileName() As String
    10. Get
    11. Return _fileName
    12. End Get
    13. Set(ByVal value As String)
    14. _fileName = value
    15. RaisePropertyChanged()
    16. End Set
    17. End Property
    18. Public ReadOnly Property IsPlaying As Boolean
    19. Get
    20. Return CType(Bass.BASS_ChannelIsActive(streamA), BASSActive) = BASSActive.BASS_ACTIVE_PLAYING
    21. End Get
    22. End Property
    23. Private _loadFileCommand As ICommand
    24. Public Property LoadFileCommand() As ICommand
    25. Get
    26. If _loadFileCommand Is Nothing Then
    27. _loadFileCommand = New RelayCommand(Sub() streamA = Bass.BASS_StreamCreateFile(FileName, 0, 0, BASSFlag.BASS_STREAM_AUTOFREE Or BASSFlag.BASS_STREAM_PRESCAN))
    28. End If
    29. Return _loadFileCommand
    30. End Get
    31. Set(ByVal value As ICommand)
    32. _loadFileCommand = value
    33. End Set
    34. End Property
    35. Private _performStopCommand As ICommand
    36. Public Property PerformStopCommand() As ICommand
    37. Get
    38. If _performStopCommand Is Nothing Then _performStopCommand = New RelayCommand(AddressOf PerformStopCommand_Execute, Function() streamA <> 0)
    39. Return _performStopCommand
    40. End Get
    41. Set(ByVal value As ICommand)
    42. _performStopCommand = value
    43. End Set
    44. End Property
    45. Private Sub PerformStopCommand_Execute(obj As Object)
    46. Bass.BASS_ChannelStop(streamA) : streamA = 0
    47. RaisePropertyChanged(NameOf(IsPlaying))
    48. End Sub
    49. Private _togglePlayCommand As ICommand
    50. Public Property TogglePlayCommand() As ICommand
    51. Get
    52. If _togglePlayCommand Is Nothing Then _togglePlayCommand = New RelayCommand(AddressOf TogglePlayCommand_Execute, AddressOf TogglePlayCommand_CanExecute)
    53. Return _togglePlayCommand
    54. End Get
    55. Set(ByVal value As ICommand)
    56. _togglePlayCommand = value
    57. RaisePropertyChanged()
    58. End Set
    59. End Property
    60. Private Sub TogglePlayCommand_Execute(obj As Object)
    61. If streamA = 0 Then
    62. If LoadFileCommand.CanExecute(Nothing) Then LoadFileCommand.Execute(Nothing)
    63. End If
    64. If IsPlaying Then
    65. Bass.BASS_ChannelPause(streamA)
    66. Else
    67. Bass.BASS_ChannelPlay(streamA, False)
    68. End If
    69. RaisePropertyChanged(NameOf(IsPlaying))
    70. End Sub
    71. Private Function TogglePlayCommand_CanExecute(obj As Object) As Boolean
    72. Return FileName IsNot Nothing AndAlso streamA <> 0
    73. End Function
    74. End Class



    Die View ist auf diese Klasse gebunden und somit ändern sich die Buttons automatisch. Auch das Disable und Enable der Buttons klappt automatisch über die Commands. Saubere Sache wie ich finde.

    XML-Quellcode

    1. ​<UserControl x:Class="uclPlayer"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    6. xmlns:local="clr-namespace:TestPlayer" xmlns:viewmodel="clr-namespace:TestPlayer.ViewModel"
    7. mc:Ignorable="d" Background="WhiteSmoke"
    8. d:DesignHeight="450" d:DesignWidth="800">
    9. <UserControl.Resources>
    10. <ResourceDictionary Source="Resources/Icons.xaml"/>
    11. </UserControl.Resources>
    12. <UserControl.DataContext>
    13. <viewmodel:PlayerViewModel/>
    14. </UserControl.DataContext>
    15. <Grid>
    16. <Grid.RowDefinitions>
    17. <RowDefinition Height="Auto"/>
    18. <RowDefinition Height="*"/>
    19. </Grid.RowDefinitions>
    20. <StackPanel>
    21. <Label Content="Dateipfad für Player"/>
    22. <DockPanel LastChildFill="True">
    23. <Button Content="_Lade Datei" DockPanel.Dock="Right" Margin="3,0" Padding="2" Command="{Binding LoadFileCommand}"/>
    24. <TextBox Text="{Binding FileName}" VerticalAlignment="Center"/>
    25. </DockPanel>
    26. </StackPanel>
    27. <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top">
    28. <Button Margin="10" Height="60" Command="{Binding TogglePlayCommand}">
    29. <Button.Style>
    30. <Style TargetType="Button">
    31. <Setter Property="Content" Value="{StaticResource IconPlay}"/>
    32. <Style.Triggers>
    33. <DataTrigger Binding="{Binding IsPlaying}" Value="True">
    34. <Setter Property="Content" Value="{DynamicResource IconPause}"/>
    35. </DataTrigger>
    36. </Style.Triggers>
    37. </Style>
    38. </Button.Style>
    39. </Button>
    40. <Button Margin="10" Height="60" Command="{Binding PerformStopCommand}" Content="{StaticResource IconStop}"/>
    41. </StackPanel>
    42. </Grid>
    43. </UserControl>


    Die Icons sind in einem ResourceDisctionary hinterlegt.

    XML-Quellcode

    1. ​ <Color x:Key="DefaultIconColor">Black</Color>
    2. <SolidColorBrush x:Key="DefaultIconBrush" Color="{StaticResource DefaultIconColor}"/>
    3. <Viewbox x:Key="IconPlay" x:Shared="False">
    4. <Canvas Width="24" Height="24">
    5. <Path Data="M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" Fill="{StaticResource DefaultIconBrush}" />
    6. </Canvas>
    7. </Viewbox>
    8. <Viewbox x:Key="IconPause" x:Shared="False">
    9. <Canvas Width="24" Height="24">
    10. <Path Data="M15,16H13V8H15M11,16H9V8H11M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" Fill="{StaticResource DefaultIconBrush}" />
    11. </Canvas>
    12. </Viewbox>
    13. <Viewbox x:Key="IconStop" x:Shared="False">
    14. <Canvas Width="24" Height="24">
    15. <Path Data="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M9,9V15H15V9" Fill="{StaticResource DefaultIconBrush}" />
    16. </Canvas>
    17. </Viewbox>


    Grüße
    Sascha
    Dateien
    • TestPlayerWpf.zip

      (413,04 kB, 55 mal heruntergeladen, zuletzt: )
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ja, gerne.

    Wichtig ist das du verstehst um was es geht. Die View ist an ein ViewModel gebunden. Im ViewModel steuerst du damit die View und deren Änderungen bzw. reagierst auf Änderungen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##