Image Binding und Source

  • WPF

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    Image Binding und Source

    Hallo zusammen,

    ich versuche in ein Image zu Laden was auch teilweise funktioniert.
    Das Problem ist , das Icon wird nur zur Laufzeit angezeigt.
    Warum kann ich es zur Entwurfszeit nicht sehen?
    Liegt das an der Pfad angabe?
    Hier ist meine Klasse und mein XAML.
    Danke schonmal

    C#-Quellcode

    1. public class MasterMenueViewModel : ViewModelBase
    2. {
    3. private ObservableCollection<MasterMenueItem> masterMenueItems;
    4. public ObservableCollection<MasterMenueItem> MasterMenueItems
    5. {
    6. get => masterMenueItems;
    7. set
    8. {
    9. masterMenueItems = value;
    10. OnPropertyChanged();
    11. }
    12. }
    13. public MasterMenueViewModel()
    14. {
    15. MasterMenueItems = new ObservableCollection<MasterMenueItem>
    16. {
    17. new MasterMenueItem("Home","/Images/home_32.png"),
    18. new MasterMenueItem("Status","/Images/status_32.png")
    19. };
    20. }
    21. }
    22. public class MasterMenueItem
    23. {
    24. public string Category { get; set; }
    25. public string ImagePath { get; set; }
    26. public MasterMenueItem(string text, string imagePath)
    27. {
    28. Category = text;
    29. ImagePath = imagePath;
    30. }
    31. }


    XML-Quellcode

    1. <ItemsControl ItemsSource="{Binding MasterMenueItems}" Grid.Column="0">
    2. <ItemsControl.ItemTemplate>
    3. <DataTemplate>
    4. <StackPanel Margin="10">
    5. <Image Source="{Binding ImagePath}" Width="32"/>
    6. </StackPanel>
    7. </DataTemplate>
    8. </ItemsControl.ItemTemplate>
    9. </ItemsControl>
    Hallo Akanel,
    Ich glaub nicht das es daran liegt.
    Das Property 'Category' wird ja angezeigt.
    Das Problem ist das Image bzw. der Image Pfad.
    Vielleicht liegt es an der PNG ?
    Wenn ich den Pfad direkt in XAML an die Source der Image übergebe wird
    das Bild zur Entwufszeit angezeigt. Nur beim Binden nicht.
    Wie macht ihr das mit den Icons in der View.
    Ich möchte ein <ItemsControl.ItemTemplate> mit eine Liste aus dem ViewModel füllen.
    Die Liste sollte eine String Eigenschaft und ein Icon beinhalten.
    Sowas wie List(of ContentItem).

    VB.NET-Quellcode

    1. Class ContentItem
    2. Prop String...
    3. Prop Icon...
    4. end Class
    Wie macht ihr das mit den Icons in der View.
    Ich hab in meine Viewmodels immer eine Logik drin, die das Viewmodel zur Designzeit mit DummiDaten befüllt.
    Auch ObservableCollection(Of BitMapSource) tu ich da befüllen, und den BitmapSources werden packedUris übergeben.
    Ich hab da aber Hilfsklassen für gebastelt, die mir eine ganze ObservableCollection(Of BitmapSource) generieren. Ich muss da nur den Resourcen-Ordner angeben - die packedUris bastelt sich das Teil selbst.
    Morgen EDR,
    das hört sich gut an.
    Hab festgestellt das wenn ich den kompletten Pfad angebe, fuktioniert es im Designer.
    Deshalb glaub ich das dein Ansatz mit der Pack Uri, die lösung des Problems ist.
    Die Idee mit der Hilfsklasse find ich auch gut.
    Wenn ich keine andere Lösung finde wird es in die richtung gehen.
    Hab jetzt erstmal im View eine FallbackValue erstellt, die ein Image anzeigt.
    Danke für die Info!
    jetzt habich nochma nachgeguckt und angehängt.
    Da habich aber wieder was anneres gemacht, da gehts nämlich um ein Menü, was aussm Viewmodel zu konfigurieren ist:

    VB.NET-Quellcode

    1. Public Class Mainmodel : Inherits MainModelBase(Of Mainmodel)
    2. '...
    3. Private _Load As String = "_Load", _Save As String = "_Save", _Add As String = "_Add", _Remove As String = "_Remove", _Clear As String = "_Clear"
    4. Public Property Menu As New CommandMenu(AddressOf Execute, AddressOf CanExecute) From {{_Load, "ICO4161.ico"}, {_Save, "Error.ico"}, _Add, _Remove, _Clear}
    Wie man sieht (hofflich) kann man Betextung+Icon angeben oder nur einen Betextung.
    (Mein CommandMenü leitet alle Klickse auf nur eine Execute-Methode, die dann die Betextung auswertet)
    Dann passieren geheimnisvolle Dinge und irgendwo im CommandMenü ist diese Bastel-Methode, die die Icon-Namen in packed-Uris übersetzt:

    VB.NET-Quellcode

    1. Public Class CommandMenu : Inherits ObservableCollection(Of CommandItem)
    2. Private _Command As RelayCommand(Of String)
    3. Private _UriRoot As String = Nothing
    4. Public IconWidth As Integer = 16
    5. '...
    6. Private Function UriRoot() As String
    7. If _UriRoot.Null Then
    8. Dim thisAssembly = Reflection.Assembly.GetCallingAssembly.FullName.LeftCut(",")
    9. _UriRoot = "pack://application:,,,/".And(thisAssembly, ";component/Resources/")
    10. End If
    11. Return _UriRoot
    12. End Function

    Das Binding von Command, Text, Icon geht dann über ein DataTemplate:

    XML-Quellcode

    1. DataContext="{Binding Mode=OneWay, Source={StaticResource Mainmodel}}">
    2. <StackPanel>
    3. <ItemsControl Margin="2" ItemsSource="{Binding Menu}">
    4. <FrameworkElement.Resources>
    5. </FrameworkElement.Resources>
    6. <ItemsControl.ItemTemplate>
    7. <DataTemplate DataType="{x:Type my:CommandItem}">
    8. <Button Command="{Binding Command, Mode=OneWay}" CommandParameter="{Binding Text}">
    9. <StackPanel Orientation="Horizontal">
    10. <Image Source="{Binding ImageSource}" Width="{Binding ImageWidth, Mode=OneTime}"/>
    11. <ContentPresenter Margin="2" Content="{Binding Text}" RecognizesAccessKey="True" VerticalAlignment="Center"/>
    12. </StackPanel>
    13. </Button>
    14. <DataTemplate.Triggers>
    15. <DataTrigger Binding="{Binding ImageSource}" Value="{x:Null}">
    16. <Setter Property="Content" Value="{Binding Text}"/>
    17. </DataTrigger>
    18. </DataTemplate.Triggers>
    19. </DataTemplate>
    20. </ItemsControl.ItemTemplate>
    21. <ItemsControl.ItemsPanel>
    22. <ItemsPanelTemplate>
    23. <UniformGrid/>
    24. </ItemsPanelTemplate>
    25. </ItemsControl.ItemsPanel>
    26. </ItemsControl>
    Dassis jetzt ein UniformGrid mit viele Buttons drauf.

    Eiglich ists aber für richtige Menüs designed:

    XML-Quellcode

    1. <Menu Margin="2" ItemsSource="{Binding Menu}">
    2. <ItemsControl.ItemContainerStyle>
    3. <Style TargetType="MenuItem">
    4. <Style.Setters>
    5. <Setter Property="Header" Value="{Binding Text}"/>
    6. <Setter Property="Command" Value="{Binding Command}"/>
    7. <Setter Property="CommandParameter" Value="{Binding Text}"/>
    8. <Setter Property="Icon" Value="{Binding Image,Mode=OneTime}"/>
    9. </Style.Setters>
    10. </Style>
    11. </ItemsControl.ItemContainerStyle>
    12. </Menu>


    Jo, hoffe, du kannst iwas nützliches daraus entnehmen.


    Dateien

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Hallo Eder,

    Danke für das ViewModelCommanding.
    Das eine oder andere werde ich bestimmt brauchen.


    Die PackUri war der richtige Hinweis.
    Die Lösung für mich ist die folgende:

    XML-Quellcode

    1. "/DerNameMeinerApp;component/Images/home_32.png"

    Das funktionier bisher ganz gut.
    Auch mit DesigneTime-Support ohne Datacontext.

    Denkst du das es eine gute Idee ist, der Liste noch das ViewModel mit zugeben?
    Man könnte Später im Items.Control einfach die SelectedItem nehmen, um das View anzuzeigen.
    Natürtlich mit einem DataTemplate im App.xaml oder Window, das als Content die View hat.
    Hallo Erfinder,

    hier eine kleine Solution als Beispiel.

    Die ListBox und das SelectedItem müsste man anders machen.
    Aber es geht eher Darum die View mit den ViewModel zu verbinden.
    Wobei mich hier stört das ich das ViewModel als Objekt übergeben muss .

    C#-Quellcode

    1. new MasterItem("Home","/MasterDetail;component/Images/home.png",new HomeViewModel()),


    Das würde bei grösseren Projekt ziemlich viel Speicher verbrauchen.Besonders wenn man
    viele Views/ViewModels hat,die auch noch Daten laden. Aus ne Datenbank zum Beispiel
    Dateien
    • MasterDetail.zip

      (39,33 kB, 58 mal heruntergeladen, zuletzt: )
    ich hab da nu reingeguckt, und finds eiglich ok, wie's gemacht ist.
    Ich weiss blos immer nicht, wann und warum etwas ein Model ist und wann ein Viewmodel.
    Imo wäre MasterItem auch viewmodel - aber die Frage ist ja hier nicht Thema.

    Amro schrieb:

    Wobei mich hier stört das ich das ViewModel als Objekt übergeben muss .
    Wie soll das anders gehen?
    Das Viewmodel muss ja iwie zu sehen sein, sonst kann mans nicht anklicken.
    Wenn enthaltene Datenmengen ein Problem sind, dann sollte man zusehen, dass man die ViewModelse bei Anwahl befüllt und bei Abwahl entleert, vielleicht.
    Danke fürs reinschauen erstmal.

    ErfinderDesRades schrieb:

    Ich weiss blos immer nicht, wann und warum etwas ein Model ist und wann ein Viewmodel.

    Das stimmt , bin da auch noch immer unsicher.
    In dem Fall hab ich ein Model genommen weil das MasterItem nur Eigenschaften
    benötigt und keine Logik in dem Sinne.
    Ich dachte auch das es besser ist, für jede View nur ein ViewModel zu haben.
    Ich lass mich auch eines besser belehren, falls ich falsch liege :).

    Hab jetzt herausgefunden das in so Fälle das Midiator-Pattern eingesetzt wird.
    Das schau ich mir die Tage mal an.
    Vielleicht ist das tatsächlich die bessere Lösung, obwohl meiner Meinung nach
    wieder viel zu Komplex.
    Hallo

    Da du mich in einem anderen Thread gebeten hast hier reinzusehen bin ich nun auch hier ;)

    Ich habe mir das angesehen und muss sagen, ich finde du hast alles richtig gemacht. Passt du gut so.

    Amro schrieb:

    Die ListBox und das SelectedItem müsste man anders machen.

    Warum? Passt doch so.

    Amro schrieb:

    Wobei mich hier stört das ich das ViewModel als Objekt übergeben muss .

    Naja, im Grunde hast du es ja richtig gemacht. Es kann hier jede Klasse reingereicht werden welche von ViewModelBase erbt. Ich habe dir im Beispiel eine neue Klasse "ChildViewBase" erstellt welche die Eigenschaft "HeaderText" besitzt. Nun kann nur noch jede Klasse welche von ChildViewBase erbt reingereicht werden und du hast etwas mehr Kontrolle über dich selbst ;)
    Und da beide Views eine Header-Eigenschaft hatten macht es ja sinn eine gemeinsame Basisklasse zu besitzen.

    Amro schrieb:

    Das würde bei grösseren Projekt ziemlich viel Speicher verbrauchen

    Verstehe dein Problem.
    Du kannst ja in die ChildViewBase eine Eigenschaft packen (z.b. "Content"). Da ist dann die Eigentliche View enthalten, diese implementiert IDisposable. In diese Methode räumst du auf. Leerst listen, gibst Objekte frei, was auch immer.
    Beim umschalten der View. (SelectedItem) stellt du sicher das Dispose() aufgerufen wird.

    Ich hab dir mal ein Beispiel reingepackt.

    Grüße
    Sascha
    Dateien
    • MasterDetail.zip

      (43,58 kB, 57 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. ##

    Danke das du dir die Zeit genommen hast rein zu schauen.
    Das mit dem ChildViewBase gefällt mir.
    Das ganze soll nachher ein Sidemenu werden.
    Es wird warscheinlich nicht immer gewollt sein das ViewModel zu Disposen.
    Manchmal will ich auch hin und her schalten.
    Das ChildViewBase hat mich jetzt auf die Idee gebracht das ganze zu verallgemeinern.
    Ich denke an eine Klasse mit der ich BarMenüs , SideMenüs und vielleicht auch Buttons Binden kann.
    Das Objekt bekommt dann ViewModel, Header und Image per Dependency Injection und soll sich selbst darum kümmern wann Dispost wird und so.
    Ich hoffe damit die RelayCommands zu umgehen zumindest für die Menüs und Buttons.
    Ich hab mich etwas mit den Midiator-Pattern beschäftig und denke das es in der richtung geht.
    Das ist auch das empfohlene Pattern dafür wie mir bekannt ist.
    Ich schau mal ob ich das hinbekomme.
    Was meinst du dazu?
    Ach ,da ich noch am Anfang stehe muss ich aufpassen das es nicht zu tief verschachtel ist hinsichtlich Interfaces und Veerbung. Da verliere ich schnell die Übersicht :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Amro“ ()

    Hallo

    Wie du zum Schluss schreibst, aufpassen das es nicht zu kompliziert wird.

    Ich würds wirklich mit nem einfachen VM Desogn aufbauen. Ich würde auch jede Klasse für sich entscheiden lassen was sie Disposed.
    Du rufst Dispose ja auf, ob es nun notwendig ist etwas zu bereinigen entscheidet die Klasse. Ist die Methode leer ists auch gut. Halte es simple und einfach.

    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. ##

    Nofear23m schrieb:

    Ich würds wirklich mit nem einfachen VM Desogn aufbauen.

    Ja das glaub ich auch, sonst kommt wieder der Frust ;( :D

    Hab noch eine Frage zu ChildViewBase.
    Wäre ChildViewModelBase vielleicht zutreffender .
    Das View in der Benamung verwirrt etwas.

    Den Header und den Content hab ich jetzt zweimal drin.
    Einmal im ChildViewBase und im Model MasterItem.
    Wird das so gemacht?
    Fühlt sich falsch (redundant) an.
    Hy

    Klar, benenne das ruig um. Ich hab das Beispiel nur recht schnell abgeändert.

    Was die "doppelten" Eigenschaften angeht. Das Model hab ich mir garnicht so richtig angesehen. Aber..... Du wirst es SEHR oft haben das du im Model und im ViewModel die selben Eigenschaften hast.
    Das kommt einem Anfangs redundant vor und ist es im Grunde auch. Der Unterschied. Im Model haste nur die Eigenschaften in der kurzen Schreibweise. Im ViewModel hast du Sie mit Getter und Setter damit due "PropertieChanged" werfen kannst. Das Model ist ja die Datengrundlage, also genau SO wie es gespeichert wird. Ich weis nicht wie und auf welchem Weg du speichern willst. Je nachdem sieht das Model fast gleich wie das ViewModel aus, oder überhaupt nicht. In manchen Fällen brauchst du auch garkein Model. 8|
    Ja, auch das gibt es.

    Grob: Das Model "Modeliert" (ja, da ist ne ähnlichkeit) dein Datenschema wenn man so will.
    Beispiel wäre ne Datenbank. Jede Modelklasse ist eine Tabelle, Jede Eigenschaft ist eine Spalte.

    Gerne können wir ein Beispiel durchgehen aber die Kapitel für MVVM sind gerade in Arbeit und kommen nun endlich nach und nach. Auf Feedback würde ich mich übrigens freuen, du kannst gerne hier Beteiligung zeigen damit ich weis das ich auf solche Dinge eben genauer Eingehen muss da dies anscheinend für Verwirrung sorgt.

    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. ##