WPF MVVM:从转换器调用视图模型中的方法

问题描述:

我有一个WPF图像控件,它的源属性绑定到返回图像的属性。WPF MVVM:从转换器调用视图模型中的方法

<Image Grid.Row="0" 
     Source="{Binding Path=ImageSrc, NotifyOnTargetUpdated=True, Converter={StaticResource imgToSrcConverter}}" /> 

然后,我有一个转换器,将图像绑定到源属性并将其转换为bitmapImage。当bitmapimage下载完成后,我想在我的视图模型中执行一个方法,所以我订阅了BitmapImage中的DownloadCompleted事件。那么我怎样才能从转换器中调用视图模型中的方法?它会违反MVVM原则吗?

转换

public class ImgToSrcConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, 
      System.Globalization.CultureInfo culture) 
    { 
     Image image = value as Image; 
     if (image != null) 
     { 
      MemoryStream ms = new MemoryStream(); 
      image.Save(ms, image.RawFormat); 
      ms.Seek(0, SeekOrigin.Begin); 
      BitmapImage bi = new BitmapImage(); 
      bi.BeginInit(); 
      bi.StreamSource = ms; 
      bi.EndInit(); 

      bi.DownloadCompleted += new EventHandler(bi_DownloadCompleted); 

      return bi; 
     } 
     return null; 
    } 

    public object ConvertBack(object value, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    private void bi_DownloadCompleted(object sender, EventArgs e) 
    { 
     // Call my method in view model 
    } 
} 
+0

如果您的应用有重要的'BitmapImage',那么视图模型中的'ImageSrc'属性应该是'BitmapImage'类型,并且您不需要转换器 – ASh

+0

我不确定我是否理解您的'重新尝试实现?你能否详细说明你想要做什么? “下载”是什么意思?当这个“下载”完成时你会做什么? –

你可以使用多转换器,接受几个值:

public class ImgToSrcConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     Image image = values[0] as Image; 
     if (image != null) 
     { 
      MemoryStream ms = new MemoryStream(); 
      image.Save(ms, image.RawFormat); 
      ms.Seek(0, SeekOrigin.Begin); 
      BitmapImage bi = new BitmapImage(); 
      bi.BeginInit(); 
      bi.StreamSource = ms; 
      bi.EndInit(); 

      ViewModel vm = values[1] as ViewModel; 
      bi.DownloadCompleted += (s, e) => 
      { 
       vm.Method(); 
      }; 

      return bi; 
     } 
     return null; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

用法:

<Image Grid.Row="0"> 
    <Image.Source> 
     <MultiBinding NotifyOnTargetUpdated="True" Converter="{StaticResource imgToSrcConverter}"> 
      <Binding Path="ImageSrc" /> 
      <Binding Path="." /> 
     </MultiBinding> 
    </Image.Source> 
</Image> 

不,这不,只要打破MVVM模式您的可测试应用程序逻辑保留在视图模型中。

+0

使用您的解决方案,当视图模型中的属性“ImageSrc”发生改变时,转换器已到达,但现在事件DownloadCompleted从未被触发。为什么?也许是因为一旦bi返回,bi对象就被破坏了? – user1624552

+0

我在示例代码中将事件处理程序定义为匿名方法。除此之外,我没有改变任何东西。你有没有在匿名方法中设置断点?如果事件在之前被提出,它仍然会被提升。 – mm8

+0

是的,我在vm.Method()中放置了一个断点。在匿名的DownloadCompleted事件中。转换器方法被执行,但事件从未被引发。 – user1624552

你可以做的是通过直接的DataContext到转换器,然后从那里访问适用属性/方法。

更新图像XAML到:

<Image Grid.Row="0" 
     Source="{Binding Path=DataContext, 
         RelativeSource={RelativeSource Self}, 
         NotifyOnTargetUpdated=True, 
         Converter={StaticResource imgToSrcConverter}}" /> 

然后,握住你的视图模型的引用,并据此称:

public class ImgToSrcConverter : IValueConverter 
{ 
    private MyViewModel _dataContext; 

    public object Convert(object value, Type targetType, object parameter, 
      System.Globalization.CultureInfo culture) 
    { 
     var dataContext = value as MyViewModel; 
     if (dataContext != null) 
     { 
      _dataContext = dataContext; 

      var image = dataContext.ImageSrc as Image; 
      if (image != null) 
      { 
       MemoryStream ms = new MemoryStream(); 
       image.Save(ms, image.RawFormat); 
       ms.Seek(0, SeekOrigin.Begin); 
       BitmapImage bi = new BitmapImage(); 
       bi.BeginInit(); 
       bi.StreamSource = ms; 
       bi.EndInit(); 

       bi.DownloadCompleted += new EventHandler(bi_DownloadCompleted); 

       return bi; 
      } 
     } 
     return null; 
    } 

    public object ConvertBack(object value, Type targetType, 
     object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 

    private void bi_DownloadCompleted(object sender, EventArgs e) 
    { 
     _dataContext?.MyMethod(); 
    } 
}   

注意,我觉得这是一个有点代码 - 的对于MVVM而言,通常情况下,您绝不会希望通过转换器访问您的ViewModel。对我来说,似乎这些逻辑可以在ViewModel本身中加载/维护,而不依赖于转换器。

+0

使用这个,转换器没有被调用。 – user1624552

+0

@ user1624552您在绑定可访问的位置使用'imgToSrcConverter'键创建了转换器的资源? –

+0

@BradleyUffner是的,我在Window.Resources部分窗口的开头创建了它。在窗口资源的下面,所有的内容都在一个主要的网格中,我把datacontext关联到如下:。也许这是影响,这是为什么不转换转换器的原因? – user1624552

使用IMultiValueConverter即使世界的方法,所以你可以得到你的形象和DataContext的(vievmodel)的转换器... WPF: MultiBinding and IMultiValueConverter

另一种方式可能是一个EventAggregator/MessageBus,然后将消息发送到您的视图模型。 ..?尽管许多人会认为MessageBus可以被视为反模式。 Roll Your Own Simple Message Bus/Event Aggregator