none
Wie kann ich folgendes mit WPF ? realisieren? RRS feed

  • Frage

  • Ich stelle diverse Zustände (jetzt noch mit Windows Forms) Visuell dar. z.B. Pfeile (einen Motor,Krahn) mit diversen Farben, für die ich für jede Farbe ein eigenes Image habe.
    Ich wollte jetzt wissen ob ich das mit WPF (SVG? ) evtl. besser löse kann. Kann ich unter WPF die Grafik oder wie auch immer nur einmal haben und denn unterschiedliche Farben über Properties oder zu setzen?
    Samstag, 23. Mai 2020 18:31

Antworten

  • Hi Thomas,
    natürlich kann so etwas auch funktionieren.

    Es muss aber driftige Gründe geben, wenn das UserControl zur Laufzeit hinzugefügt werden soll, da dann Design und Logik gemischt werden und die Gestaltung, Tests und Fehlersuche erschwert werden. Üblich ist solche Arbeitsweise, wenn aus externen Quellen (z.B. Datenbanken) eine Liste von Datenobjekten geladen wird und für jedes Listenelement ein Steuerelement darzustellen ist.

    In der folgenden Demo wird aus einer Liste ("Farben") mit dem oben gezeigten Steuerelement ("Window023UC1") ein Listen-Steuerelement (ItemsControl) gefüllt.

    XAML:

    <Window x:Class="Window023"
            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:WpfApp1.WpfApp023"
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
            mc:Ignorable="d"
            Title="Window023" Height="200" Width="200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <ItemsControl ItemsSource="{Binding ElementListe}"/>
      </Grid>
    </Window>
    

    Dazu der Code:

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    Imports WpfControlLibrary1
    
    Namespace WpfApp023
      Public Class ViewModel
    
        Public Sub New()
          For i = 0 To Farben.Count - 1
            Dim uc As New Window023UC1 With {.Height = 100}
            uc.SetBinding(Window023UC1.FarbeProperty, $"Farben[{i}].FarbBrush")
            ElementListe.Add(uc)
          Next
        End Sub
    
        Public ReadOnly Property Farben As List(Of Farbe)
          Get
            Dim l As New List(Of Farbe)
            l.Add(New Farbe With {.Name = "rot", .FarbBrush = Brushes.Red})
            l.Add(New Farbe With {.Name = "blau", .FarbBrush = Brushes.Blue})
            l.Add(New Farbe With {.Name = "grün", .FarbBrush = Brushes.Green})
            Return l
          End Get
        End Property
    
        End Sub
    
      End Class
    
      Public Class Farbe
        Public Property Name As String
        Public Property FarbBrush As Brush
      End Class
    
    End Namespace

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 6. Juni 2020 03:15

