WPF closing child- closes parent-window - wpf

i have the pretty same sample as mentioned here.
Fast concluded: MainWindow closes when the last childwindow is closed.
My Problem: I couldn't solve my problems with the described solutions. I can't produce a program where it als takes place. Only in one of my bigger progs. Maybe someone has an idea or knows any further steps.
Thanks for reading - Thomas
As requested here's a bit of code:
This is the part in the MainWindow:
bool editAfterSearch = false;
Movie selectedMovie = (Movie)this.listView.SelectedItem;
Movie backup = (Movie)selectedMovie.Clone();
if (new OnlineSearchWindow().EditMovieViaOnlineSearch(ref selectedMovie, out editAfterSearch))
{
this.coverFlow.Update(selectedMovie);
}
And that's the part of the ChildWindow:
public bool EditMovieViaOnlineSearch(ref Movie preset, out bool editAfter)
{
this.exitWithOk = false;
this.editMovieAfterSearch = false;
this.tbx_SearchTerm.Text = preset.Title;
this.linkedMovie = preset;
this.ShowDialog();
editAfter = editMovieAfterSearch;
if (this.exitWithOk)
{
this.linkedMovie.CloneOnlineInformation(ref preset);
preset.Bitmap = this.linkedMovie.Bitmap;
return true;
}
else
{
return false;
}
}

Try playing with the ShutDownMode property of your App.xaml.cs. The 3 values are OnMainWindowClose, OnLastWindowClose, and OnExplicitShutdown, and the default is OnLastWindowClose
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.ShutdownMode = System.Windows.ShutdownMode.OnMainWindowClose;
}
}

The below code worked for me.
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (MessageBoxResult.No == (MessageBox.Show("Are you sure you want to close this?.", "ProjectName", MessageBoxButton.YesNo)))
{
e.Cancel = true;
foreach (var item in Application.Current.Windows)
{
Window window = item as Window;
if (window.Title == "PopUpWindowName")
{
window.Topmost = true;
break;
}
}
return;
}
else
{
base.OnClosed(e);
Application.Current.Shutdown();
}
}

you can try setting the child window's allowShutDown to false and then show the mainwindow. I'm assuming you will start with mainwindow's visibility set to hidden.
Application.Current.MainWindow.Visibility = System.Windows.Visibility.Visible;
this.allowShutDown = false;
The allowShutDown will be your own property which u can set to enable you have to handle the closing event.

Related

wpf how to get mouse position in container when dragging controls inside?

I want to get mouse position of container while dragging controls inside so I can add auto-scroll logic to container. However, MouseMove does not fired at all when dragging, DragOver fired only when dragging over controls inside.
test example
Draggable gizmo:
public class Gizmo : TextBlock
{
public Gizmo()
{
this.AllowDrop = true;
this.Background = Brushes.Gray;
this.Margin = new System.Windows.Thickness(6);
}
public Gizmo(string content) : this()
{
this.Text = content;
}
private bool isDragging;
private Point lastPressedLocation;
protected override void OnPreviewMouseMove(System.Windows.Input.MouseEventArgs e)
{
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
{
if (!this.isDragging)
{
Point newLocation = e.GetPosition(this);
Vector offset = this.lastPressedLocation - newLocation;
if (offset.LengthSquared > 36)
{
this.lastPressedLocation = newLocation;
this.isDragging = true;
System.Windows.DragDrop.DoDragDrop(this, DateTime.Now, DragDropEffects.Move);
}
else
{
this.isDragging = false;
}
}
}
}
private bool canDrop;
protected override void OnPreviewDragEnter(DragEventArgs e)
{
Console.WriteLine("drag enter inside");
if (this.Text == "gizmo 1")
{
e.Effects = DragDropEffects.Move;
this.canDrop = true;
}
else
{
e.Effects = DragDropEffects.None;
this.canDrop = false;
}
e.Handled = true;
base.OnPreviewDragEnter(e);
}
protected override void OnPreviewDragOver(DragEventArgs e)
{
Console.WriteLine("drag over inside");
if (this.canDrop)
{
e.Effects = DragDropEffects.Move;
}
else
{
e.Effects = DragDropEffects.None;
e.Handled = true;
}
base.OnPreviewDragOver(e);
}
}
container:
public class Container : WrapPanel
{
protected override void OnInitialized(EventArgs e)
{
for (int i = 1; i <= 16; i++)
this.Children.Add(new Gizmo(string.Format("gizmo {0}", i)));
base.OnInitialized(e);
}
protected override void OnPreviewDragEnter(System.Windows.DragEventArgs e)
{
Console.WriteLine("drag enter outside");
base.OnPreviewDragEnter(e);
}
protected override void OnPreviewDragOver(System.Windows.DragEventArgs e)
{
//I want to get mouse postion here, but this will be called only when dragging over gizmo inside
Console.WriteLine("drag over outside");
base.OnPreviewDragOver(e);
}
}
running result and question
or it's just impossible?
The last function in your code should work. Alternatively (since there should be no other elements handling the event before you) you can use the OnDragOver method instead of the Preview.
protected override void OnDragOver(DragEventArgs e)
{
Point position = e.GetPosition(this);
}
If it doesn't work, that usually means that specific area of your control is not hit-test visible. Make sure IsHitTestVisible is true (has to be, otherwise child elements wouldn't work either) and that the Background of your control is not null. If you want no background and still be able to be hit-test visible, use Transparent for the background.

