简体   繁体   中英

How can I dynamically change what control I'm using within MVVM XAML

I am writing a simple application that uses VLC to play a video. This would normally be a simple solution, but there are side-effects to virtually all VLC libraries currently out there; Vlc.DotNet.WPF is great but it has a side-effect that it is CPU intensive but solves problems with layering videos, and LibVLCSharp.WPF is much better with CPU utilization but has problems with layering videos (airspace issues). So, I want to be able to architect my application to allow me to switch which video library I'm using to play the video so that I can manage the trade-offs of the two renderers depending on the hardware and situational requirements I have.

Each library provides a custom UserControl for use to display a video within a View. LibVLCSharp provides LibVLCSharp.WPF.VideoView and Vlc.DotNet.WPF provides Vlc.DotNet.Wpf.VlcControl .

I've structured my code to display each of these independently in the following way:

Control Class for using VlcDotNetWpf:

public class vlcPlayerDotNet : Vlc.DotNet.Wpf.VlcControl
{
   public vlcPlayerDotNet() {}
   ~vlcPlayerDotNet()
   {
      // class-specific cleanup routines
   }

   public void PlayVideo()
   {
      // class-specific video playback code to play Source video
   }

   // Dependency Properties
   public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register(
      "Source", typeof(string),
      typeof(vlcPlayerDotNet)
   );
   public string Source
   {
      get { return (string)GetValue(SourceProperty); }
      set { SetValue(SourceProperty, value); }
   } 

   /*
      ... and so on, for multiple dependency properties
   */
}

And Control class for using LibVlcSharp.Wpf:

public class vlcPlayerLibVLCSharp : LibVLCSharp.WPF.VideoView
{
   public vlcPlayerLibVLCSharp() {}
   ~vlcPlayerLibVLCSharp()
   {
      // class-specific cleanup routines
   }

   public void PlayVideo()
   {
      // class-specific video playback code to play Source video
   }

   // Dependency Properties
   public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register(
      "Source", typeof(string),
      typeof(vlcPlayerLibVLCSharp)
   );
   public string Source
   {
      get { return (string)GetValue(SourceProperty); }
      set { SetValue(SourceProperty, value); }
   } 

   /*
      ... and so on, for multiple dependency properties
   */
}

In my ViewModel I have a few needed inputs, namely:

   private string animationFile;
   public string AnimationFile
   {
      get { return animationFile; }
      set { SetProperty(ref animationFile, value); }
   }

Then, in my View, I define which video control I want to use - for example, if I am using the LibVLCSharp player I use the following:

   <Grid>
      <local:vlcPlayerLibVLCSharp 
         Grid.Column="0" Grid.Row="0"                 
         HorizontalAlignment="Center" VerticalAlignment="Center" 
         Height="720" Width="1280"
         Source="{Binding AnimationFile}" LoadedBehavior="Play"  
         MediaEnded="animationPlayer_MediaEnded" 
         MediaFailed="animationPlayer_MediaFailed" 
         x:Name="vlcPlayer"/>
   </Grid>

And so on for the other player.

What I DO NOT want is to have two View UserControls stacked and collapsed; I want to be able to have ONE View UserControl in the View and be able to dynamically choose which UserControl is actually being used within the View. My goal is to have a straightforward View, and it would be amazing if I could later add another Control without changing the View (but it's not a dealbreaker).

I have investigated using a ContentControl in the View to contain the video UserControl(s), but I couldn't figure out how to handle the Dependency Properties that need to be passed to the Control. I've also looked at DataTemplate and DataTemplateSelector but all of the examples I've found deal with List Boxes and I'm having a hard time making the conceptual leap from the listbox to these derived controls.

Any advice on how to architect my code to allow this swap under the hood is appreciated!

You can use a content control and data templates

In your parent window define the data templates for each video player.

<Window ...
        ...>

    <Window.Resources>
        <DataTemplate DataType="{x:Type viewModels:LibSharpViewModel}">
            <views:LibSharpView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewModels:VlcViewModel}">
            <views:VlcView/>
        </DataTemplate>

    </Window.Resources>

    <Grid>        
        <ContentControl  Content="{Binding SelectedVideoPlayerViewModel}"/>               
    </Grid>
</Window>

Then define the Vlc and LibSharp views in separate xaml files.

You can create a base class for the video player viewmodels that define the required dependency properties that the Vlc and LibSharp VM's will inherit from.

In the parent window's VM you can determine which player to use based on your hardware criteria, and set the SelectedVideoPlayerViewModel to switch which control is displayed in the content control.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM