WPF MVVM如何在ViewModel直接操作控件对象
早些年在WPF中使用COM组件时,需要在ViewModel中操作COM组件中的控件对象,但是这个控件对象又不支持绑定,
后面的解决办法是在窗口加载时,将控件对象以参数传递到Loaded事件的处理命令中,然后将这个对象记录下来,后面就可以直接操作这个控件了。
今天同事在使用WebView2的时候,又遇到这个问题,写个文章分享一下,给后续需要的小伙伴提供点参考。
我们创建一个WPF的项目,然后在界面上放置一个WMP控件(Windows Media Player)。
MainWindow.xaml
1 <Window x:Class="GetControlInViewModel.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:wmp="clr-namespace:AxWMPLib;assembly=AxInterop.WMPLib" 7 xmlns:local="clr-namespace:GetControlInViewModel" 8 mc:Ignorable="d" 9 Title="MainWindow" Height="450" Width="800"> 10 <Grid> 11 <WindowsFormsHost> 12 <wmp:AxWindowsMediaPlayer x:Name="WMPPlayer"></wmp:AxWindowsMediaPlayer> 13 </WindowsFormsHost> 14 </Grid> 15 </Window>
如果我们想在ViewModel中操作这个WMP对象,可以在Loaded事件中添加如下处理
这里我使用了废弃的包MvvmLight来做演示,因为比较简单方便。
1 <i:Interaction.Triggers> 2 <i:EventTrigger EventName="Loaded"> 3 <i:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=WMPPlayer}"></i:InvokeCommandAction> 4 </i:EventTrigger> 5 </i:Interaction.Triggers>
ViewModel
1 public class MainViewModel : ViewModelBase 2 { 3 private AxWMPLib.AxWindowsMediaPlayer mediaPlayer; 4 5 public RelayCommand<AxWMPLib.AxWindowsMediaPlayer> OnLoadedCommand { get; private set; } 6 7 public MainViewModel() 8 { 9 OnLoadedCommand = new RelayCommand<AxWMPLib.AxWindowsMediaPlayer>(OnLoaded); 10 } 11 12 private void OnLoaded(AxWMPLib.AxWindowsMediaPlayer axWindowsMediaPlayer) 13 { 14 this.mediaPlayer = axWindowsMediaPlayer; 15 } 16 }
然后将ViewModel绑定到MainWindow上,运行时就可以获取到WMP对象
有时我们会遇到控件未初始化,在Loaded事件中无法获取到控件对象,类似下面这样
比如在使用Prism进行区域导航的时候,就可能会出现这个问题。
此时我们可以将整个窗口/用户控件(UserControl)传过来,然后延时一秒,或者延时加循环几次,就可以获取到控件对象
1 private async void OnLoaded(Window window) 2 { 3 await Task.Delay(1000); 4 var webViewElement = window.FindName("WebView"); 5 if (webViewElement != null) 6 this.webView = webViewElement as WebView2; 7 }
示例代码