Ich arbeite zur Zeit an einer etwas größeren WPF Anwendung und merke dabei dass ich doch so einiges noch nicht wirklich richtig anwenden kann.
Mein Szenario ist folgendes:
Meine View hat das ViewModel als DataContext gesetzt.
In der View befindet sich ein Slider.
Das Model hat 2 Properties vom Typ long welche die Werte des Sliders bestimmen sollen. Im Endeffekt handelt es sich um einen Musikplayer und der Slider soll die aktuelle Spielzeit des Songs anzeigen und manipulieren können. Die beiden Properties setzen also Maximum und Position des Sliders. Ein Timer sorgt dafür dass in einem bestimmten Intervall die Positionsproperty neu gesetzt wird.
Jetzt müssen diese Daten irgendwie an die View geliefert werden. Weiterhin soll es möglich sein den Slider mit der Maus zu manipulieren und so die Position des Songs zu ändern. Mein aktueller Ansatz funktioniert zwar, dabei baue ich mir aber einen Kreislauf und dadurch wird das ganze langsam und ruckelig. Aufwendig und unschön ist das ganze auch. Die Länge des Songs, also die zweite Property lasse ich erst mal außen vor. Hier geht das Binding nur in eine Richtung. Das läuft auch soweit. So nun zu meinem bisherigen Vorgehen:
In der View binde ich die Position des Sliders an eine DependencyProperty des ViewModels. Das Model implementiert INotifyPropertyChangend. In einem Zeitintervall wird die aktuelle Position im Song geholt und die Property im Model dafür aktualisiert. Sobald die Property geändert wird wird das Event gefeuert. Das ViewModel empfängt das Event und passt die DependencyProperty an.
Bis jetzt klappt noch alles. Ist zwar etwas aufwendig, aber es klappt. Nun geht es um die andere Richtung. Ich möchte den Slider verschieben und so die Position des Songs manipulieren. Dafür bekommt die DependencyProperty Metadaten zugewiesen. Dort setze ich ein PropertyChangedCallback. Sobald die DependencyProperty geändert wird rufe ich auf dem Model eine Funktion auf die die aktuelle Position im Song selbst verschiebt.
Hier kommt es nun zum Problem. In beide Richtungen beeinflusse ich die DependencyProperty und dadurch wird diese Callback Funktion aufgerufen. Jetzt kommt es zum Kreislauf.
XAML Code für den Slider in meiner View:
|
Quellcode
|
1
|
<Slider DockPanel.Dock="Top" Value="{Binding CurrentTime}" Maximum="{Binding CurrentChannelLength}" />
|
ViewModel auf das wichtigste reduziert:
|
C#-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
public class PlayerViewModel:DependencyObject
{
//...
private AudioPlayer player;
public AudioPlayer Player { get { return player; } }
public PlayerViewModel()
{
player = new AudioPlayer();
player.PropertyChanged += player_PropertyChanged;
//...
}
void player_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("CurrentChannelPosition"))
{
CurrentTime = player.CurrentChannelPosition;
}
else if(e.PropertyName.Equals("CurrentChannelLength"))
{
CurrentChannelLength = player.CurrentChannelLength;
}
}
public static readonly DependencyProperty CurrentTimeProperty = DependencyProperty.Register("CurrentTime",
typeof(long), typeof(PlayerViewModel),
new PropertyMetadata(setCurrentTime));
private static void setCurrentTime(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as PlayerViewModel).player.SetChannelPosition((long)e.NewValue);
}
public long CurrentTime
{
get { return (long)GetValue(CurrentTimeProperty); }
set { SetValue(CurrentTimeProperty, value); }
}
public static readonly DependencyProperty CurrentChannelLengthProperty = DependencyProperty.Register("CurrentChannelLength", typeof(long), typeof(PlayerViewModel));
public long CurrentChannelLength
{
get { return (long)GetValue(CurrentChannelLengthProperty); }
set { SetValue(CurrentChannelLengthProperty, value); }
}
//...
}
|
Model auf das wesentliche reduziert:
|
C#-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
public class AudioPlayer:INotifyPropertyChanged
{
//....
private DispatcherTimer positionTimer;
private long currentChannelPosition;
private long currentChannelLength;
public long CurrentChannelPosition { get { return currentChannelPosition; } set { currentChannelPosition = value; NotifyPropertyChanged("CurrentChannelPosition"); } }
public long CurrentChannelLength { get { return currentChannelLength; } set { currentChannelLength = value; NotifyPropertyChanged("CurrentChannelLength"); } }
//...
private int currentStreamHandle;
public int CurrentStreamHandle
{
get { return currentStreamHandle; }
set { currentStreamHandle = value; }
}
public AudioPlayer()
{
positionTimer = new DispatcherTimer(DispatcherPriority.ApplicationIdle);
positionTimer.Interval = TimeSpan.FromMilliseconds(100);
positionTimer.Tick += positionTimerTick;
positionTimer.IsEnabled = true;
//...
}
//...
public void SetChannelPosition(long position)
{
CurrentChannelPosition = Math.Max(0, Math.Min(position, CurrentChannelLength));
Bass.BASS_ChannelSetPosition(CurrentStreamHandle, Math.Max(0, Math.Min(position, CurrentChannelLength)));
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
|
Beim laden des Songs wird die Länge des Songs festgelegt. Wie gesagt die Lösung ist alles andere als sauber und auch nicht wirklich gut. An sich sollte das doch eigentlich ein recht normales Problem sein. Deshalb hoffe ich, dass ihr mich auf den richtigen Weg leiten könnt
![;)](wcf/images/smilies/wink.png.pagespeed.ce.L9LRa_F2a5.png)
Schönen Abend noch.