Eine typische Gui-Funktionalität ist das Auswählen aus Listen, und die getroffene Auswahl soll dann im Viewmodel weiterverarbeitet werden.
Hier mal ein PictureViewer:
FileInfos werden links in einer Listbox präsentiert, und aus dem ausgewählte FileInfo wird eine ImageSource erstellt und angezeigt. Ausserdem sind Name und Größe der angewählten Datei angezeigt, aber auch die Abmaße der ImageSource (was komplizierterweise keine Eigenschaft des FileInfos ist).
Nun kann man zwar im Viewmodel eine Auflistung instanzieren, und im Xaml auch daran binden (etwa Listbox.ItemsSource an eine ObservableCollection(Of T)).
Um jedoch die getroffene Auswahl ins Viewmodel zu kommunizieren, muß man üblicherweise zusätzlich eine SelectedIndex-Property auch im Viewmodel anlegen und Listbox.SelectedIndex daran binden.
Zu sehen? Die Listbox ist zweifach gebunden: einmal die ItemsSource, und dann der SelectedIndex.
An Listbox.SelectedItem sind wiederum sind 2 Textblöcke gebunden, die Datei-Informationen anzeigen: .Name und .Length. Hierbei handelt es sich um ein ElementBinding, was auch vom Xaml-Designer recht schön unterstützt wird.
Die 3. Textbox ist an MainModel2.PixelSizeText gebunden, denn das in der Listbox angewählte FileInfo kann zwar viele Angaben zur Datei machen, aber nicht die Größe des Bildes in Pixeln.
Ebensowenig kann Image.Source an Listbox.SelectedItem gebunden werden, denn ein FileInfo ist keine ImageSource. Da muß schon das Viewmodel herkommen, und auf Abruf hin das File in ein BitmapImage laden (falls das vom Dateityp her möglich ist).
Und nur falls das möglich ist, kann auch ein PixelSizeText bereitgestellt werden, ansonsten sei er Nothing.
Also das Viewmodel:
Es sind 6 Public Properties angeboten, an die zu binden ist:
Hier mal ein PictureViewer:
FileInfos werden links in einer Listbox präsentiert, und aus dem ausgewählte FileInfo wird eine ImageSource erstellt und angezeigt. Ausserdem sind Name und Größe der angewählten Datei angezeigt, aber auch die Abmaße der ImageSource (was komplizierterweise keine Eigenschaft des FileInfos ist).
Nun kann man zwar im Viewmodel eine Auflistung instanzieren, und im Xaml auch daran binden (etwa Listbox.ItemsSource an eine ObservableCollection(Of T)).
Um jedoch die getroffene Auswahl ins Viewmodel zu kommunizieren, muß man üblicherweise zusätzlich eine SelectedIndex-Property auch im Viewmodel anlegen und Listbox.SelectedIndex daran binden.
XML-Quellcode
- <Window x:Class="MainWindow2"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:hlp="clr-namespace:System.Windows.Controls;assembly=HelpersSmallEd"
- Title="MainWindow2" Height="350" Width="525"
- xmlns:my="clr-namespace:PictureViewer"
- DataContext="{StaticResource MainModel2}" FontSize="14">
- <hlp:GridEx>
- <hlp:GridEx.RowDefinitions>
- <RowDefinition Height="Auto" MinHeight="10" />
- <RowDefinition MinHeight="10" />
- </hlp:GridEx.RowDefinitions>
- <hlp:GridEx.ColumnDefinitions>
- <ColumnDefinition MinWidth="10" Width="Auto" />
- <ColumnDefinition MinWidth="10" />
- </hlp:GridEx.ColumnDefinitions>
- <Menu FontSize="14">
- <MenuItem Header="Re_Load" Command="{Binding Path=Reload}" />
- <MenuItem Header="_Remove" Command="{Binding Path=DeleteCurrent}" />
- </Menu>
- <ListBox x:Name="lstFiles" hlp:GridEx.Range="a2" DisplayMemberPath="Name"
- ItemsSource="{Binding Path=Files}" SelectedIndex="{Binding Path=SelectedIndex}" />
- <StackPanel hlp:GridEx.Range="b1" Orientation="Horizontal" >
- <TextBlock Text="{Binding ElementName=lstFiles, Path=SelectedItem.Name}" Margin="6,0" />
- <TextBlock Text="{Binding ElementName=lstFiles, Path=SelectedItem.Length, StringFormat=(\{0\} Bytes)}"/>
- <TextBlock Text="{Binding Path=PixelSizeText}" Margin="6,0" />
- </StackPanel>
- <Image hlp:GridEx.Range="b2" Source="{Binding Path=CurrentImage}" />
- </hlp:GridEx>
- </Window>
An Listbox.SelectedItem sind wiederum sind 2 Textblöcke gebunden, die Datei-Informationen anzeigen: .Name und .Length. Hierbei handelt es sich um ein ElementBinding, was auch vom Xaml-Designer recht schön unterstützt wird.
Die 3. Textbox ist an MainModel2.PixelSizeText gebunden, denn das in der Listbox angewählte FileInfo kann zwar viele Angaben zur Datei machen, aber nicht die Größe des Bildes in Pixeln.
Ebensowenig kann Image.Source an Listbox.SelectedItem gebunden werden, denn ein FileInfo ist keine ImageSource. Da muß schon das Viewmodel herkommen, und auf Abruf hin das File in ein BitmapImage laden (falls das vom Dateityp her möglich ist).
Und nur falls das möglich ist, kann auch ein PixelSizeText bereitgestellt werden, ansonsten sei er Nothing.
Also das Viewmodel:
VB.NET-Quellcode
- Imports System.IO
- Imports System.Windows.Media.Imaging
- Imports System.Collections.ObjectModel
- Imports Microsoft.VisualBasic
- Public Class MainModel2 : Inherits MainModelBase(Of MainModel2)
- Private _DataDir As New DirectoryInfo("..\..\Data")
- Public Property Files As New ObservableCollection(Of FileInfo) From {New FileInfo("c:\Dummi.vb")}
- Public Property Reload As New RelayCommand(Sub()
- Files.Clear()
- _DataDir.GetFiles().ForEach(AddressOf Files.Add)
- SelectedIndex = If(Files.Count > 0, 0, -1)
- End Sub)
- ''' <summary>only deletes from viewed collection - not from FileSystem</summary>
- Public Property DeleteCurrent As New RelayCommand( _
- Sub() Files.RemoveAt(_SelectedIndex), _
- Function() _SelectedIndex >= 0)
- Public ReadOnly Property PixelSizeText() As String
- Get
- With _CurrentImage
- Return If(.Null, "<kein Bild>", String.Format("{{ {0} * {1} Pixel }}", .PixelWidth, .PixelHeight))
- End With
- End Get
- End Property
- Private _CurrentImage As BitmapImage = Nothing
- Public Property CurrentImage() As BitmapImage
- Get
- Return _CurrentImage
- End Get
- Private Set(ByVal value As BitmapImage)
- If ChangePropIfDifferent(value, "CurrentImage", _CurrentImage) Then RaisePropChanged("PixelSizeText")
- End Set
- End Property
- Private _SelectedIndex As Integer = -1
- Public Property SelectedIndex() As Integer
- Get
- Return _SelectedIndex
- End Get
- Set(ByVal value As Integer)
- If _SelectedIndex = value Then Return
- _SelectedIndex = value
- If _SelectedIndex < 0 Then
- CurrentImage = Nothing
- Else
- Try
- CurrentImage = New BitmapImage(New Uri(Files(_SelectedIndex).FullName))
- Catch ex As Exception
- CurrentImage = Nothing
- End Try
- End If
- RaisePropChanged("SelectedIndex")
- End Set
- End Property
- End Class
- Public Property Files - eine ObservableCollection, die zunächst mit einem nicht existenten FileInfo gefüttert wird - solche Voreinstellungen sind angenehm im Xaml-Designer
- Public Property Reload - ein Command zum Befüllen von Files
- Public Property DeleteCurrent - ein Command zum Löschen des aktuell gewählten Files
- Public ReadOnly Property PixelSizeText() As String - gibt die BildAbmaße als String formatiert aus
- Public Property CurrentImage() As BitmapImage - die Bitmap wird immer neu eingelesen, wenn SelectedIndex sich ändert. Keinesfalls sollen alle Bilder von vornherein eingelesen werden - das würde bei großen Ordnern in eine lang andauernde Operation ausarten, und enorm Speicher verbrauchen.
- Public Property SelectedIndex() As Integer - im Setter dieser Property wird das CurrentImage gleich mit-gesetzt (zeilen #50, #53, #55).
Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „ErfinderDesRades“ ()