2012-05-17 32 views
14

entiendo que ViewModel no debe tener ningún conocimiento de vista, pero ¿cómo puedo llamar MediaElement.Play() método del modelo de vista, aparte de tener una referencia para ver (o directamente a MediaElement) en ViewModel?
Otra pregunta (vinculada): ¿cómo puedo administrar la visibilidad de los controles de View desde ViewModel sin violar el patrón de MVVM?MVVM patrón de violación: MediaElement.Play()

+0

la cuestión vinculada no está allí .. :( – rydev

Respuesta

22

1) No llame Play() del modelo de vista. Generar un evento en el modelo de vista en su lugar (por ejemplo PlayRequested) y escuchar a este evento en la vista:

vista del modelo:

public event EventHandler PlayRequested; 
... 
if (this.PlayRequested != null) 
{ 
    this.PlayRequested(this, EventArgs.Empty); 
} 

vista:

ViewModel vm = new ViewModel(); 
this.DataContext = vm; 
vm.PlayRequested += (sender, e) => 
{ 
    this.myMediaElement.Play(); 
}; 

2) puede quedar expuesto en la vista, modele una propiedad booleana pública y vincule la propiedad Visibility de sus controles a esta propiedad. Como Visibility es del tipo Visibility y no bool, tendrá que usar un convertidor.

se puede encontrar una implementación básica de un convertidor de este tipo here. Este related question podría ayudarlo también.

+0

Muchas gracias PD: no hay necesidad de convertidor si exponer una propiedad de visibilidad en lugar de un bool uno – italianogrosso

+2

Mejor uso bool con convertidor – Zabavsky

+2

@italianogrosso de qué. :) Pero no debe exponer una propiedad de tipo 'Visibilidad'. Esta enumeración se encuentra en el espacio de nombres 'System.Windows', que, como dice el espacio de nombre, significa que está puramente relacionado con el lado de la vista de su aplicación. En realidad, incluso si requiere más código, es mejor exponer un booleano que no esté relacionado con la vista en absoluto. – ken2k

4

utilizo elemento multimedia para reproducir sonidos en la interfaz de usuario cada vez que se produce un evento en la aplicación. El modelo de vista que lo maneja se creó con una propiedad Source de tipo Uri (con notificación de propiedad modificada, pero usted ya sabe que necesita eso para notificar a UI).

Todo lo que tiene que hacer cada vez que cambia la fuente (y depende de usted), es establecer la propiedad fuente en nulo (esta es la razón por la cual la propiedad Fuente debe ser Uri y no cadena, MediaElement lanzará naturalmente excepción, NotSupportedException I pensar), luego configúralo en el URI que quieras.

Probablemente, el aspecto más importante de este consejo es que usted tiene que establecer la propiedad de LoadedBehaviour MediaElement jugar en XAML de la vista. Es de esperar que no se necesite código detrás de lo que desea lograr.

El truco es sumamente sencilla por lo que no voy a poner un ejemplo completo. La función de reproducción del modelo de vista debería tener este aspecto:

private void PlaySomething(string fileUri) 
    { 
     if (string.IsNullOrWhiteSpace(fileUri)) 
      return; 
     // HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI. 
     this.Source = null; 
     this.Source = new Uri(fileUri); 
    } 

Aquí es la propiedad Source, nada de especial:

#region Source property 

    /// <summary> 
    /// Stores Source value. 
    /// </summary> 
    private Uri _Source = null; 

    /// <summary> 
    /// Gets or sets file URI to play. 
    /// </summary> 
    public Uri Source 
    { 
     get { return this._Source; } 
     private set 
     { 
      if (this._Source != value) 
      { 
       this._Source = value; 
       this.RaisePropertyChanged("Source"); 
      } 
     } 
    } 

    #endregion Source property 

En cuanto a visibilidad, y cosas como esta, puede utilizar convertidores (por ejemplo, de bool a visibilidad, que puede encontrar en CodePlex para WPF, SL, WP7,8) y enlazar la propiedad de su control con la de los modelos de vista (p. ej., IsVisible). De esta manera, usted controla partes del aspecto de su vista. O puede simplemente hacer que la propiedad de Visibilidad escriba System.Windows.Visibility en su modelo de vista (no veo ningún incumplimiento de patrón aquí). Realmente, no es tan raro.

Buena suerte,

Andrei

P. S. Debo mencionar que .NET 4.5 es la versión donde probé esto, pero creo que debería funcionar en otras versiones también.

8

Para todos los recién llegados,

