Identify that a Window is completely hidden - wpf

In order to create a screen shot, I am hiding a wpf window. The code looks like that.
Hide();
var fullScreenshot = _cropper.TakeFullScreenshot();
Show();
Sometimes the Application is not hidden when the screen shot is taken. How can I can I identify, that the window is completely hidden?

I don't know how the screenshot is taken but I suspect that the UI-Thread has not removed all the content and therefore the TakeFullScreenshot sees rests of your app.
I would try to wait until your app has done all necessary ui-work and then trigger the TakeFullScreenshot-operation.
Trigger the ScreenShot-Operation with the Dispatcher:
Hide();
Dispatcher.BeginInvoke(new Action(delegate {
fullScreenshot = _cropper.TakeFullScreenshot();
Show();
}), System.Windows.Threading.DispatcherPriority.ContextIdle, null);

Currently I am trying out this solution:
public void Foo()
{
IsVisibleChanged += WhenVisibiltyChangend_TakeScreenshot_and_OpenCreateTicketDialog;
Hide();
}}
void WhenVisibiltyChangend(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == false) {
var fullScreenshot = _cropper.TakeFullScreenshot();
Show();
}
}
I hope this is the correct answer, but I have to do some additional tests.

Related

How to hide a window using another thread?

I've create a WPF application with NotifyIcon, and I use this.Hide() to make it minimized, but now I hope it could be minimized once it had been executed, so I invoke this.Hide() in the method MainWindow_Loaded, but once I start the app, the content of the window turn to be all dark.
Then I try to invoke this.Hide() in another thread, and this is what my code looks like...
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
...
Thread thread = new Thread(DoWork);
thread.Start();
Console.WriteLine("thread start");
while (!thread.IsAlive) ;
Thread.Sleep(500);
thread.Join();
Console.WriteLine("thread has terminated.");
...
}
public void DoWork()
{
this.Hide();
}
then I encounter the problem, when DoWork() is invoked, it showed Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on. What should I do to avoid this? Thanks!
You need to use Dispatcher object.
Dispatcher.BeginInvoke((Action) (() =>
{
// your code
}));

Using WPF Extended Toolkit MessageBox from other threads and curious behavior