When opening same window multiple times and use current window instance, throws null reference exception

When i open the same window multiple times, the last one becomes as 'Current.MainWindow' and for other windows this 'Current' instance is null. Ofcourse when I'm trying to instantiate it, it throws null reference exception. On each window i have button which will hide/show all controls inside that window + change it's opacity. Maybe there is another way to do that, or rather than using Current.MainWindow instance use something else?
Method that wil change window opacity:
private void btnHideShow_Click(object sender, RoutedEventArgs e)
{
if (this._hide)
{
Application.Current.MainWindow.Background.Opacity = 0;
this._hide = false;
//...
}
else
{
Application.Current.MainWindow.Background.Opacity = 0.1;
this._hide = true;
//...
}
}
If that code is the code behind of the window, you simply can do:
private void btnHideShow_Click(object sender, RoutedEventArgs e)
{
if (this._hide)
{
this.Background.Opacity = 0;
this._hide = false;
//...
}
else
{
this.Background.Opacity = 0.1;
this._hide = true;
//...
}
}
Also you may give name to your controls with the x:Name attr:
<Grid x:Name="LayoutRoot"></Grid>
Then you can use it in the code behind.

Detecting input keystroke during WPF processing

Greetings,
I want to write code that executes within an event handler, inside a WPF Windows application, that can detect a keypress, specifically an "Escape" character keypress, within a processing loop. This will allow the user to escape from processing. I realize this may be accomplished with some kind of multi-threaded approach, but the problem seems so simple I wondered if it might be accomplished as follows:
// Attempt 1: See if Keyboard static IsKeyDown method detects key presses while executing.
// Note that this was not successful. The Keyboard states do not appear to be updated during processing.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (Keyboard.IsKeyDown(Key.Enter))
iskeypressed = true;
}
So, on to attempt #2. I saw some articles and samples using the Pinvoke "GetKeyboardState" method. I'm not sure I used the method correctly, but here is my attempt. It is a bit clumsy to refer to a Windows.Forms enumeration in a WPF application, but it seems like it could work.
// Attempt 2: Use Pinvoke GetKeyboardState method.
// So far, I've been unsuccessful with this as well, but I'm not sure my usage is correct.
bool iskeypressed = false;
while (!iskeypressed)
{
System.Threading.Thread.Sleep(1000);
if (isEscapePressed())
iskeypressed = true;
}
}
[DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] lpKeyState);
private bool isEscapePressed()
{
byte[] keyboardState = new byte[255];
int keystate = GetKeyboardState(keyboardState);
if (keyboardState[(int)System.Windows.Forms.Keys.Escape] == 128)
return true;
else
return false;
}
But unfortunately, I'm not seeing any change in the keyboard states as this executes. I also played around a little with calls to the Dispatcher to see if I could get the keyboard information to refresh during processing, but I have not been successful with any technique.
I'm out of ideas. Can someone propose something? Thank you in advance for your assistance.
David
Something like this:
private bool IsCancelled { get; set; }
private void OnButtonClick(object sender, EventArgs e)
{
Action doWorkDelegate = DoWork;
doWorkDelegate.BeginInvoke(null, null);
}
protected override void OnKeyDown(KeyEventArgs e) {
if (e.Key == Key.Escape) {
IsCancelled = true;
e.Handled = true;
} else {
base.OnKeyDown(e);
}
}
private void DoWork()
{
IsCancelled = false;
while (!IsCancelled)
{
System.Threading.Thread.Sleep(1000);
}
}
The important point is that the method that does the work is executed in a separate thread so the main thread can process user input (key strokes).
You can not detect a key event while you are blocking WPF by executing a very long loop. You must use a multithreaded approach or you have to split the loop.
Using a BackgroundWorker is an easy way to let WPF continue handling the frontend while executing the loop.
private BackgroundWorker bw;
private void Button_Click(object sender, RoutedEventArgs e)
{
if (bw != null)
return;
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += (senderBw, eBw) =>
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
bw.ReportProgress(i);
if (eBw.Cancel)
return;
}
};
bw.ProgressChanged += (senderBw, eBw) =>
{
//TODO set progressbar to eBw.ProgressPercentage
};
bw.RunWorkerCompleted += (senderBw, eBw) =>
{
this.bw = null;
//TODO frontend stuff (hide progressbar etc)
};
bw.RunWorkerAsync();
}
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (this.bw != null && this.bw.IsBusy && e.Key == Key.Escape)
this.bw.CancelAsync();
}