Alle Antworten

  • Hi Thomas,
    natürlich geht so etwas in WPF, wenn unter Grafik ein Steuerelement zu verstehen ist, z.B. ein UserControl. Über Eigenschaften kann man das Aussehen der Innereien des Steuerelementes beeinflussen. Je nach Aussehen und Ziel der Gestaltung kann u.U. auch schon ein vorhandenes Steuerelement ausreichen, z.B. ein Rechteck, dessen Hintergrundfarbe verändert werden soll.

    Beschriebe mal etwas genauer, was du machen willst und in welcher Programmiersprache. Da die Möglichkeiten von WPF um ein Vielfaches umfangreicher sind als die Möglichkeiten in Windows Forms, gibt es auch sehr verschiedene Lösungswege.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 23. Mai 2020 18:58
  • Hi Peter, ich zeige für eine Bäckerei eine Visualisierung mit diversen Geräten an. Ich habe u.a. für eine Grafik  5 verschiedenen Dateien abgelegt. Wo (hier jetzt der Rote Teil) in unterschiedlichen Farben dargestellt wird. Da wollte ich wissen ob ich mit WPF( SVG) das nur mit einer (auch skalierbaren) Grafik lösen könnte ?
    Aktuell sind es alles png's . 

    Gruß

    Montag, 25. Mai 2020 07:52
  • Hi Thomas,
    ich würde ein UserControl programmieren, welche einzelne Steuerelemente so platziert, dass das Bild entsteht. Dem Steuerelement können Eigenschaften hinzugefügt werden, die dann auf die Darstellung einwirken, z.B. Hintergrundfarbe eines Rechtecks, welches ein Teil der Gesamtgrafik ist. U.U. ist auch eine Kombination möglich, wobei im Hintergrund eine Grafik liegt, die im Vordergrund durch ein UI-Element überlagert wird, z.B. durch ein Recheck, Ellipse oder Polygon. Die Eigenschaften dies "Vordergrund-Elements" werden dann von "außen" gesteuert, z.B. Transparenz. Das alles kann bei Bedarf auch mit einem StoryBoard animiert werden.

    Hier mal ein Beispiel:

    XAML MainWindow:

    <Window x:Class="Window023"
            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:WpfApp1.WpfApp023"
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
            mc:Ignorable="d"
            Title="Window023" Height="200" Width="200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox x:Name="lb"
                  ItemsSource="{Binding Farben}"
                  DisplayMemberPath="Name"
                  SelectedValuePath="FarbBrush"/>
        <uc:Window023UC1 Grid.Column="1" 
                         Farbe="{Binding ElementName=lb, Path=SelectedValue}"/>
      </Grid>
    </Window>

    ViewModel für Farbliste:

    Namespace WpfApp023
      Public Class ViewModel
    
        Public ReadOnly Property Farben As List(Of Farbe)
          Get
            Dim l As New List(Of Farbe)
            l.Add(New Farbe With {.Name = "rot", .FarbBrush = Brushes.Red})
            l.Add(New Farbe With {.Name = "blau", .FarbBrush = Brushes.Blue})
            l.Add(New Farbe With {.Name = "grün", .FarbBrush = Brushes.Green})
            Return l
          End Get
        End Property
      End Class
    
      Public Class Farbe
        Public Property Name As String
        Public Property FarbBrush As Brush
      End Class
    
    End Namespace
    

    XAML UserControl:

    <UserControl x:Class="Window023UC1"
                 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:WpfControlLibrary1"
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
      <Canvas x:Name="cv">
        <Rectangle Fill="{Binding Hintergrund}" 
                   Canvas.Top="10" Canvas.Left="10"
                   Width="20" Height="20"/>
        <Rectangle Fill="Gray" 
                   Canvas.Top="30" Canvas.Left="15"
                   Width="10" Height="50"/>
        </Canvas>
    </UserControl>

    CodeBehind UserControl:

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Public Class Window023UC1
      Implements INotifyPropertyChanged
      Public Sub New()
    
        ' This call is required by the designer.
        InitializeComponent()
    
        ' Add any initialization after the InitializeComponent() call.
        cv.DataContext = Me
      End Sub
    
    
      Public Shared ReadOnly FarbeProperty As DependencyProperty =
                DependencyProperty.RegisterAttached("Farbe", GetType(Brush),
                  GetType(Window023UC1), New PropertyMetadata(Brushes.Black, AddressOf OnFarbeChanged))
    
      Private Shared Sub OnFarbeChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
        Dim uc = TryCast(d, Window023UC1)
        If uc Is Nothing OrElse e.NewValue Is Nothing Then Exit Sub
        uc.Hintergrund = CType(e.NewValue, Brush)
        uc.OnPropChanged(NameOf(Hintergrund))
      End Sub
    
      Public Shared Function GetFarbe(obj As DependencyObject) As Brush
        Return CType(obj.GetValue(FarbeProperty), Brush)
      End Function
    
      Public Shared Sub SetFarbe(obj As DependencyObject, value As Brush)
        obj.SetValue(FarbeProperty, value)
      End Sub
    
      Public Property Hintergrund As Brush = Brushes.Yellow
    
      Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
      Private Sub OnPropChanged(<CallerMemberName> Optional propName As String = "")
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
      End Sub
    End Class

    Und das Ergebnis:


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks


    Montag, 25. Mai 2020 09:19
  • Hi Peter, cool danke. Bekomme ich das denn auch hin (wie in meinem obigen Bild) wenn die zu wechselnde Farbe irgendwie in der Grafik (verschlungen) drin ist? So ala wie im Film wenn die mit einer Greenbox arbeiten?
    Irgendwie ein Control basteln, wo die Grafik transparent ist und dein Rectangle dahinter liegt ? Denn bräuchte ich nur vom Rectangle die Farbe ändern.
    Dienstag, 26. Mai 2020 09:19
  • Hallo Peter, ich habe jetzt noch mal was mit Inkscape gezeichnet und als XAML gespeichert. In eine WPF Seite eingebunden, bekomme aber folgende Fehlermeldung beim Image

    Dieses Dateiformat wird von Blend nicht unterstützt. data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABHNCSVQICAgIfAhkiAAAAS5JREFU%20WIXtmF0OgyAMgFuzy3gV7qtH2Y7TPRg2UH7a8mvil+zFVPxSYLTgtq0ECoz5aF4T8+IGXoUIw5FI%20vDgOSCzBQ477ITcO6fjpJZOC/6xpP2Dfs1mVjxMVlGUthysqG3MJPTTmjfXkXAivazTNRbDP7uRL%20eoJ1pzXGeV2mCU5xe/hJ+An2yZ4Lbz0uACPk+AyaYks+i4MF87DP4rbEs4hEUHRW1iEuOEkGAfZ9%20DT6ffg0+gqXcRVBWAtUl80cd2z09STncZYoBxkwzs5oZSW6JLX5QzyxOXVErexJN1yVH1iMHMmgl%20W4jKG/jIFMs6Lx6624XEGqwp2eDqwx9Q+wG9mIVZsIayybl+K6/UhRX1+WotF1NOQcnfp48ZftTl%20eARLmV7wtElGlv5hviWVZQtHKICpAAAAAElFTkSuQmCC

    Was müsste ich jetzt machen ?

     

    Mittwoch, 27. Mai 2020 09:06
  • Hi Thomas,
    Inkscape kenne ich nicht.

    Lege die Zeichnung als png-Datei im Projekt als separate Datei oder Ressource ab und nutze dann diese Datei in einem Image-Steuerelement.


    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Mittwoch, 27. Mai 2020 10:28
  • Danke dir , ich frage aber trotzdem noch mal im Forum ob nicht einer dafür noch eine direkte Lösung hat , damit ich den String in der XAML benutzen kann.
    Mittwoch, 27. Mai 2020 18:12
  • Hallo Peter,


    >Private Shared Sub OnFarbeChanged(d As DependencyObject, e As >DependencyPropertyChangedEventArgs)
    >    Dim uc = TryCast(d, Window023UC1)
    >    If uc Is Nothing OrElse e.NewValue Is Nothing Then Exit Sub
    >    uc.Hintergrund = CType(e.NewValue, Brush)
    >    uc.OnPropChanged(NameOf(Hintergrund))
    >  End Sub

    reagiert ja je nach Auswahl aus deiner Liste. Wie müßte ich denn das ganze handeln wenn ich im Mainwindow einen Button habe , der bei Click die Farbe des Rectangle ändern soll ?
    Du benutzt ja aktuell auch noch nicht
    > Public Shared Sub SetFarbe(obj As DependencyObject, value As Brush)
     >   obj.SetValue(FarbeProperty, value)
    >  End Sub
    was müßte man denn hier vom Mainwindow als DependencyObject angeben?


    Freitag, 29. Mai 2020 09:41
  • Hallo Peter, ich bin es noch mal . 
    Wie würde obiges funktionieren, wenn ich das Usercontrol zur Laufzeit einer Viewbox hinzufüge (auch mehrere Unterschiedliche) ?
    kann ich mir irgendwie die Reference zum Property Hintergrund in einer Liste oder sonstigen weglegen, um zur Laufzeit die Hintergrundfarbe zu ändern?
    Dienstag, 2. Juni 2020 11:56
  • Hi Thomas,
    wenn du einen Button zur Steuerung wünschst, dann kannst du in der Methode zum Click-Ereignis einen Brush zuweisen. Das Beispiel meines UserControls hat die Eigenschaft "Farbe", der der Brush zugewiesen werden kann. Das kann man direkt im CodeBehind machen oder auch mittels Bindung, wenn die Eigenschaft "Farbe" an eine Eigenschaft im Objekt gebunden ist, welches als DataContext zugewiesen wurde. Hier mal ein Beispiel:

    XAML:

    <Window x:Class="Window023"
            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:WpfApp1.WpfApp023"
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
            mc:Ignorable="d"
            Title="Window023" Height="200" Width="200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <StackPanel>
          <Button Content="Rot" Command="{Binding Cmd}" CommandParameter="0"/>
          <Button Content="Grün" Command="{Binding Cmd}" CommandParameter="2"/>
          <uc:Window023UC1 Farbe="{Binding Zustandsfarbe}"/>
        </StackPanel>
      </Grid>
    </Window>
    

    Und der Code dazu:

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    
    Namespace WpfApp023
      Public Class ViewModel
        Implements INotifyPropertyChanged
    
        Public ReadOnly Property Farben As List(Of Farbe)
          Get
            Dim l As New List(Of Farbe)
            l.Add(New Farbe With {.Name = "rot", .FarbBrush = Brushes.Red})
            l.Add(New Farbe With {.Name = "blau", .FarbBrush = Brushes.Blue})
            l.Add(New Farbe With {.Name = "grün", .FarbBrush = Brushes.Green})
            Return l
          End Get
        End Property
    
        Public Property Zustandsfarbe As Brush = Brushes.Yellow
    
        Public ReadOnly Property Cmd As ICommand = New RelayCommand(AddressOf CmdExec)
    
        Private Sub CmdExec(obj As Object)
          Dim index = CType(obj, Integer)
          Zustandsfarbe = Farben(index).FarbBrush
          OnPropertyChanged(NameOf(Zustandsfarbe))
        End Sub
    
        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
        Private Sub OnPropertyChanged(<CallerMemberName> Optional propName As String = "")
          RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propName))
        End Sub
    
      End Class
    
      Public Class Farbe
        Public Property Name As String
        Public Property FarbBrush As Brush
      End Class
    
    End Namespace

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 6. Juni 2020 02:42
  • Hi Thomas,
    natürlich kann so etwas auch funktionieren.

    Es muss aber driftige Gründe geben, wenn das UserControl zur Laufzeit hinzugefügt werden soll, da dann Design und Logik gemischt werden und die Gestaltung, Tests und Fehlersuche erschwert werden. Üblich ist solche Arbeitsweise, wenn aus externen Quellen (z.B. Datenbanken) eine Liste von Datenobjekten geladen wird und für jedes Listenelement ein Steuerelement darzustellen ist.

    In der folgenden Demo wird aus einer Liste ("Farben") mit dem oben gezeigten Steuerelement ("Window023UC1") ein Listen-Steuerelement (ItemsControl) gefüllt.

    XAML:

    <Window x:Class="Window023"
            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:WpfApp1.WpfApp023"
            xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
            mc:Ignorable="d"
            Title="Window023" Height="200" Width="200">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <Grid>
        <ItemsControl ItemsSource="{Binding ElementListe}"/>
      </Grid>
    </Window>
    

    Dazu der Code:

    Imports System.ComponentModel
    Imports System.Runtime.CompilerServices
    Imports WpfControlLibrary1
    
    Namespace WpfApp023
      Public Class ViewModel
    
        Public Sub New()
          For i = 0 To Farben.Count - 1
            Dim uc As New Window023UC1 With {.Height = 100}
            uc.SetBinding(Window023UC1.FarbeProperty, $"Farben[{i}].FarbBrush")
            ElementListe.Add(uc)
          Next
        End Sub
    
        Public ReadOnly Property Farben As List(Of Farbe)
          Get
            Dim l As New List(Of Farbe)
            l.Add(New Farbe With {.Name = "rot", .FarbBrush = Brushes.Red})
            l.Add(New Farbe With {.Name = "blau", .FarbBrush = Brushes.Blue})
            l.Add(New Farbe With {.Name = "grün", .FarbBrush = Brushes.Green})
            Return l
          End Get
        End Property
    
        End Sub
    
      End Class
    
      Public Class Farbe
        Public Property Name As String
        Public Property FarbBrush As Brush
      End Class
    
    End Namespace

    --
    Best Regards / Viele Grüße
    Peter Fleischer (former MVP for Developer Technologies)
    Homepage, Tipps, Tricks

    Samstag, 6. Juni 2020 03:15
  • Danke
    Montag, 8. Juni 2020 04:49