I'm trying to capture video from camera and display in c# WPF form. But image dont show up when i start the program. Also debugger gives me no exception it's just running. I m debugging and i see the CapturedImage property is taking the data as supposed to be. It might be they work in the different threads. But i cant figure out. So, HELP ME,
I'm binding a ImageSource type property. As you can see,
public class VideoCapturing : INotifyPropertyChanged
{
private ImageSource capturedImage;
public ImageSource CapturedImage
{
get { return capturedImage; }
set { capturedImage = value; OnPropertyChanged("CapturedImage"); }
}
Also capturing code is here,
public void run()
{
if (cap.capture == null)
{
capture = new Emgu.CV.VideoCapture(0);
CurrentFrame = new Mat();
}
capture.ImageGrabbed += VideoCapture_ImageGrabbed;
capture.Start();
}
private void VideoCapture_ImageGrabbed(object sender, EventArgs e) // runs in worker thread
{
capture.Retrieve(CurrentFrame);
CapturedImage = ImageSourceFromBitmap(CurrentFrame.ToImage<Emgu.CV.Structure.Bgr, byte>().ToBitmap());
}
} // VideoCapturing class ends.
here is the xaml part for binding,
<Grid>
<Image x:Name="img" Source="{Binding CapturedImage}"></Image>
</Grid>
This is the mainwindow.xaml.cs,
public MainWindow()
{
InitializeComponent();
VideoCapturing VideoCapture = new VideoCapturing();
this.DataContext = VideoCapture ;
VideoCapture.run();
}
There is a typo when you call OnPropertyChanged(). "CaptureImage" but it should be "CapturedImage".
public ImageSource CapturedImage
{
get { return capturedImage; }
set { capturedImage = value; OnPropertyChanged("CapturedImage"); }
}
In MainWindow you should call VideoCapture.run() instead of capture.run().
Because of VideoCapture_ImageGrabbed runs in worker thread you have set CapturedImage on UI thread by calling Dispatcher.BeginInvoke.
private void VideoCapture_ImageGrabbed(object sender, EventArgs e)
{
capture.Retrieve(CurrentFrame);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
CapturedImage = ImageSourceFromBitmap(CurrentFrame.ToImage<Emgu.CV.Structure.Bgr, byte>().ToBitmap());
}));
}
Related
I have make a ContentControl and it has some custom Propertities. The control itself works fine but I like to update its interface during design time in XAML editor. The problem is next: The control's UI update if I change its Size (SizeChanged event will do that) but I cannot find any way to do this if CustomProperty like OffsetX changes during design time.
So, how to change the following code to make this happen? It isn't too convenient to update Control UI changing its size every time.
public sealed class MyControlElement: ContentControl
{
//
//SOME INITIALIZE CODE IS HERE
//
public MyControlElement() => DefaultStyleKey = typeof(MyControlElement);
protected override void OnApplyTemplate()
{
//
//SOME INITIALIZE CODE IS HERE
//
base.OnApplyTemplate();
}
//OFFSET X DESCRIPTION
[Description("OffsetX"), Category("MyControlElementParameters"), Browsable(true)]
//OFFSET X
public int OffsetX
{
get
{
return (int)GetValue(OffsetXProperty);
}
set
{
if (OffsetX != value)
{
SetValue(OffsetXProperty, value);
OnOffsetXChanged(this, new EventArgs());
}
}
}
public static readonly DependencyProperty OffsetXProperty = DependencyProperty.Register("OffsetX", typeof(int), typeof(MyControlElement), PropertyMetadata.Create(0));
public event EventHandler OffsetXChanged;
private void OnOffsetXChanged(object sender, EventArgs e)
{
UpdateControlUI();
this.OffsetXChanged?.Invoke(this, e);
}
}
I found some kind of "Hack". Still hoping to find better solution. The next trick works and it is possible to update Control interface during design time.
First need to add handler for Loaded.
public MyControlElement()
{
this.DefaultStyleKey = typeof(MyControlElement);
this.Loaded += MyControlElement_Loaded;
}
private void MyControlElement_Loaded(object sender, RoutedEventArgs e)
{
//
//SOME INITIALIZE CODE HERE IF NEEDED
//
//RUN CONTROL VISUAL UPDATER ONLY IF IN DESIGN MODE
if (DesignMode.DesignModeEnabled) ControlDesignTimeUIUpdater();
//FLAG - CONTROL HAS BEEN INITIALIZED
IsControlInitialized = true;
}
And lets add ControlDesignTimeUIUpdater void for UI update. This void has a loop to keep UI updated during design time.
private async void ControlDesignTimeUIUpdater()
{
double OldImageWidth = ImageWidth;
double OldImageHeight = ImageHeight;
CornerRadius OldImageCornerRadius = ImageCornerRadius;
double OldBorderThickness = BorderThickness;
ImageSource OldMyImageSource = MyImageSource;
while (this.IsLoaded)
{
//CHECK CHANGES DELAY 100ms
await Task.Delay(100);
//MAKE SURE CONTROL IS INITIALIZED BEFORE ANY UI UPDATES
if (IsControlInitialized)
{
if (OldImageWidth != ImageWidth)
{
OldImageWidth = ImageWidth;
SetImageWidth();
}
if (OldImageHeight != ImageHeight)
{
OldImageHeight = ImageHeight;
SetImageHeight();
}
if (OldImageCornerRadius != ImageCornerRadius)
{
OldImageCornerRadius = ImageCornerRadius;
SetImageCornerRadius();
}
if (OldBorderThickness != BorderThickness)
{
OldBorderThickness = BorderThickness;
SetBorderThickness();
}
if (OldMyImageSource != MyImageSource)
{
OldMyImageSource = MyImageSource;
SetMyImageSource();
}
//
// ETC.
//
}
}
}
By this Hack it is possible update control in "real-time" during design. It's even possible add animations, size changes etc.
There is a property in my ViewModel whose value is changed in the DoWork method of the BackgroundWorker. When I start the application and click on the button that starts the BackgroundWorker, I see how the value of this property changes. However, when I open a new window, this property retains its default value and is not updated even though the BackgroundWorker is still running.
Her is the code in my ViewModel:
private string currentData;
...
public ViewModel()
{
...
// Property initialised with a default value
currentData = "BackgroundWorker is not running";
...
}
public string CurrentData
{
get { return this.currentData; }
private set
{
if (this.currentData != value)
{
this.currentData = value;
this.RaisePropertyChanged("CurrentData");
}
}
}
private void DoWork(object sender, DoWorkEventArgs e)
{
isUpdating = true;
...
this.CurrentData = "BackgroundWorker is running...";
for (...)
{
...
if(...)
{
this.CurrentData = "value1";
}
else
{
this.CurrentData = "value2";
...
}
}
}
RaisePropertyChanged Method:
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML-code for both windows (MainWindow and newtWindow):
<TextBlock Margin="10" MinWidth="250" VerticalAlignment="Center" Text="{Binding CurrentData}" FontSize="12" Foreground="White" HorizontalAlignment="Left" />
BackgroundWorker:
private readonly BackgroundWorker worker;
...
public ImageViewModel()
{
currentData = "BackgroundWorker is not running";
this.worker = new BackgroundWorker();
this.worker.DoWork += this.DoWork;
this.worker.ProgressChanged += this.ProgressChanged;
this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Completeted);
this.worker.WorkerReportsProgress = true;
}
Can you tell what I'm doing wrong and how I can fix it?
You would have to create a private string reference out of your property,
where the property can set the value and it will be saved on the stack,
something like so(this is how wpf get info from text boxes in the text property)
private string _text; //string that is used as a reference which you can plug your new new window
public string Text
{
get
{
return this._text;
}
set
{
this._text = value;
if (null != PropertyChanged)
{
this.PropertyChanged(this, new PropertyChangedEventArgs ("Text"));
}
}
}
I would avoid updating a property, which is bound to the UI, from a background thread. I'm not sure if this will solve your issue, but I would try to use the BackgroundWorker's ReportProgress method to notify your ViewModel about changes of CurrentData. Then in the OnProgressChanged event handler you can set the CurrentData to a new String.
public void ReportProgress(int percentProgress, object userState)
You can put your String into the "userState" object.
Edit
something like this:
public ViewModel()
{
...
backgroundWorker.ReportsProgress = true;
backgroundWorker.ProgressChanged += OnProgressChanged;
...
}
private void DoWork(object sender, DoWorkEventArgs e)
{
isUpdating = true;
...
ReportProgress(0,"BackgroundWorker is running...");
for (...)
{
...
if(...)
{
ReportProgress(0,"value1");
}
else
{
ReportProgress(0,"value2");
...
}
}
}
and
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.CurrentData = (string)e.UserState;
}
Ok so from what you've said so far my understanding is as follows:
From your original question:
However, when I open a new window, this property retains its default value and is not updated even though the BackgroundWorker is still running.
From your comment to my previous answer about setting the window's DataContext:
<Window.DataContext> <local:ViewModel /> </Window.DataContext>
When you create a new window, you also create a new instance of your ViewModel. This new instance also has its own BackgroundWorker. When you say "...even though the BackgroundWorker is still running", then this is only true for your first window, since the Backgroundworker from your new window has to be started first.
If you want the same DataContext (and thus the same BackgroundWorker) for both windows, you need to set the DataContext of your new window to the already existing instance of your ViewModel.
I have an Image element that's bound to an ImageSource element inside a class that I've created. The ImageSource gets updated every time a slider is changed. When I first instantiate my window, the ImageSource is blank until the user loads a file. Once the file is loaded, the image appears and the user can scroll the slider and see the image change. They can then select "OK" on the dialog to save this pattern. This all works fine.
However, if they double-click on the item in the ListView then it will re-open this dialog to make further edits. So, it creates a new dialog and then reloads the pertinent info about the image. However, for whatever reason... the image binding no longer works. I can put a breakpoint on the ImageSource getter and everytime I change the slider, the image does get updated... However, it just doesn't appear the be binding correctly. Why would it bind correctly on the first time the window is opened, but not on subsequent openings. I'll try to lay out my code.
In my .XAML code:
<UserControl x:Class="MyControls.CreatePattern"
x:Name="PatternCreation"
...
d:DesignHeight="160" d:DesignWidth="350">
<Slider Value="{Binding ElementName=PatternCreation, Path=Pattern.ZNorm, Mode=TwoWay}" Maximum="1" Name="Slider" VerticalAlignment="Stretch" />
<Image Name="PatternPreview" Source="{Binding ElementName=PatternCreation, Path=Pattern.WPFSlice}" Stretch="Uniform"></Image>
</UserControl
In my code behind I define the Pattern to be bound:
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; }
}
In my PatternVoxelBased class, I have a WPFSlice and ZNorm properties defined like this:
protected ImageSource mWPFSlice;
public ImageSource WPFSlice
{
get { return mWPFSlice; }
set
{
mWPFSlice = value;
NotifyPropertyChanged("WPFSlice");
}
}
protected double mZNorm = 0.5;
public double ZNorm
{
get { return mZNorm; }
set
{
if (mZNorm == value) return;
mZNorm = value;
NotifyPropertyChanged("ZNorm");
WPFSlice = BuildImageAtZ(mZNorm);
}
}
I have an event to load the dialog window the first time:
private void CreatePattern_Click(object sender, RoutedEventArgs e)
{
CCreateVoxelPattern dlg = new CCreateVoxelPattern();
dlg.DataContext = DataContext;
dlg.CShow(PatternLibraryMenu);
}
My ListView Double-Click function to reload the dialog window:
private void ListViewPatternLibrary_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
PatternVoxelBased item = ((ListView)sender).SelectedValue as PatternVoxelBased;
CCreateVoxelPattern dlg = new CCreateVoxelPattern();
dlg.DataContext = DataContext;
dlg.Main.Pattern = item;
dlg.Main.LoadPattern();
dlg.CShow(PatternLibraryMenu);
}
public void LoadPattern()
{
if (Pattern == null) return;
Pattern.WPFSlice = Pattern.BuildImageAtZ(Pattern.ZNorm);
}
In your class where this is
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; }
}
you have to implement INotifyPropertyChanged.
Example
public class YourClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; OnPropertyChanged(new PropertyChangedEventArgs("Pattern"));}
}
}
EDIT
In your Pattern-class, you have to implement that too on every Property.
I have two MediaElements created in my ViewModel and bound in my View. On MediaOne.MediaEnded(), I'm trying to fire another method. BUT, MediaEnded() never fires!
Here is the relevant code:
ViewModel:
private MediaElement _mediaOne;
public MediaElement MediaOne
{
get { return _mediaOne; }
set { _mediaOne = value; NotifyPropertyChanged(); }
}
private MediaElement _mediaTwo;
public MediaElement MediaTwo
{
get { return _mediaTwo; }
set { _mediaTwo = value; NotifyPropertyChanged(); }
}
public SpeechViewModel()
{
_mediaOne = new MediaElement();
_mediaTwo = new MediaElement();
MediaOne.MediaEnded += MediaOne_MediaEnded;
MediaTwo.MediaEnded += MediaTwo_MediaEnded;
}
private async void MediaOne_MediaEnded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// Do stuff
}
private async void MediaTwo_MediaEnded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
// Do other stuff
}
XAML
<ContentControl Content="{Binding mediaOne}"/>
<ContentControl Content="{Binding mediaTwo}"/>
I have a panel with a button on it that is used to trigger an image capture from an external camera. The capture can take several seconds, so I want the button to disable when capture is in progress. I also want to be able to prevent the user capturing when my program is running a control script. Here is my ViewModel class:
public class CameraControlViewModel : ViewModelBase
{
public CameraControlViewModel()
{
}
public CameraControlViewModel( DataModel dataModel )
: base( dataModel )
{
dataModel.PropertyChanged += DataModelOnPropertyChanged;
_captureImageCommand = new RelayCommand( captureImage );
_capturedImage = new BitmapImage();
_capturedImage.BeginInit();
_capturedImage.UriSource = new Uri( "Images/fingerprint.jpg", UriKind.Relative );
_capturedImage.CacheOption = BitmapCacheOption.OnLoad;
_capturedImage.EndInit();
}
public ICommand CaptureImageCommand
{
get { return _captureImageCommand; }
}
public bool CanCaptureImage
{
get { return !dataModel.IsScriptRunning && !_captureInProgress; }
}
public bool IsCaptureInProgress
{
get { return _captureInProgress; }
set
{
if (_captureInProgress != value)
{
_captureInProgress = value;
OnPropertyChanged( "IsCaptureInProgress" );
OnPropertyChanged( "CanCaptureImage" );
}
}
}
public int PercentDone
{
get { return _percentDone; }
set
{
if (_percentDone != value)
{
_percentDone = value;
OnPropertyChanged( "PercentDone" );
}
}
}
public BitmapImage CapturedImage
{
get { return _capturedImage; }
}
private void DataModelOnPropertyChanged( object sender, PropertyChangedEventArgs propertyChangedEventArgs )
{
string property = propertyChangedEventArgs.PropertyName;
if (property == "IsScriptRunning")
{
OnPropertyChanged( "CanCaptureImage" );
}
OnPropertyChanged( property );
}
private void captureImage( object arg )
{
IsCaptureInProgress = true;
PercentDone = 0;
// TODO: remove this placeholder.
new FakeImageCapture( this );
// TODO (!)
}
internal void captureComplete()
{
IsCaptureInProgress = false;
}
// Remove this placeholder when we can take images.
private class FakeImageCapture
{
CameraControlViewModel _viewModel;
int _count;
Timer _timer = new Timer();
public FakeImageCapture( CameraControlViewModel viewModel )
{
this._viewModel = viewModel;
_timer.Interval = 50;
_timer.Elapsed += TimerOnTick;
_timer.Start();
}
private void TimerOnTick( object sender, EventArgs eventArgs )
{
++_count;
if (_count <= 100)
{
_viewModel.PercentDone = _count;
}
else
{
Application.Current.Dispatcher.Invoke( (Action)_viewModel.captureComplete );
_timer.Stop();
_timer = null;
_viewModel = null;
}
}
}
private readonly ICommand _captureImageCommand;
private volatile bool _captureInProgress;
private BitmapImage _capturedImage;
private int _percentDone;
}
Here is the XAML for the button:
<Button Command="{Binding CaptureImageCommand}"
Grid.Row="0" Grid.Column="0"
Margin="4"
IsEnabled="{Binding CanCaptureImage}"
ToolTip="Capture Image">
<Image Source="../Images/camera-icon.gif" Width="64" Height="64" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Button>
Clicking the "capture" button goes fine. The button disables and elsewhere a progress bar appears showing the (currently faked) image capture progress. However, when the capture completes, even though I set the CanCaptureImage property in the captureComplete() method, the button does not change back to its "enabled" appearance. It will only do this when I click somewhere (anywhere) in the window. However, the button is actually enabled because I can click on it again to trigger a 2nd capture.
I have tried CommandManager.InvalidateRequerySuggested() inside captureComplete() but that doesn't help. Any ideas?
Rather than having a separate IsEnabled binding to enable/disable the button, you should really just use the CanExecute predicate of the RelayCommand: http://msdn.microsoft.com/en-us/library/hh727783.aspx
This would ensure that the button will get enabled/disabled properly when calling CommandManager.InvalidateRequerySuggested(). Get rid of the CanCaptureImage property and modify your code as follows:
public CameraControlViewModel( DataModel dataModel )
: base( dataModel )
{
dataModel.PropertyChanged += DataModelOnPropertyChanged;
_captureImageCommand = new RelayCommand( captureImage, captureImage_CanExecute );
_capturedImage = new BitmapImage();
_capturedImage.BeginInit();
_capturedImage.UriSource = new Uri( "Images/fingerprint.jpg", UriKind.Relative );
_capturedImage.CacheOption = BitmapCacheOption.OnLoad;
_capturedImage.EndInit();
}
private bool captureImage_CanExecute( object arg)
{
return !dataModel.IsScriptRunning && !_captureInProgress;
}