I'm looking for something similar with 'Form.Deactivate' event but per application. If I use Form.Deactivate event on my MainForm this event is fired even when I open a modal dialog that has as parent my MainForm.
In conclusion I nedd an event that is fired when my application was deactivated.
It is an odd omission but easily fixed. Paste this in your startup form:
protected void OnActivateApp(bool activate) {
Console.WriteLine("Activate {0}", activate);
}
protected override void WndProc(ref Message m) {
// Trap WM_ACTIVATEAPP
if (m.Msg == 0x1c) OnActivateApp(m.WParam != IntPtr.Zero);
base.WndProc(ref m);
}
Related
I am planning to have a threadsafe observable collection, where in I run a task on the background and update the UI as and when result is obtained using the dispatcher.
I got a download from the internet which is ThreadSafe. But I have a small concern. I wish to do a silent update on the UI. Since the user is already working or selecting the bound collection, I would not want to disturb the user selected entry. In other words, I would like to add an entry similar to Microsoft Outlook, when a new mail arrives.
Is this possible, and are there any such examples.
Thanks
You can have derived class from ObservableCollection and override it OnCollectionChanged function and raise it handler when ever you require. follows are code.
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged;
private bool suspendCollectionChangeNotification;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!this.suspendCollectionChangeNotification)
{
NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk through invocation list
bool isEventInvoked = false;
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
isEventInvoked = false;
if (handler.Target is DispatcherObject)
{
DispatcherObject dispatcherObject = handler.Target as DispatcherObject;
// This check is to make sure if the call on a different thread than the dispatcher.
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
{
// Invoke handler in the target dispatcher's thread
// Intentionally called begin invoke because there is a problem with Dispatcher.Invoke that it hangs when
// called simultaneously
dispatcherObject.Dispatcher.BeginInvoke(DispatcherPriority.DataBind, handler, this, e);
isEventInvoked = true;
}
}
if (!isEventInvoked)
{
handler(this, e);
}
}
}
}
}
I have a Silverlight 4.0 datagrid, which has the SelectionMode set to Single. The problem with this is that users need to CTRL+Click on an already-selected row in order to deselect it (and have nothing selected in the grid). I'd like for them to be able to simply left-click on the already-selected row to have it deselected.
I tried doing this with a SelectionChanged event (inspecting the added items in the event arguments), however it didn't work because the event isn't thrown when the user clicks on the same row twice.
Any advice?
There is no way to capture the second event because it is never fired. What you could do is apply the type of customization used in this project to one that does capture the second click and fire the event a second time should you wish:
http://www.codeproject.com/KB/silverlight/doubleClickDataGridSL.aspx
I have the same task, so here is my solution:
attach handler for datagrid's MouseLeftButtonDown event using AddHandler dataGrid.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(DataGrid_MouseLeftButtonDown), true);
, save SelectedIndex in private variable
private int prevSelectedIndex;
void DataGrid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (prevSelectedIndex != -1 && prevSelectedIndex == dataGrid.SelectedIndex)
{
dataGrid.SelectedIndex = -1;
}
prevSelectedIndex = dataGrid.SelectedIndex;
}
if you want reuse this logic you can create Behavior for DataGrid type
Add System.Windows.Interactivity assembly reference, add class DataGridSecondClickUnselectBehavior
public class DataGridSecondClickUnselectBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown), true);
}
private int prevSelectedIndex;
void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (prevSelectedIndex != -1 && prevSelectedIndex == AssociatedObject.SelectedIndex)
{
AssociatedObject.SelectedIndex = -1;
}
prevSelectedIndex = AssociatedObject.SelectedIndex;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown));
}
}
Now after you compile solution in blend you can add this behavior simply Drag'n'drop from Assets->Behaviors to DataGrid control
I have a grid in my application. After user selects some files in ofdialog application processes some calculations. While app is making calculations it looks like it is not responding. How to display some picture and make main window in black&white while calculating? Maybe make some dp in MainWindow a la "IsBusy" and bind a popup with picture to it?
How you implement this logic in yours apps?
One easy way is to use the busy indicator from Extended WPF Toolkit:
Dowload the binaries and add project reference to WPFToolkit.Extended.dll.
Next add following namespace in your 'main window':
xmlns:ext="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit.Extended"
Then add the busy indicator in the view (place it so that when shown, it will occupy the whole screen) Here my main window has two rows and I want the control to span on both rows. The control's IsBusy property is bound to a bool property in the view's data context.
<ext:BusyIndicator Grid.RowSpan="2" x:Name="busyIndicator" IsBusy="{Binding IsBusy}" />
The long lasting calculation should be processed in another thread so that it won't block the user interface. For threading you can use BackgroundWorker class.
You should have the long running tasks in a seperate thread to avoid UI blocking.
Here's one way you could achieve that:
Define background thread as below:
//Delegate that you could pass into the worker thread
public delegate void ProgressMonitor(string s);
//Call this to start background work
void StartLongRunningWork(ProgressMonitor mon)
{
using (BackgroundWorker bgw = new BackgroundWorker())
{
bgw.DoWork += WorkerThread;
bgw.RunWorkerCompleted += WorkerThreadCompleted;
bgw.RunWorkerAsync(mon);
}
}
void WorkerThread(object sender, DoWorkEventArgs e)
{
ProgressMonitor pm = (ProgressMonitor)e.Argument;
WorkerActual(pm, <any other parameters>);
}
void WorkerActual(ProgressMonitor pm,<any other parameters>)
{
...
pm("Doing x");
Do long running task
pm("Doing y");
...
}
//This function is called in case of Exception, Cancellation or successful completion
//of the background worker. Handle each event appropriately
void WorkerThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Long running task threw an exception
}
else
if (e.Cancelled)
{
//Long running task was cancelled
}
else
{
//Long running task was successfuly completed
}
}
And Call it as below:
private void UpDateProgressLabel(string s)
{
this.Dispatcher.BeginInvoke((Action)delegate
{
NotificationLabel.Content = s;
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
StartLongRunningWork(UpDateProgressLabel);
}
I've made a rather complex Silverlight 4 out-of-browser application. One of my main view models adds an event handler to the Application.Current.MainWindow.Closing event.
This works fine when the application is initially run. It is able to cancel the close operation.
However, sometimes after performing operations like showing and closing a ChildWindow, the MainWindow's Closing event is no longer calling my handler.
In the debugger, I added a watch to the MainWindow's underlying closing event delegate. It's not null before showing the ChildWindow. Then sometimes after the ChildWindow is closed the delegate is null. This is explains why my handler is not called any more. But why is this delegate getting nulled? And why is it only happening occasionally?
My application is not unbinding my event handler at any point.
This is the delegate I'm watching:
System.Windows.Application.Current.MainWindow.m_closingEvent
Other stuff: I'm using Caliburn Micro
I had the exact same problem. We have a large silverlight application running OOB.
For some reason the m_ClosingEvent was nulled after running for a while. I have not been able to find the cause of this issue but I think it may have something to do with us changing the root visual or all the child windows we show.
I´m using a class ApplicationWrapper.
public class ApplicationWrapper : IApplicationWrapper
{
public void Initialize()
{
HookCloseEvent(true);
}
private void HookCloseEvent(bool hook)
{
if (hook && IsRunningOutOfBrowser)
{
Application.Current.MainWindow.Closing += OnClosing;
}
else
{
if (IsRunningOutOfBrowser)
{
Application.Current.MainWindow.Closing -= OnClosing;
}
}
}
private void OnClosing(object sender, ClosingEventArgs e)
{
InvokeClosing(e);
}
... etc..
}
And the InvokeClosing method was never called. But when I changed it to
public class ApplicationWrapper : IApplicationWrapper
{
private Window _mainWindow;
public void Initialize()
{
if(IsRunningOutOfBrowser)
{
_mainWindow = Application.Current.MainWindow;
}
HookCloseEvent(true);
}
private void HookCloseEvent(bool hook)
{
if (hook && IsRunningOutOfBrowser)
{
_mainWindow.Closing += OnClosing;
}
else
{
if (IsRunningOutOfBrowser)
{
_mainWindow.Closing -= OnClosing;
}
}
}
private void OnClosing(object sender, ClosingEventArgs e)
{
InvokeClosing(e);
}
... etc...
}
The m_ClosingEvent isn´t nulled.
So, try to just store the "initial" MainWindow in a field and check if that solves your problem.
Instead of hooking to the event, why not register a service instead? Create a class that implements IApplicationService and IApplicationLifetimeAware. The latter gives you an "onexiting" and "onexited" pair of events. You place the service in the application by pointing to it in a section called in your App.xaml. I've used this for many projects and never had an issue with the exiting methods not being called.
Ok, after pulling out my hair and many false starts I finally found the answer - it seems to be a known bug with the Closing event, OOB and ChildWindows open/closes...
The trick is to store a static reference to the Main Window:
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
//you have to store this to work around the bug
//http://forums.silverlight.net/forums/p/185664/424174.aspx
_mainWindow = App.GetApp.MainWindow;
App.GetApp.MainWindow.Closing += (s, e1) =>
{
if (UIUtilities.ShowMessage("Would you like to exit AMT Mobile?", "Exit Application", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
{
e1.Cancel = true;
}
};
}
Is there an event in WinForms that get's fired when a window is dragged?
Or is there a better way of doing what I want: to drop the window opacity to 80% when the window is being dragged around?
Unfortunately this is stupidly tricky to search for because everyone is looking for drag and drop from the shell, or some other object.
No need for WndProc hacking, this works fine:
protected override void OnResizeBegin(EventArgs e) {
this.Opacity = 0.6;
}
protected override void OnResizeEnd(EventArgs e) {
this.Opacity = 1.0;
}
Moves also trigger the OnResizeXxx events.
It's the LocationChanged event you want:
private void YourApp_LocationChanged(object sender, EventArgs e)
{
this.Opacity = 0.8;
}
You'll have to override WndProc and handle the exit move event to reset the opacity back to 1:
protected override void WndProc(ref Message m)
{
Trace.WriteLine(m.ToString());
switch (m.Msg)
{
case WMEXITSIZEMOVE:
this.Opacity = 1.0;
break;
}
base.WndProc(ref m);
}
Not forgetting to define the message code:
private const int WMEXITSIZEMOVE = 0x0232;
It might be more efficient to handle the WM_ENTERSIZEMOVE (code 0x0231) message instead of LocationChanged as this would only result in setting the opacity once (at the start of the drag) rather than continually throughout the drag.