I tried to implement a DialogManager like described here. I'm not using caliburn, so I refactored a bit, and also it's no longer SimpleDialog but CustomDialog, but there weren't big changes.
So now when I click a button that calls DialogManager.ShowDialog and wait for the resulting task with task.Wait() the application just freezes (as aspected for wait) but without showing the dialog. I tried to debug, but it works alright until the line
await Application.Current.Windows.OfType<MetroWindow>().First().ShowMetroDialogAsync(dialog);
. dialog is a valid BaseMetroDialog and i get the right window from the Application.Cur... call. It also happens when I'm just calling ShowMessageAsync, so the problem seems to be in the connection of the call and the waiting for the task. Is there no way to really block the following execution (forcing the dialog to be modal)?
If you need additional information please comment, I'll extend the question, but right now I don't know what to show besides the code already linked at the beginning.
You mention task.Wait(). If you started a task to show the dialog, I'm afraid you can't do that. The GUI is single threaded. You can still await the result, just has to be initiated from the main thread, not a task.
This is all the gimmicks I had to do in order to get this to work without having to use async/await:
using SysThread = System.Threading;
using WpfThread = System.Windows.Threading;
using SysTasks = System.Threading.Tasks;
using MahCtl = MahApps.Metro.Controls;
using MahDlg = MahApps.Metro.Controls.Dialogs;
using Wpf = System.Windows;
.
.
.
private SysThread.CancellationTokenSource tokenSource;
.
.
.
MahCtl.MetroWindow parentMetroWindow = Wpf.Application.Current.Windows.OfType<MahCtl.MetroWindow>().First();
var metroDialogSettings = new MahDlg.MetroDialogSettings();
metroDialogSettings.OwnerCanCloseWithDialog = true; //does not appear to have any effect
metroDialogSettings.AnimateHide = false;
metroDialogSettings.AnimateShow = false;
using( tokenSource = new SysThread.CancellationTokenSource() )
{
metroDialogSettings.CancellationToken = tokenSource.Token;
SysTasks.Task<MahDlg.MessageDialogResult> task = MahDlg.DialogManager.ShowMessageAsync( parentMetroWindow, title, message, mahStyle, metroDialogSettings );
parentMetroWindow.Closing += onMainWindowClosing;
while( !(task.IsCompleted || task.IsCanceled || task.IsFaulted) )
Wpf.Application.Current.Dispatcher.Invoke( WpfThread.DispatcherPriority.Background, new Action( delegate { } ) );
parentMetroWindow.Closing -= onMainWindowClosing;
return task.Result;
}
.
.
.
private void onMainWindowClosing( object sender, SysCompMod.CancelEventArgs cancelEventArgs )
{
tokenSource.Cancel();
}
The extra handler for the Closing event of the main window is necessary in order to handle the situation where the user attempts to close the main window of your application via the task bar, and while a modal dialog box is open, because OwnerCanCloseWithDialog does not seem to have any effect, or it was never meant to do what the documentation tricked me into believing that it was perhaps meant to do.
Related
I need to click an okay button which might appear after completing a field - it might take 5 seconds to appear. So i need (if) Wait for existence 5 seconds. I'm using PageFactory in a pages framework, I've seen some solutions but cant figure out how to implement them in this context.
[FindsBy(How = How.Name, Using = "OK")]
private IWebElement alertOKBtn;
public void PopulateFields //method to populate the form
{
// Populate fields
dateFromField.SendKeys(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
// Click on this field
descriptionField.Click();
//OK button might appear, might take 5secs - pseudcode
if ( ***alertOKBtn exists, wait for it for 5 secs..*** )
{
alertOkBtn.Click();
}
//continue populating form
}
The PopulateFields method is called from the [Test] as:-
Pages.PTW.PopulateFields();
where Pages.PTW is a get method to PageFactory.InitElements(browser.Driver, page); return page;
Managed to resolve it - in PopulateFields i now do this:-
//wait to see if alert popup appears - give it 8 secs
string waitToSee = browser.wait(alertOKBtn, 8);
if ( waitToSee == "true" )
{
alertOKBtn.Click(); //alert popup did appear
}
Then I've added a method to my browser.class :-
public static string wait(IWebElement elem, int timeout ) //waits for existence of element up to timeout amount
{
try
{
var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(timeout));
wait.Until(ExpectedConditions.ElementToBeClickable(elem));
return "true";
}
catch (Exception e ) //didnt appear so exception thrown return false
{
return "false";
}
So it now waits up to 8 seconds and if it doesnt appear it ignores and moves on. Thanks Bendram for the pointers.
Need to add conditional wait. That means, your code should wait till the control appears and then perform the action.
WebDriverWait class which inherits DefaultWait class serves the purpose. The below is the code snippet.
var wait = new WebDriverWait(this.driver, waitTime);
wait.Until(ExpectedConditions.ElementToBeClickable(alertOkBtn));
alertOkBtn.Click();
PROBLEM
I am using a secondary view to run my media files, but When I close my secondary view with close button on it ( while media is still playing ) the secondary view/window closes but the media somehow keeps playing because I can hear the sound and source of sound seems to be the primary view ( main app window ). how can I completely terminate the secondary window when I close it?
TRIED
I followed windows samples multiple views and was able to complete all steps, I copied the ViewLifetimeControl.cs file from the sample and used it in my project. the code runs fine until it reaches Windows.Current.Close() in released event of the secondary view.
Then it gives an exception when it tries "Window.Current.Close()" with in the released event. according to documentation exception occurs due to any on going changes ( which might be because of media file playing ), but I need to force close the window even when media file is playing how can I do that? btw here is the exception :
Message = "COM object that has been separated from its underlying RCW cannot be used."
Code to Create and Show secondary view
internal static async Task CompactOpen(string Title, string caption)
{
ViewLifetimeControl viewControl = null;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
viewControl = ViewLifetimeControl.CreateForCurrentView();
viewControl.Title = Title;
viewControl.StartViewInUse();
var frame = new Frame();
frame.MinHeight = 200;
frame.MinWidth = 200;
frame.Navigate(typeof(CompactNowPlayingPage), new object[] { viewControl,caption});
Window.Current.Content = frame;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = viewControl.Title;
});
((App)App.Current).SecondaryViews.Add(viewControl);
var selectedView = viewControl;
var sizePreference = new SizePreferenceString() { Title = "SizePreference", Preference = ViewSizePreference.Default };
var anchorSizePreference = new SizePreferenceString() { Title = "AnchorSizePreference", Preference = ViewSizePreference.Default };
if (selectedView != null && sizePreference != null && anchorSizePreference != null)
{
try
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
sizePreference.Preference,
ApplicationView.GetForCurrentView().Id,
anchorSizePreference.Preference);
if (!viewShown)
{
// The window wasn't actually shown, so release the reference to it
// This may trigger the window to be destroyed
}
// Signal that switching has completed and let the view close
selectedView.StopViewInUse();
}
catch (InvalidOperationException)
{
// The view could be in the process of closing, and
// this thread just hasn't updated. As part of being closed,
// this thread will be informed to clean up its list of
// views (see SecondaryViewPage.xaml.cs)
}
}
}
Released Event
private async void ViewLifetimeControl_Released(Object sender, EventArgs e)
{
((ViewLifetimeControl)sender).Released -= ViewLifetimeControl_Released;
// The ViewLifetimeControl object is bound to UI elements on the main thread
// So, the object must be removed from that thread
await mainDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
((App)App.Current).SecondaryViews.Remove(thisViewControl);
});
// The released event is fired on the thread of the window
// it pertains to.
//
// It's important to make sure no work is scheduled on this thread
// after it starts to close (no data binding changes, no changes to
// XAML, creating new objects in destructors, etc.) since
// that will throw exceptions
Window.Current.Close(); //this is where that exception occurs
}
Note : both of above methods and even all the related variables, all of them I have followed the guidelines within the uwp sample for multiple views.
Thanks in advance, any help would be really appreciated, I only want to force close the secondary view ( If that's possible )
Is this in the editor or the app? If it's in your debug or build of the app, the secondary view is most likely still open but hidden. You may be using a custom close button which doesn't perform its job well enough. Instead of putting down SecondaryViews.Remove you should do what you had originally written and try StopViewInUse. It may not work, I'm not used to this kind of thing.
I am looking for some help with my MultiSourceFrameArrived event in the Kinect v2 SDK.
The following is the method in question:
private async void _reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
{
MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();
using (var colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame())
{
if (colorFrame != null)
{
_writeableBitmap.Lock();
colorFrame.CopyConvertedFrameDataToIntPtr(
_writeableBitmap.BackBuffer,
(uint)(_colorFrameDescription.Width * _colorFrameDescription.Height * _colorFrameDescription.BytesPerPixel),
ColorImageFormat.Bgra);
_writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, _writeableBitmap.PixelWidth, _writeableBitmap.PixelHeight));
_writeableBitmap.Unlock();
reflectionOverlayControl.ReflectionImageSource = _writeableBitmap;
}
}
using (var bodyFrame = multiSourceFrame.BodyFrameReference.AcquireFrame())
{
if (bodyFrame != null)
{
Body body = JointHelpers.FindClosestBody(bodyFrame);
if (body != null)
{
if (body.IsTracked)
{
Dictionary<BodyComponentType, BodyComponent> bodyComponentDictionary = BuildBodyComponentDictionary(body);
foreach (BodyComponent bodyComponent in bodyComponentDictionary.Values.OrderBy(x => x.BodyComponentType))
{
bodyComponent.Generate(_writeableBitmap, _coordinateMapper, FrameType.Color, 25);
if (!_isRunningFiltering)
{
_isRunningFiltering = true;
try
{
await Task.Run(() =>
{
bodyComponent.RunFunFiltering();
});
}
finally
{
_isRunningFiltering = false;
}
}
}
reflectionOverlayControl.UpdateValues(
bodyComponentDictionary,
GetFullBodyComponent(body));
}
}
}
}
}
Now, allow me to explain:
The method runs when a particular kind of frame arrives from the Kinect, this is acquired and I can extract the ColorFrame and BodyFrame out of it in the using blocks.
The first "using" block turns the ColorFrame into a WPF WriteableBitmap (declared in the constructor) and sets a user control's ReflectionImageSource set equal to this WriteableBitmap. If this were the only using block, I would see a very smooth feed on the screen!
The second BodyFrame using determines the closest body, if it is tracked and then creates a Dictionary populated with a persons BodyComponents (hands, feet, head etc.)
The foreach loop here runs the "Generate" function on each BodyComponent, which sets a few of it's properties. For example, it sets an EncompassingRectangle property which is an Int32Rect object designed to encompass the component.
The next bit is where I need help!
The method RunFunFiltering is a heavily intensive processing method which, when run, would create a blocking statement that freezes up my UI. This would have the effect of making my color frame video feed very jumpy! This RunFunFiltering method needs to set some of the BodyComponent class's properties, such as the colour that the rectangle should be displayed, the number of white pixels in it's ReflectionImageSource and to set another writeable bitmap with the part of the first ReflectionImageSource which is contained in the rectangle.
Since this object is now complete, with all properties set (and this has been done for each of the BodyComponent's in the dictionary) I run an UpdateValues method on the view, which displays the interesting stuff in the BodyComponent class on the screen for me.
Following some advice from #sstan in this post: Async Await to Keep Event Firing
I threw in a Task.Run() block. However, this doesn't seem to be releasing my UI and I still see a jumpy image. The weird thing is in that timer example, that it works perfectly! I'm at a bit of a loss here to know what to do.
I'm a bit of a beginner with asynchronous functions but I would really like to understand your solutions. If you can provide an explanation with your code I'd be extremely grateful!
Update
I have been able to identify that the using statement which acquires the frame blocks the UI when it is placed outside of the Task.Run call.
I can't just make the whole BodyFrame using block run asynchronously because I need the first "Generate" function to always happen and not be part of the heavy processing thread. Two using blocks seems inelegant and is rather pushing my question under the carpet...
From your comment I understand the following:
You have an async function that is called when a frame arrives
If no RunFunFiltering task is running start one
If such a task is running, don't start a new one
If RunFunFiltering is finished Process the result
.
Task taskFunFiltering = null;
private async Task ProcessFrame(...)
{ // a new frame is arrived
DoSomeProcessing(...);
// only start a new run fun filtering if previous one is finished
if (taskFunFiltering == null || taskFunFiltering.IsCompleted)
{ // start a new fun filtering
// don't wait for the result
taskFunFiltering = Task.Run( () => ...);
}
}
private async Task RunFunFiltering(...)
{
// do the filtering and wait until finished
var filterResult = await DoFiltering(...);
DisplayResult(filterResult);
}
Got some problem and cant't get why it appears.
I'm using .net 4.5 / C# and I try to set Content to a ContentControl after an async function succeeded.
The main focus of what i want to to in that part of the programm is to switch between an own WPF Loading animation (Usercontrol IsLoading) and a PDF Content (Usercontrol PDFDokument). The PDF is internal loaded & rendered inside the "PDFDokument" and works already very well.
If more information is needed, every asking is welcome.
For you to know i would say i'm still at a beginning level of developing. (the first of three years :) )
public ucPDFDocument PDFDokument = new ucPDFDocument();
public ucLoading IsLoading = new ucLoading();
protected async void lstSuchergebnis_DoubleClickItem(object sender, MouseButtonEventArgs e)
{
var tempAkte = ((ListViewItem)sender).Content as Akten;
tbctrlResultPanel.SelectedIndex = 1;
PDFDokument.IsDataChangeAllowed(false);
contSwapControls.Content = IsLoading;
await PDF(tempAkte);
contSwapControls.Content = PDFDokument; **<-- after executing this line, the ui freezes**
}
private Task PDF(Akten paramAkte)
{
Akten _tempAkte = paramAkte;
return Task.Run(() => { PDFDokument.LoadPDFDokument(_tempAkte.akt_ID, ref _DaKsManger); });
}
I tried different ways of using that async loading, but nothing solved that problem.
Hope someone got an idea how to solve that :)
Thanks a lot!!!
The only thing that is async is PDF(...), which you await. Setting the content is not being executed in an async manner. The content you are setting happens on the UI thread. (which is the only way to do so, since you are modifying the UI, which can only happen on the thread it is created on)
I need to be able to let multiple instances of the same form be open as my application can be used in different places at once. On the other hand I need to be able to process the operations during the "OK" event one at a time to ensure data is stored safely and not overwritten by another form instance by accident.
I show my form using the .Show() method as I am using a few delegates in it:
private void newToolStripMenuItem_Click(object sender, EventArgs e)
{
bookingForm = new BookingForm(AddMemberBooking, AddUserBooking, CloseBooking);
bookingForm.Show();
}
I have tried to use the mutex to allow only one event of the OK button being pressed happen at a time, i have combined this with a Thread to meet the criteria i need.
When i click on the "OK" button I am given the following error:
Cross-thread operation not valid: Control 'comboBoxDay' accessed from a thread other than the thread it was created on.
This is the code for my booking form class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace Collection
{
//Allows the class to be serialized
[Serializable()]
public delegate void AddMemberBookingMethod(int date, int time, int mNo);
public delegate void AddUserBookingMethod(int date, int time, string fName, string lName, string pCode);
public delegate void CloseBookingFormMethod();
public partial class BookingForm : Form
{
public CloseBookingFormMethod CloseBookingForm;
public AddMemberBookingMethod AddMemberBooking;
public AddUserBookingMethod AddUserBooking;
private Mutex bookingMut = new Mutex();
private Thread thread;
public bool IsUser;
public BookingForm(AddMemberBookingMethod ambm, AddUserBookingMethod aubm, CloseBookingFormMethod cbfm)
{
InitializeComponent();
AddMemberBooking = ambm;
AddUserBooking = aubm;
CloseBookingForm = cbfm;
checkBoxMember.Checked = true;
//Control.CheckForIllegalCrossThreadCalls = false;
}
private void checkBoxUser_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxUser.Checked)
{
IsUser = true;
checkBoxMember.CheckState = CheckState.Unchecked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
else
{
IsUser = false;
checkBoxMember.CheckState = CheckState.Checked;
textBoxMNo.Enabled = true;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
}
private void checkBoxMember_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxMember.Checked)
{
IsUser = false;
checkBoxUser.CheckState = CheckState.Unchecked;
textBoxFName.Enabled = false;
textBoxLName.Enabled = false;
textBoxPCode.Enabled = false;
}
else
{
IsUser = true;
checkBoxUser.CheckState = CheckState.Checked;
textBoxMNo.Enabled = false;
textBoxFName.Enabled = true;
textBoxLName.Enabled = true;
textBoxPCode.Enabled = true;
}
}
private void buttonOK_Click(object sender, EventArgs e)
{
this.thread = new Thread(new ThreadStart(MakeBooking));
this.thread.Name = "bookingThread";
this.thread.Start();
}
private void MakeBooking()
{
this.bookingMut.WaitOne();
int date = this.comboBoxDay.SelectedIndex;
int time = this.comboBoxTime.SelectedIndex;
if (IsUser)
{
string fName = textBoxFName.Text;
string lName = textBoxLName.Text;
string pCode = textBoxPCode.Text;
AddUserBooking(date, time, fName, lName, pCode);
}
else
{
int mNo = int.Parse(textBoxMNo.Text);
AddMemberBooking(date, time, mNo);
}
this.bookingMut.ReleaseMutex();
CloseBookingForm();
}
private void buttonClose_Click(object sender, EventArgs e)
{
CloseBookingForm();
}
}
}
I realise I may not be doing this in the most efficient way but time is a bit of a factor.
I've researched the error and have heard of using delegates and .Invoke() but I'm still not entirely sure how to fix it.
EDIT:
I've found this code snippet when searching for a fix to my problem. I don't understand where/how I would use it.
if(this.InvokeRequired)
{
this.Invoke(new MyEventHandler(this.CreateAForm()));
return;
}
EDIT2:
Seems the guy finally saw sense, by creating the from with the new word it apparently passes the criteria. I wish I'd have known this before trying to reinvent the wheel.
You are getting this exception because your thread is accessing controls. That's not legal, control properties must only ever be accessed from the UI thread. You're okay on the TextBox.Text property, that one happens to be cached. But not ComboBox.SelectedIndex. And closing the form from another thread is going to bomb too.
Your mutex has nothing to do with it, but keep it if you want to prevent threads from overlapping. Using a delegate's Invoke method isn't going to solve it, that just starts a thread as well. You'll need to collect the info that the thread is going to need in a little helper class and pass that as the argument to the Thread.Start() method.
Closing the form is a bit tricky too, the user might well have already closed it while the thread was running. That's going to cause an ObjectDisposed exception. A quick fix is to set the form's Enabled property to false so the user can't close it. You'll need to use the form's Invoke() method to ensure the closing is done on the UI thread.
Last but not least, if these threads don't take a lot of time (a second or so), consider not using threads at all and display a wait cursor instead.
One simple way to do this is to use the overload of the Thread.Start method that accepts an object: Thread.Start Method (Object). In this object you will store all the data/state necessary in order to make the update.
All the code that references the form and its controls needs to be moved into the OK click event method or refactored out to a method that just returns a data object. Then pass this object into the thread start method.
Some pseudo code:
on_click_event()
{
object data=getFormData();
thread.start(data);
}
There are better ways to do this but this is a quick fix for your code.
I think you could simply disable the OK buttons on other open forms to give users a visual cue. Then you shouldn't even have the issue. Provide a callback delegate to something in the application controller which knows which forms are open. Each form can provide a public method to disable the OK button. Disable to OK button on all the other forms.
I'm not really following your code too well. I would think the mutex could be outside of the form code in the first place (i.e. in the delegates that do the actual work), and if it is within a single application, you could just use the lock (object) method to ensure only one thread is executing a given bit of code.
I'd also like to add that a mutex is not going to stop multiple users on different machiens being able to click OK at the same time. I'm not sure if that's what you meant in your question by a form being run in different places.
I think that AddUserBooking and the other delegate should be responsible for ensuring that they are threadsafe and this should not be part of the UI. If they aren't threadsafe, why aren't they? It's relatively easy to make database commit functions each have their own connection to the database during their operations and thread-safety should not be an issue.