2012-05-17 28 views
14

Tôi hiểu rằng ViewModel không có bất kỳ kiến ​​thức nào về Chế độ xem, nhưng làm cách nào tôi có thể gọi phương thức MediaElement.Play() từ ViewModel, ngoài việc tham chiếu đến Chế độ xem (hoặc trực tiếp đến MediaElement) trong ViewModel?
Câu hỏi khác (được liên kết): làm cách nào để quản lý chế độ hiển thị của Chế độ xem của Chế độ xem từ ViewModel mà không vi phạm mẫu MVVM?Vi phạm mẫu MVVM: MediaElement.Play()

+0

các câu hỏi liên quan là không có .. :( – rydev

Trả lời

22

1) Không gọi Play() từ kiểu xem. Nâng cao một sự kiện trong mô hình điểm để thay thế (ví dụ PlayRequested) và lắng nghe sự kiện này trong giao diện:

xem mô hình:

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

xem:

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

2) Bạn có thể phơi bày trong mô hình chế độ xem thuộc tính boolean công khai và ràng buộc thuộc tính Visibility của các điều khiển của bạn với thuộc tính này. Vì Visibility thuộc loại Visibility và không phải là bool, bạn sẽ phải sử dụng trình chuyển đổi.

Bạn có thể tìm thấy triển khai cơ bản về công cụ chuyển đổi như vậy here. Điều này related question cũng có thể giúp bạn.

+0

Thank you very much Ps: không cần phải chuyển đổi nếu tôi hiển thị một tài sản Visibility thay vì một bool một – italianogrosso

+2

Better sử dụng bool với chuyển đổi – Zabavsky

+2

@italianogrosso Bạn đang chào đón. :) Nhưng bạn không nên để một thuộc tính của kiểu 'Visibility'. Điều này liệt kê trong không gian tên 'System.Windows', mà - như không gian tên nói - có nghĩa là nó hoàn toàn liên quan đến mặt bên của ứng dụng của bạn. Thực sự, ngay cả khi nó yêu cầu mã nhiều hơn, tốt hơn là để lộ một boolean không liên quan đến khung nhìn nào cả. – ken2k

4

Tôi sử dụng phần tử phương tiện để phát âm thanh trong giao diện người dùng bất cứ khi nào sự kiện xảy ra trong ứng dụng. Mô hình khung nhìn xử lý này, được tạo ra với thuộc tính Nguồn của kiểu Uri (với thuộc tính thông báo đã thay đổi, nhưng bạn đã biết bạn cần điều đó để thông báo cho UI). Tất cả bạn phải làm bất cứ khi nào thay đổi nguồn (và điều này tùy thuộc vào bạn), là đặt thuộc tính nguồn thành null (đây là lý do tại sao Thuộc tính nguồn phải là Uri chứ không phải chuỗi, MediaElement sẽ tự động ném ngoại lệ, NotSupportedException I suy nghĩ), sau đó đặt nó thành bất kỳ URI nào bạn muốn.

Có lẽ, khía cạnh quan trọng nhất của mẹo này là bạn phải đặt thuộc tính của MediaElement LoadedBehaviour thành Play trong XAML của chế độ xem của bạn. Hy vọng rằng không có mã phía sau là cần thiết cho những gì bạn muốn đạt được.

Bí quyết cực kỳ đơn giản nên tôi sẽ không đăng một ví dụ hoàn chỉnh. chức năng chơi Quan điểm của mô hình sẽ trông như thế này:

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); 
    } 

Dưới đây là tài sản Nguồn, không có gì đặc biệt về nó:

#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 

Đối với Tầm nhìn, và các công cụ như thế này, bạn có thể sử dụng bộ chuyển đổi (ví dụ như từ bool đến khả năng hiển thị, mà bạn có thể tìm thấy trên CodePlex cho WPF, SL, WP7,8) và ràng buộc tài sản điều khiển của bạn với thuộc tính của mô hình xem (ví dụ: IsVisible). Bằng cách này, bạn kiểm soát các phần của khía cạnh bạn xem. Hoặc bạn chỉ có thể có thuộc tính Visibility đã gõ System.Windows.Visibility trên mô hình khung nhìn của bạn (tôi không thấy bất kỳ sự vi phạm mẫu nào ở đây). Thực sự, nó không phải là không phổ biến.

Chúc may mắn,

Andrei

T.B. Tôi phải đề cập đến .NET 4.5 là phiên bản mà tôi đã thử nghiệm, nhưng tôi nghĩ nó cũng nên hoạt động trên các phiên bản khác.

8

Đối với tất cả các cuối comers,

Có rất nhiều cách để đạt được kết quả tương tự và nó thực sự phụ thuộc vào cách bạn muốn thực hiện của bạn, miễn là mã của bạn không phải là khó khăn để duy trì, tôi làm tin rằng nó là ok để phá vỡ các mô hình MVVM trong trường hợp nhất định. Nhưng có nói rằng, tôi cũng tin rằng luôn luôn có cách để làm điều này trong khuôn mẫu, và sau đây là một trong số họ chỉ trong trường hợp nếu có ai muốn biết những lựa chọn thay thế khác có sẵn.

Các Nhiệm vụ:

  1. chúng tôi không muốn có tài liệu tham khảo trực tiếp từ ViewModel để bất kỳ yếu tố giao diện người dùng, ví dụ: các MediaElement và View riêng của mình.
  2. chúng tôi muốn sử dụng Command để làm điều kỳ diệu ở đây

Giải pháp:

Nói tóm lại, chúng ta sẽ giới thiệu một giao diện giữa View và ViewModel để phá vỡ dependecy và Chế độ xem sẽ triển khai giao diện và chịu trách nhiệm kiểm soát trực tiếp MediaElement trong khi để ViewModel chỉ giao tiếp với giao diện, có thể được hoán đổi với triển khai khác cho mục đích thử nghiệm nếu cần và tại đây có phiên bản dài:

  1. giới thiệu một giao diện được gọi là IMediaService như sau:

    public interface IMediaService 
    { 
        void Play(); 
        void Pause(); 
        void Stop(); 
        void Rewind(); 
        void FastForward(); 
    } 
    
  2. Thực hiện IMediaService trong Xem:

    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. thì chúng ta làm vài điều trong DemoView.XAML:

    • Cho MediaElement một tên để mã đằng sau có thể truy cập vào nó như ở trên:
    <MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/> 
    
    • Cho xem một tên vì vậy chúng tôi có thể vượt qua nó như là một tham số và
    • nhập không gian tên tương tác để sử dụng sau này (một số không gian tên mặc định bị bỏ qua vì lý do đơn giản):
    <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"> 
    
    • khô kết hợp sự kiện Loaded thông qua kích hoạt để vượt qua quan điểm riêng của mình với mô hình xem qua một lệnh
    <ia:Interaction.Triggers> 
         <ia:EventTrigger EventName="Loaded"> 
          <ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction> 
         </ia:EventTrigger> 
        </ia:Interaction.Triggers> 
    
    • cuối cùng nhưng không ít nhất, chúng ta cần phải ho okup các điều khiển phương tiện truyền thông thông qua lệnh:
    <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. Chúng tôi bây giờ có thể bắt tất cả mọi thứ trong ViewModel (Tôi đang sử dụng DelegateCommand lăng kính ở đây):

    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 lưu ý: Tôi sử dụng tính năng Auto Wiring của Prism để liên kết View và ViewModel. Vì vậy, ở mã View của tập tin đằng sau không có mã chuyển nhượng DataContext, và tôi thích giữ nó theo cách đó, và do đó tôi đã chọn sử dụng các lệnh thuần túy để đạt được kết quả này.