Disable right click "Silverlight" popup in comboboxes

Hi
I'm trying to get rid of the annoying "About Silverlight" context menu that pops up whenever you right click in a Silverlight application. I've added the usual ways:
In App.xaml
rootVisual.MouseRightButtonDown += ((s, args) => args.Handled = true);
and the same for all ChildWindows.
The problem that persist is in all "pop up"-controls like comboboxes and datepicker calender popup. There I can't get rid of it. I would like to handle the right click in a style that I can make implicit for the entire application. Is this possible? Can I solve it some other smart way?
Best
Daniel
The answer was to inherit the combobox and make a custom control like this:
public class CellaComboBox : ComboBox
{
public CellaComboBox()
{
DropDownOpened += _dropDownOpened;
DropDownClosed += _dropDownClosed;
}
private static void _dropDownClosed(object sender, EventArgs e)
{
HandlePopupRightClick(sender, false);
}
private static void _dropDownOpened(object sender, EventArgs e)
{
HandlePopupRightClick(sender, true);
}
private static void HandlePopupRightClick(object sender, bool hook)
{
ComboBox box = (ComboBox)sender;
var popup = box.GetChildElement<Popup>();
if (popup != null)
{
HookPopupEvent(hook, popup);
}
}
static void HookPopupEvent(bool hook, Popup popup)
{
if (hook)
{
popup.MouseRightButtonDown += popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown += popup_MouseRightButtonDown;
}
else
{
popup.MouseRightButtonDown -= popup_MouseRightButtonDown;
popup.Child.MouseRightButtonDown -= popup_MouseRightButtonDown;
}
}
static void popup_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
}
with the extension method for framworkelement looking like this:
public static class FrameworkElementExtensions
{
public static TType GetChildElement<TType>(this DependencyObject parent) where TType : DependencyObject
{
TType result = default(TType);
if (parent != null)
{
result = parent as TType;
if (result == null)
{
for (int childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); ++childIndex)
{
var child = VisualTreeHelper.GetChild(parent, childIndex) as FrameworkElement;
result = GetChildElement<TType>(child) as TType;
if (result != null) return result;
}
}
}
return result;
}
}
You need to handle the DatePicker in the same way but instead of DropDownOpened and DropDownClosed you use CalenderOpened and CalenderClosed
C# Corner has an article for fixing the about popup on Silverlight 3:
Disable Context Menu in Silverlight 3 Application

Keep a Silverlight Combobox DropDown open after losing focus

Is it possible to avoid the automatic collapse of a Silverlight ComboBox after LostFocus?
Well, looking at the disassembled code it looks like
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
this.FocusChanged(this.HasFocus());
}
is a good candidate for overwritting.
There's no way to solve your problem without implementing your own control subclass.
I've done the same to have a ComboBox with a Popup that doesn't close when I select an item (I want to have a multi-select behaviour).
If anyone is interested, here are my classes (works just fine for me as it is):
public class ComboBoxWithMultiSelect : ComboBox
{
protected override void OnKeyDown(KeyEventArgs e)
{
if (base.IsDropDownOpen &&
(e.Key == Key.Enter ||
e.Key == Key.Space))
{
e.Handled = true;
}
else
{
base.OnKeyDown(e);
}
}
protected override DependencyObject GetContainerForItemOverride()
{
return new ComboBoxItemWithMultiSelect();
}
}
public class ComboBoxItemWithMultiSelect : ComboBoxItem
{
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (e == null)
{
throw new ArgumentNullException("e");
}
if (!e.Handled)
{
e.Handled = true;
}
}
}
I don't think there is an easy way around this. The code below is copied from the disassembled code from the ComboBox Class. As you can see it closes always when hasFocus is false. I don't think there is any way around this. Writing your own ComboBox is a solution.
private void FocusChanged(bool hasFocus)
{
this.UpdateSelectionBoxHighlighted();
base.SetValueInternal(IsSelectionActiveProperty, hasFocus, true);
if (!hasFocus)
{
this.IsDropDownOpen = false;
}
}

Resources