I am having trouble using the WPF Extended Toolkit (version 2.1.0.0) MessageBox from other threads. The namespace is: Xceed.Wpf.Toolkit.MessageBox
I replaced my regular MessageBoxs (System.Windows.MessageBox) with the Toolkit MessageBox and get errors when I launch one from another thread. The System.Windows.MessageBox has no such problems. I saw this posting that reports the problem, but there seems to be no follow up:
https://wpftoolkit.codeplex.com/workitem/21046
I'm guessing there is a work around. An example is presented there that shows the problem, but here is my simple example:
First, I wrap the Toolkit.MessageBox. I do this primarily because I'm applying style (although I've commented that out to show that's not the problem)
public class CustomMessageBox
{
//static DummyUserControl1 _ctrl = new DummyUserControl1();
public static MessageBoxResult Show(string msgText, Style styleArg = null)
{
Cursor saveCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = null;
//Style style = styleArg != null ? styleArg : _ctrl.FindResource("MessageBoxStyle1") as Style;
// MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK, style);
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK);
Mouse.OverrideCursor = saveCursor;
return result;
}
}
The main window just has two buttons on it, and here's the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnMainThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
CustomMessageBox.Show("Hello on main thread");
}
private void btnAltThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
Thread altThread1 = new Thread(new ThreadStart(AltThread1Proc));
altThread1.SetApartmentState(ApartmentState.STA);
altThread1.Priority = ThreadPriority.AboveNormal;
altThread1.IsBackground = true;
altThread1.Start();
}
public void AltThread1Proc()
{
MessageBox.Show("Hello on Alt Thread");
CustomMessageBox.Show("Hello on alt thread");
}
}
The problems occur in AltThreadProc() with CustomMessageBox.Show(...). The curious behavior I referred to is this: If you hit the main thead button and then the Alt thread button, you get the error:
Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.
However, if you skip the main thread button and just hit the Alt thread button, you get the error:
The calling thread cannot access this object because a different thread owns it.
I'm curious what the "Freezable" error is all about and why you can get different errors based on what would seem to be an innocuous event: clicking/not clicking button that produces message box on main thread.
Ideally, it would be nice to just replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox, but if there is some sort of extra code to write, that might be acceptable. The documentation, and the link I provided hints at using a WindowContainer, but I can't really see any examples of how you do that. I was attracted to the Toolkit MessageBox as it allows one to do some cool stuff with MessageBox (which I don't show here) such as apply styles, change the text of the OK, CANCEL button, etc.
Any ideas would be much appreciated.
Thanks,
Dave
Extra info:
User1341210 suggestion works well if you just have one window. However, if you have a second window in it's own thread it doesn't work so well. Perhaps someone can tell me what I'm doing wrong. I use the suggestion of the TaskScheduler, but the code throws an exception if the TaskScheduler used is the one of the second window. That is, all works fine if I use the TaskScheduler of the first window, but throws an exception if I use the TaskScheduler of the second window. Here is the code behind for my second window:
public partial class AltThreadWindow : Window
{
private TaskScheduler _ui;
public AltThreadWindow()
{
InitializeComponent();
_ui = TaskScheduler.FromCurrentSynchronizationContext();
}
// This constructor is for passing in the TaskScheduler of the mainwindow and works great
public AltThreadWindow(TaskScheduler scheduler)
{
InitializeComponent();
_ui = scheduler;
}
private void btnWindowsMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Standard Windows message box");
}
private void btnCustomMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result;
Task.Factory.StartNew(() => { result = CustomMessageBox.Show("Custom MessageBox on separate window"); }, CancellationToken.None,
TaskCreationOptions.None,
_ui);
}
}
Notice the two constructors. The default one assigns the TaskScheduler of the second window. The other constructor allows one to pas in the TaskScheduler of the main Window.
Here's the code I use to launch the second window from the main window. Again, I'm launching the second window on another thread, and I pass in the TaskScheduler of the main window. It would be nice to use the TaskScheduler of the second window instead.
_altWindowThread = new Thread(new ThreadStart(AltWinThreadProc));
_altWindowThread.SetApartmentState(ApartmentState.STA);
_altWindowThread.Priority = ThreadPriority.AboveNormal;
_altWindowThread.IsBackground = true;
_altWindowThread.Start();
And the actual threadproc:
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
public void AltWinThreadProc()
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
_altWindow = new AltThreadWindow(_ui);
_altWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
Notice here I pass in the TaskScheduler of the MainWindow.
we had the same issue in our application (I created the work item on codeplex).
The error messages are quite confusing and I cant provide you an answer to that.
But:
We didn't used a separated WindowContainer to solve it. Instead came up with calling the separate task/thread with the UI scheduler:
Task.Factory.StartNew(
() => { result = CustomMessageBox.Show(messageText); },
CancellationToken.None,
TaskCreationOptions.None,
_ui);
Where _ui is assigned in a method that is executed from UI context (e.g. Constructor of your Window/Control:
_ui = TaskScheduler.FromCurrentSynchronizationContext();
Hope this helps for solving the "replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox" part of your question.
If you want that the messagebox shows up on another Window you have to set the "Owner" property of the message box to the other window.
Best regards.

Modal Messagebox from background thread

I've noticed that there seems to be inconsistent behavior as to when a MessageBox is modal.
First up, launching a MessageBox from the UI thread. This results in a modal MessageBox, as expected :
void MainThreadClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello!");
}
Next up, launching from a background thread. This results in a Modeless MessageBox, I assume because it's not on the UI thread?
void WorkerThreadClick(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem((x) =>
{
MessageBox.Show("Hello!");
});
}
Next, launching from a background thread, but dispatched to the UI thread, results in it being modal again :
void WorkerThreadClick(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem((x) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
MessageBox.Show("Hello!");
});
});
}
And finally, this is the strange one, similar to above, but using the FileSystemWatcher thread results in a Modeless dialog. Why is this? ... it's being Invoked on the UI thread, so why isn't it Modal like the previous example?
public MainWindow()
{
InitializeComponent();
m_watcher = new FileSystemWatcher()
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
NotifyFilter = NotifyFilters.LastWrite,
IncludeSubdirectories = true,
Filter = "*.*"
};
m_watcher.Changed += OnFileSystemResourceChanged;
m_watcher.EnableRaisingEvents = true;
}
void OnFileSystemResourceChanged(object _sender, FileSystemEventArgs _args)
{
Application.Current.Dispatcher.Invoke(() =>
{
MessageBox.Show("Hello!");
});
}
Whilst I can solve the last problem using the MessagBox.Show() method that takes a Window owner as a parameter, I want to understand what's going on.
Why is the behavior different in the last 2 examples?
This question has indeed stumped me for some time. On doing some analysis I found that in the last case (FileSystemWatcher) the owner changed(I have not yet figured who has taken over the ownership).
I also found that there is a minor but important difference.
In the scenario number 2
void WorkerThreadClick(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem((x) =>
{
MessageBox.Show("Hello!");
});
}
Even though the behaviour is Modeless when ever I close my MainWindow my application is also shut down.
In the FileSystemWatcher scenario the behaviour is again Modeless but when I close my MainWindow the application is not shut down unless I close the MessageBox(so I know that someone has taken over the ownership. Who has taken it I don't know yet).
EDIT
I changed the Shutdown mode in the last scenario
void OnFileSystemResourceChanged(object sender, FileSystemEventArgs args)
{
Application.Current.Dispatcher.Invoke(() =>
{
Application.Current.ShutdownMode=ShutdownMode.OnMainWindowClose;
MessageBox.Show("Test");
});
}
Even then when I close the MainWindow my Application is not closed unless the MessageBox is closed. I tried finding the owner but then I get null reference exceptions.

throwing out touches in WPF

I'm trying to eliminate TouchDevices in a WPF for Surface so I can ignore non-finger touches because blobs seem to trigger events I don't want.
At first I had something simple like this
private void SurfaceWindow1_PreviewTouchDown(object sender, TouchEventArgs e)
{
if (!e.TouchDevice.GetIsFingerRecognized() && InteractiveSurface.PrimarySurfaceDevice.IsFingerRecognitionSupported == true)
{
e.Handled = true;
}
}
Which works well to stop touch interactions with things like inside ScatterViewItems and manipulations. however there must be something else that happens before PreviewTouchDown because I can use a blob to activate an SVI and bring it to the top although no other manipulations occur. I'm guessin TouchEnter on the SVI still shows up and brings it forward but handling TouchEnter on all the elements gives me the same thing so there's still something else going on.
I looked into Touch.FrameReported but I can't release the proper TouchCaptures before the SVIs hear about it
private void myTouchFrameHandler(object sender, TouchFrameEventArgs e)
{
foreach (TouchPoint _tp in e.GetTouchPoints(this)) {
if (!_tp.TouchDevice.GetIsFingerRecognized())
{
this.ReleaseAllTouchCaptures();
}
}
}
Any ideas?
Thanks
I stumbled upon the same problem and implemented an attached behavior for ScatterViewItems. This behavior disables the automatic IsTopmostOnActivation behavior and listens to PreviewTouchDown events to decide if an item is to be brought to top based on a test condition. It features an easy to use activation method
CustomTopmostBehavior.Activate();
which adds an application-wide style enabling the behavior for all ScatterViewItems.
The behavior can be customized by setting its TestCondition property which is by default:
CustomTopmostBehavior.TestCondition = (t) =>
{
return t.GetIsFingerRecognized();
};
Ok here's my dirty workaround to stop Touches from advancing when they're not recognized as fingers, and stopping SVIs from rising to the top when hovered over.
this.PreviewTouchDown += new EventHandler<System.Windows.Input.TouchEventArgs>(SurfaceWindow1_PreviewTouchDown);
SVI.TouchEnter += new EventHandler<TouchEventArgs>(SVI_TouchEnter);
SVI.TouchLeave +=new EventHandler<TouchEventArgs>(SVI_TouchLeave);
void SurfaceWindow1_PreviewTouchDown(object sender, System.Windows.Input.TouchEventArgs e)
{
if (!e.TouchDevice.GetIsFingerRecognized() && Microsoft.Surface.Presentation.Input.InteractiveSurface.PrimarySurfaceDevice.IsFingerRecognitionSupported) { e.Handled = true; }
else
{
//normal stuff
}
}
private void SVI_TouchEnter(object sender, TouchEventArgs e)
{
ScatterViewItem svi = sender as ScatterViewItem;
if (!e.TouchDevice.GetIsFingerRecognized() && Microsoft.Surface.Presentation.Input.InteractiveSurface.PrimarySurfaceDevice.IsFingerRecognitionSupported == true)
{
svi.IsTopmostOnActivation = false;
e.Handled = true;
}
else
{
foreach(ScatterViewItem svi in mainScatterView.Items.SourceCollection){
svi.IsTopmostOnActivation = false;
}
SVI.IsTopmostOnActivation = true;
}
}
private void SVI_TouchLeave(object sender, TouchEventArgs e)
{
ScatterViewItem svi = sender as ScatterViewItem;
svi.IsTopmostOnActivation = true;
}
I feel gross just coming up with such an unclever method. But since there's no tunneling TouchEnter, you would have to check all the visual tree objects that get a TouchEnter, which would even worse. Plus I just couldn't figure out how to use the protected method TouchDevice.Deactivate() anyway. But since the SVIs capture the touchDevice anyway and get restacked the only way I found to keep them in place is with the TopmostOnActivation Property, then catch the PreviewTouchDown on the Window and throw out non-fingers.
There's got to be a better way to get into the touch hierarchy, right?
OK so I've dug deeper into the touch hierarchy.
First of all TouchEnter happens before all the TouchDowns occur, but there's no tunneling event for that. TouchFrameHandler occurs after all the events are done, so throw that out.
Then I realized Releasing Captures on UIElements doesn't really make a difference for my problem because the Touch is already Captured. So I need to eliminate the TouchDevice in TouchEnter on every element. There is a Deactivate method in TouchDevice but it's protected. If I can figure out how to Deactivate a TouchDevice I think that should do the trick. Does that sound reasonable? If so I have to figure out how to override a protected method.

Drag a custom activity in a re-hosted workflow designer

Hello
I have a problem in my Design which is in this http://archive.msdn.microsoft.com/wfxbap/Release/ProjectReleases.aspx?ReleaseId=4668
I have made a custom activity in my Re-hosted workflow designer in WPF, and I make this custom activity shows in the end-user toolbar with the other activites, but the custom one can't be drop into the sequence at all like other activities.
I put the AllowDrop="True" in the .XAML file and the following in the .cs file:
protected override void OnDragEnter(DragEventArgs e)
{
//Check the object is actually something we want to be droppable
if (DragDropHelper.AllowDrop(
e.Data,
this.Context,
typeof(Activity)))
{
e.Effects = (DragDropEffects.Move & e.AllowedEffects);
e.Handled = true;
}
base.OnDragEnter(e);
}
protected override void OnDragOver(DragEventArgs e)
{
//Check the object is actually something we want to be droppable
if (DragDropHelper.AllowDrop(
e.Data,
this.Context,
typeof(Activity)))
{
e.Effects = (DragDropEffects.Move & e.AllowedEffects);
e.Handled = true;
}
base.OnDragOver(e);
}
protected override void OnDrop(DragEventArgs e)
{
//droppedItem - may be a ModelItem or a newly instantiated object (from toolbox)
object droppedItem = DragDropHelper.GetDroppedObject(this, e, this.Context);
ModelItem canvasActivity = this.ModelItem;
canvasActivity.Properties["Children"].Collection.Add(droppedItem);
e.Handled = true;
DragDropHelper.SetDragDropCompletedEffects(e, DragDropEffects.Move);
base.OnDrop(e);
}
Any help please?
I realize this post is old but I just ran across this problem as well so thought I would post for anyone else who stumbles upon this. I found that adding your custom Activity library to the same binary path as your rehosted designer should do the trick.

Resources