0

I am new to WPF and C#, I try to implement an Image control, which is updated whenever there is new data coming from serial port, but failed after a lot of attempts. It just shows the black image at the beginning and when there is new data coming, the Image control is not updated (ImageSource in ImageViewModel was not updated and event NotifyPropertyChanged was not fired). Can anyone help me out?

I have 3 classes and 1 Usercontrol. They are ImageViewModel, ImageConverter, Control and UserControl and UserControl code behind.

My ImageViewModel class

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);
    }
}

My ImageConverter Class

[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;
    }
}

My Control class

    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;

    }

My Usercontrol

<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 code behind

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

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

Thanks a lot!

4

2 回答 2

0

看起来串行端口处理类不需要从 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();
}
于 2013-08-03T21:17:28.350 回答
0

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

<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) 将包含用户控件的窗口控件(MainWindow)绑定到 MainWindowViewModel

2)MainWindowViewModel 会有一个 Visibility 参数来控制 User Controls Visibility。

3)<usercontrol DataContext="{Binding imageViewModel}" Visibility="{Binding imageViewVisibility}"/>

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

4) MainWindowViewModel:ViewModelbase

{

  ImageViewModel _viewModel;
  public ImageViewModel imageViewModel
  {
      get{ return _viewModel; }
      set
      {
          _viewModel=value;
          OnPropertyChanged("imageViewModel");
      }
}
于 2013-08-03T16:36:45.913 回答