Benutzer mit den meisten Antworten
Wie kann ich folgendes mit WPF ? realisieren?

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?
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- Als Antwort markiert Thomas Klinger2 Montag, 8. Juni 2020 04:49
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 -
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ß
-
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
- Bearbeitet Peter Fleischer Montag, 25. Mai 2020 10:14
-
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. -
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 ?
-
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 -
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 Subreagiert 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 Subwas müßte man denn hier vom Mainwindow als DependencyObject angeben?- Bearbeitet Thomas Klinger2 Freitag, 29. Mai 2020 09:42
-
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? -
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 -
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- Als Antwort markiert Thomas Klinger2 Montag, 8. Juni 2020 04:49