【问题标题】:WPF MVVM : Calling a method in view model from a converterWPF MVVM:从转换器调用视图模型中的方法
【发布时间】:2023-03-17 18:05:02
【问题描述】:

我有一个 WPF 图像控件,它的源属性绑定到返回图像的属性。

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

然后我有一个转换器,它将图像绑定到源属性并将其转换为位图图像。当 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
    }
}

【问题讨论】:

  • 如果您的应用拥有BitmapImage 很重要,那么视图模型中的ImageSrc 属性应该是BitmapImage 类型,并且您不需要转换器
  • 我不确定我是否理解您想要实现的目标?你能详细说明你到底想要做什么吗? “下载”是什么意思?当这个“下载”完成后你会做什么?

标签: c# wpf mvvm


【解决方案1】:

您可以使用接受多个值的多转换器:

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 模式。

【讨论】:

  • 使用您的解决方案,当视图模型中的属性“ImageSrc”发生更改时,会到达转换器,但现在永远不会触发事件 DownloadCompleted。为什么?可能是因为 bi 返回后 bi 对象就被销毁了?
  • 我在示例代码中将事件处理程序定义为匿名方法。除此之外,我没有改变任何东西。您是否在匿名方法中设置了断点?如果之前引发了该事件,它仍然会引发。
  • 是的,我在 vm.Method(); 行中放了一个断点在匿名 DownloadCompleted 事件中。转换器方法已执行,但从未引发事件。
  • 下载完成了吗?它曾经奏效吗?无论如何,您的问题是关于如何调用视图模型的方法,我相信这已经得到解答。
  • 对。我将为此打开另一个线程。我接受你的回答。我将在此处放置线程的新链接。
【解决方案2】:

您可以将DataContext 直接传递给转换器,然后从那里访问适用的属性/方法。

将图像 xaml 更新为:

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

然后,保留对您的 ViewModel 的引用,并相应地调用:

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 本身中加载/维护,而不是依赖于转换器。

【讨论】:

  • 使用这个,转换器不会被调用。
  • @user1624552 您在绑定可访问的位置使用imgToSrcConverter 的键为转换器创建了资源?
  • @BradleyUffner 是的,我在 Window.Resources 部分的窗口开头创建了它。同样在窗口资源下方,所有内容都在一个主网格中,我在其中关联数据上下文,如下所示:。也许这正在影响,这就是没有调用转换器的原因?
  • 只有在源属性被更新并且DataContext属性永远不会被更新时才会调用转换器。这就是为什么您应该按照我的回答使用 MultiBinding。
【解决方案3】:

有一种使用 IMultiValueConverter 的方法,因此您可以将图像和 datacontext(vievmodel) 获取到转换器... WPF: MultiBinding and IMultiValueConverter

另一种方式可能是 EventAggregator/MessageBus 然后向您的 ViewModel 发送消息...?尽管许多人会争辩说 MessageBus 可以被视为一种反模式。 Roll Your Own Simple Message Bus / Event Aggregator

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 2018-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多