【问题标题】:WPF Image control is not automatic update from ViewModelWPF 图像控件不是从 ViewModel 自动更新的
【发布时间】:2013-08-04 17:35:26
【问题描述】:

我是 WPF 和 C# 的新手,我尝试实现一个 Image 控件,只要有来自串行端口的新数据,它就会更新,但经过多次尝试后失败了。它只是在开始时显示黑色图像,当有新数据到来时,Image 控件不更新(ImageViewModel 中的ImageSource 未更新并且未触发事件NotifyPropertyChanged)。谁能帮帮我?

我有 3 个类和 1 个用户控件。它们分别是ImageViewModel、ImageConverter、Control以及UserControl和UserControl背后的代码。

我的 ImageViewModel 类

public class ImageViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private System.Drawing.Image _img;

    public System.Drawing.Image ImageSource
    {
        get { return _img; }
        set { _img = value; NotifyPropertyChanged("ImageSource"); }
    }

    public ImageViewModel()
    {
        ImageSource = new System.Drawing.Bitmap(320, 240);
    }
}

我的 ImageConverter 类

[ValueConversion(typeof(System.Drawing.Image), typeof(System.Windows.Media.ImageSource))]
public class ImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        // empty images are empty...
        if (value == null) { return null; }
        else
        {
            var image = (System.Drawing.Image)value;
            // Winforms Image we want to get the WPF Image from...
            var bitmap = new System.Windows.Media.Imaging.BitmapImage();
            bitmap.BeginInit();
            MemoryStream memoryStream = new MemoryStream();
            // Save to a memory stream...
            image.Save(memoryStream, ImageFormat.Bmp);
            // Rewind the stream...
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
            bitmap.StreamSource = memoryStream;
            bitmap.EndInit();
            return bitmap;
        }
    }

    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        return null;
    }
}

我的控制类

    public void OpenPort()
    {
        try
        {
            SerialPort.Open();
            SerialPort.DataReceived += new SerialDataReceivedEventHandler(receivedDataFromSerialPort);
            SerialPort.DtrEnable = true;
            SerialPort.RtsEnable = true;

        }
        catch (Exception e)
        {
            throw e;
        }
    }

    public void ClosePort()
    {
        try
        {
            SerialPort.Close();
            SerialPort.DataReceived -= new SerialDataReceivedEventHandler(receivedDataFromSerialPort);

        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    ImageViewModel ivm = new ImageViewModel();
    private void receivedDataFromSerialPort(object sender, SerialDataReceivedEventArgs e)
    {
        System.Drawing.Image tmp = new Bitmap(320,240);

        //other codes
        ivm.ImageSource = tmp;

    }

我的用户控件

<UserControl.Resources>
    <imgcv:ImageConverter x:Key="imageConverter" />
</UserControl.Resources>
<UserControl.DataContext>
    <viewmodel:ImageViewModel/>
</UserControl.DataContext>

<StackPanel Height="240" Width="300">
    <Image Width="320" Height="240" Source="{Binding Path=ImageSource,IsAsync=True, Converter={StaticResource imageConverter},ConverterParameter=ImageSource, UpdateSourceTrigger=LostFocus}" />
</StackPanel>

UserControl 后面的代码

public partial class UserControls : System.Windows.Controls.UserControl
{
    Control ctrl = new Control();

    //other codes
    ctrl.OpenPort();
    //other codes
    ctrl.ClosePort();
}

非常感谢!

【问题讨论】:

  • 我看到了活动,但我错过了你课堂上的界面?是错字还是没用过界面?如果是后者,那是你的问题。
  • 应该只实例化一个ImageViewModel 类。为什么你在 XAML 中定义了一个实例?
  • 感谢您的建议,但我在 ImageViewModel 类中添加了 INotifyPropertyChanged 但没有任何变化。
  • receivedDataFromSerialPort方法写在你UserControl后面的代码里?
  • receivedDataFromSerialPort 方法写在 Control 类中,而不是 UserControl 后面的代码。

标签: c# wpf image data-binding inotifypropertychanged


【解决方案1】:

为什么需要转换器?您的用户控件可以这样创建:

<UserControl Name="userControl1">
  <StackPanel Height="240" Width="300">
     <Image Width="320" Height="240" Source="{Binding Source}"/>
  </StackPanel>

您的 ViewModel 类可以如下:

 public class ImageViewModel : ViewModelBase
    {
        ImageSource _source;
        public ImageSource Source
        {
            get { return _source; }
            set
            {
                _source = value;
                OnPropertyChanged("Source");
            }
        }

        public ImageViewModel()
        {
            //Initialize Source using new bitmap or any other way
        }
    }

ViewModelBase 将实现 INotifyPropertyChanged 并且 OnPropertyChanged 方法应该存在于 ViewModelBase 中。

这应该可行。我刚刚在您的问题中遇到您的图像将被异步加载。在这种情况下,您可以使用 Dispatcher 更新“源”。

可以通过以下方式分配数据上下文:

ImageViewModel imageViewModel= new ImageViewModel();
userControl1.DataContext     = imageViewModel;

还有另一种分配数据上下文的方法,这是我大部分时间使用的:

1)将一个Window控件(MainWindow)绑定到一个MainWindowViewModel上

2)MainWindowViewModel 将有一个 Visibility 参数来控制用户控件的可见性。

3)&lt;usercontrol DataContext="{Binding imageViewModel}" Visibility="{Binding imageViewVisibility}"/&gt;

数据上下文参数将出现在 MainWindowViewModel 中。

4) MainWindowViewModel:ViewModelbase

{

  ImageViewModel _viewModel;
  public ImageViewModel imageViewModel
  {
      get{ return _viewModel; }
      set
      {
          _viewModel=value;
          OnPropertyChanged("imageViewModel");
      }
}

【讨论】:

    【解决方案2】:

    看起来串行端口处理类不需要从 Control 派生。它可以是一个普通的类,它将视图模型实例传递给它的构造函数:

    public class SerialPortController
    {
        private ImageViewModel ivm;
    
        public SerialPortController(ImageViewModel ivm)
        {
            this.ivm = ivm;
        }
    
        public void OpenPort()
        {
            ...
        }
    
        public void ClosePort()
        {
            ...
        }
    
        private void receivedDataFromSerialPort(object sender, SerialDataReceivedEventArgs e)
        {
            ...
        }
    }
    

    您的 UserControl 现在将像这样使用SerialPortController

    public partial class UserControls : System.Windows.Controls.UserControl
    {
        private SerialPortController ctrl;
    
        public UserControls()
        {
            InitializeComponent();
    
            // pass ImageViewModel from the UserControl's DataContext
            ctrl = new SerialPortController(DataContext as ImageViewModel);
        }
    
        //other code
        ctrl.OpenPort();
        //other code
        ctrl.ClosePort();
    }
    

    【讨论】:

      猜你喜欢
      • 2017-11-26
      • 1970-01-01
      • 1970-01-01
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 2011-11-08
      • 2014-01-12
      • 1970-01-01
      相关资源
      最近更新 更多