Hay muchas maneras de lograr el mismo resultado y lo que realmente depende de lo que le gustaría poner en práctica la tuya, siempre y cuando el código no es difícil de mantener, lo hago cree que está bien romper el patrón MVVM en ciertos casos.

Pero una vez dicho esto, también creo que siempre hay forma de hacerlo dentro del patrón, y el siguiente es uno de ellos en caso de que alguien quisiera saber qué otras alternativas hay disponibles.

Las Tareas:

  1. no queremos tener referencia directa desde el modelo de vista de los elementos de interfaz de usuario, es decir, la MediaElement y el propio punto de vista.
  2. que quiere usar el control para hacer la magia aquí

La solución:

En resumen, vamos a introducir una interfaz entre la vista y el modelo de vista para romper el dependecy , y View implementará la interfaz y será responsable del control directo de MediaElement mientras deja ViewModel hablando solo a la interfaz, que puede intercambiarse con otras implementaciones para fines de prueba si es necesario, y aquí viene la versión larga:

  1. introducen una interfaz llamada IMediaService de la siguiente manera:

    public interface IMediaService 
    { 
        void Play(); 
        void Pause(); 
        void Stop(); 
        void Rewind(); 
        void FastForward(); 
    } 
    
  2. implementar el IMediaService en la vista:

    public partial class DemoView : UserControl, IMediaService 
    { 
        public DemoView() 
        { 
         InitializeComponent(); 
        } 
    
        void IMediaService.FastForward() 
        { 
         this.MediaPlayer.Position += TimeSpan.FromSeconds(10); 
        } 
    
        void IMediaService.Pause() 
        { 
         this.MediaPlayer.Pause(); 
        } 
    
        void IMediaService.Play() 
        { 
         this.MediaPlayer.Play(); 
        } 
    
        void IMediaService.Rewind() 
        { 
         this.MediaPlayer.Position -= TimeSpan.FromSeconds(10); 
        } 
    
        void IMediaService.Stop() 
        { 
         this.MediaPlayer.Stop(); 
        } 
    } 
    
  3. que a continuación hacemos algunas cosas en el DemoView.XAML:

    • Dale MediaElement un nombre por lo que el código subyacente puede acceder a ella como arriba:
    <MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/> 
    
    • Dar a la vista un nombre por lo que puede pasar como una parámetro, y
    • importar el espacio de nombres de interactividad para su uso posterior (algunos espacios de nombres predeterminados se omiten por razones de simplicidad):
    <UserControl x:Class="Test.DemoView" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity" 
        x:Name="MediaService"> 
    
    • Conexión para el caso de carga a través de disparo para pasar la vista en sí al modelo de vista a través de un comando
    <ia:Interaction.Triggers> 
         <ia:EventTrigger EventName="Loaded"> 
          <ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction> 
         </ia:EventTrigger> 
        </ia:Interaction.Triggers> 
    
    • por último pero no menos, necesitamos ho Okup los controles de medios a través de los comandos:
    <Button Command="{Binding PlayCommand}" Content="Play"></Button> 
        <Button Command="{Binding PauseCommand}" Content="Pause"></Button> 
        <Button Command="{Binding StopCommand}" Content="Stop"></Button> 
        <Button Command="{Binding RewindCommand}" Content="Rewind"></Button> 
        <Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button> 
    
  4. Ahora podemos coger todo en el modelo de vista (estoy usando DelegateCommand del prisma aquí):

    public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest 
    { 
        public IMediaService {get; private set;} 
    
        private DelegateCommand<IMediaService> loadedCommand; 
        public DelegateCommand<IMediaService> LoadedCommand 
        { 
         get 
         { 
          if (this.loadedCommand == null) 
          { 
           this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) => 
           { 
            this.MediaService = mediaService; 
           }); 
          } 
          return loadedCommand; 
         } 
        } 
        private DelegateCommand playCommand; 
        public DelegateCommand PlayCommand 
        { 
         get 
         { 
          if (this.playCommand == null) 
          { 
           this.playCommand = new DelegateCommand(() => 
           { 
            this.MediaService.Play(); 
           }); 
          } 
          return playCommand; 
         } 
        } 
    
        . 
        . // other commands are not listed, but you get the idea 
        . 
    } 
    

Side nota: utilizo la función de cableado automático de Prism para vincular la vista y el modelo de vista. Por lo tanto, en el código de la vista detrás del archivo no hay un código de asignación de DataContext, y prefiero mantenerlo así, y por lo tanto, elegí usar comandos puramente para lograr este resultado.