18

ViewModelはViewの知識を持ってはならないことを理解していますが、ViewModelでView(または直接MediaElement)への参照を持つ以外に、ViewModelからMediaElement.Play()メソッドを呼び出すにはどうすればよいですか?
その他の(リンクされた)質問:MVVMパターンに違反することなく、ViewModelからViewのコントロールの可視性を管理するにはどうすればよいですか?

4

3 に答える 3

25

Play()1)ビューモデルから呼び出さないでください。代わりに(たとえばPlayRequested)ビューモデルでイベントを発生させ、ビューでこのイベントをリッスンします。

ビューモデル:

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

見る:

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

Visibility2)ビューモデルでパブリックブールプロパティを公開し、コントロールのプロパティをこのプロパティにバインドできます。VisibilityタイプVisibilityであり、ではないので、boolコンバーターを使用する必要があります。

このようなコンバーターの基本的な実装はここにあります。この関連する質問も役立つかもしれません。

于 2012-05-17T07:49:48.063 に答える
13

すべての後発者のために、

同じ結果を達成する方法はたくさんありますが、コードの保守が難しくない限り、実装方法によって異なりますが、特定の場合にMVVMパターンを壊しても問題ないと思います。

しかし、そうは言っても、パターン内でこれを行う方法は常にあると思います。他にどのような選択肢があるか知りたい場合に備えて、以下はその1つです。

タスク:

  1. ViewModelからUI要素、つまりMediaElementとView自体への直接参照は必要ありません。
  2. コマンドを使用してここで魔法を実行したい

ソリューション:

要するに、依存関係を解消するためにViewとViewModelの間にインターフェースを導入し、Viewはインターフェースを実装し、ViewModelがインターフェースとのみ通信するようにしながら、MediaElementの直接制御を担当します。必要に応じて、テスト目的で他の実装と交換できます。長いバージョンがあります。

  1. 以下のようにIMediaServiceと呼ばれるインターフェースを導入します。

    public interface IMediaService
    {
        void Play();
        void Pause();
        void Stop();
        void Rewind();
        void FastForward();
    }
    
  2. ビューにIMediaServiceを実装します。

    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. 次に、DemoView.XAMLでいくつかのことを行います。

    • MediaElementに名前を付けて、背後のコードが上記のようにアクセスできるようにします。
       <MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
    
    • ビューに名前を付けて、パラメーターとして渡すことができるようにします。
    • 後で使用するために対話型名前空間をインポートします(簡単にするために、一部のデフォルトの名前空間は省略されています)。
       <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">
    
    • トリガーを介してLoadedイベントをフックアップし、コマンドを介してビュー自体をビューモデルに渡します
       <ia:Interaction.Triggers>
             <ia:EventTrigger EventName="Loaded">
                 <ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
             </ia:EventTrigger>
         </ia:Interaction.Triggers>
    
    • 最後になりましたが、コマンドを使用してメディアコントロールを接続する必要があります。
       <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. これで、ViewModel内のすべてをキャッチできます(ここではprismのDelegateCommandを使用しています)。

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

補足:Prismの自動配線機能を使用してViewとViewModelをリンクしています。したがって、ビューのコードビハインドファイルにはDataContext割り当てコードがなく、そのままにしておくことを好みます。したがって、この結果を達成するために、純粋にコマンドを使用することにしました。

于 2016-08-21T06:20:23.247 に答える
4

アプリケーションでイベントが発生するたびに、メディア要素を使用してUIでサウンドを再生します。これを処理するビューモデルは、タイプUriのSourceプロパティを使用して作成されました(notifyプロパティが変更されていますが、UIに通知するためにそれが必要であることはすでにわかっています)。

ソースが変更されるたびに(そしてこれはあなた次第です)、ソースプロパティをnullに設定するだけです(これが、ソースプロパティが文字列ではなくUriである必要がある理由です。MediaElementは当然例外をスローします。NotSupportedExceptionだと思います)。必要なURIに設定します。

おそらく、このヒントの最も重要な側面は、MediaElementのプロパティLoadedBehaviourをビューのXAMLで再生するように設定する必要があることです。うまくいけば、達成したいことのために背後にあるコードは必要ありません。

トリックは非常に単純なので、完全な例を投稿することはしません。ビューモデルの再生関数は次のようになります。

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

Sourceプロパティは次のとおりですが、特別なことは何もありません。

    #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

可視性、およびこのようなものに関しては、コンバーター(たとえば、ブール値から可視性まで、CodePlex for WPF、SL、WP7,8にあります)を使用して、コントロールのプロパティをビューモデルのプロパティ(IsVisibleなど)にバインドできます。 。このようにして、ビューのアスペクトの一部を制御します。または、ビューモデルでVisibilityプロパティにSystem.Windows.Visibilityと入力することもできます(ここではパターン違反は見られません)。本当に、それはそれほど珍しいことではありません。

幸運を、

アンドレイ

PS .NET 4.5は私がこれをテストしたバージョンであることに言及する必要がありますが、他のバージョンでも機能するはずです。

于 2013-09-04T13:01:26.470 に答える