How to find cause of WPF Window location change? - c#

Whenever I resume my computer from hibernation, it seems that my Window's location shifts to the top of the screen. I have the following to log my Window location:
LocationChanged += (sender, args) =>
{
Logger.Info($"{Top},{SettingsHelper.Instance.Settings.WindowTop}");
};
WindowTop is a property that is loaded into memory from a file that keeps track of where the Window last was before it was closed. Whenever this prints on startup, both values are correct as expected. However, after resuming from hibernation, this prints a 0 for the actual value, while the setting value is still the same. That eliminates an incorrect setting value being loaded from being the cause.
I'm also attempting to reload the settings whenever the computer resumes from hibernation:
private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
if (e.Mode == PowerModes.Resume)
{
LoadUiSettings();
}
}
private void LoadUiSettings()
{
Left = SettingsHelper.Instance.Settings.WindowLeft;
Top = SettingsHelper.Instance.Settings.WindowTop;
}
This also does not resolve the issue. However, when I add in a MessageBox before the load, it'll show the Window at the incorrect location, then it'll properly load the settings and switch to the correct one. Since I moved my window away from the original location, I can see that on resume, it'll fix the location correctly, then somehow, after unlocking my computer, it'll move itself to Top = 0. Strange thing is that Left is not plagued by this issue at all. All the usages of both properties in the solution are identical. This does not happen with a vanilla WPF application, so I'm not really sure how to investigate this further. Strangely enough, this does not occur if I just lock the computer manually, and not by hibernating.
Is there a way to find out what is changing my Window's Top property somehow?
Edit: my workaround is to use SystemEvents.SessionSwitch and SessionSwitchReason.SessionUnlock and call LoadUiSettings from there, but that's still a bandaid around the actual problem. I'd still like to figure out what's causing it and solve it at the root.
Edit 2: If the Window isn't currently visible, setting it still does not change the actual position. I temporarily overrode the Visibility, then performed the following:
while (Math.Abs(Top - SettingsHelper.Instance.Settings.WindowTop) > 0.1)
{
Top = SettingsHelper.Instance.Settings.WindowTop;
}
I see the Window show up for a split second while it's waiting to be adjusted, then after it disappears again, and I toggle the visibility manually through the UI, it's at the top again. Toggling it manually when it's already in the correct location does not yield this behavior.

Pertinent to your specific question (i.e. finding the CAUSE of the problem, but not asking for the solution), the answer may be found using the following code snippet to perform analysis of WPF Window position change corresponding to PowerModeChange event:
SystemEvents.PowerModeChanged += OnPowerChange;
private void OnPowerChange(object s, PowerModeChangedEventArgs e)
{
switch ( e.Mode )
{
case PowerModes.Resume:
// Log the position as it's done in your example
Logger.Info ("Mode Change: Resume");
Logger.Info($"{Top},{SettingsHelper.Instance.Settings.WindowTop}");
break;
case PowerModes.Suspend:
Logger.Info ("Mode Change: Suspend");
Logger.Info($"{Top},{SettingsHelper.Instance.Settings.WindowTop}");
break;
}
}
Upon finding the root cause of your problem (namely, which event causes the Window dislocation), you may develop relatively simply fix, like for e.g. re-positioning the Window on PowerModes.Resume event.
Hope this may help.

Related

UI function firing before quick event finishes

I have a web browser frame docked in a Silverlight application, and sometimes full-window XAML/Silverlight UI elements can pop up over it. The old problem, which I've more-or-less fixed, is that the web frame's contents didn't seem to play nice with the Silverlight content, always wanting to be rendered on top. This isn't really an issue now since I have an event that fires whenever an application-obscuring popup appears, but now there's a new problem.
My code that launches pop-ups looks like this:
public void OnModuleShown()
{
if (ModuleShown != null)
ModuleShown(this, new ModuleShownEventArgs());
}
public void ShowModule (string uri, string headerTitle, string message, string transition = "DefaultTransition")
{
// Security and destination validation
GetInfoFromURI(uri, out contentKey, out dataKey, out securityToken);
OnModuleShown();
ShowLoadingSpinner();
_loadModule(contentKey, dataKey, securityToken, headerTitle, message, transition);
}
My code that handles the event looks like this:
private void Shell_ModuleShown(object sender, ModuleShownEventArgs e)
{
if (browserFrame.Visibility == Visibility.Visible)
{
browserFrame.Visibility = Visibility.Collapsed;
return;
}
}
The new problem is that even though I call the event before I start loading and displaying the new module, and even though all the event is doing is changing a web frame's visibility, the module tends to appear first if the loading time is short. Since the module's appearance is animated, it looks even worse since the web frame seems to be waiting for the module to finish its animation before it vanishes.
Questions
Is there some kind of threading method I can use to address this? I really don't want to use Thread.Sleep but it's the only one I know of that would fix this without large program changes I can't make. Even better would be if there was a way to get this web frame to play along with Z-indexes or something similar.
I am using Visual Studio 2013, and my project's .NET Framework version is 4.0.

Black areas on inherited ToolStrip [duplicate]

I use StatusStrip that contains ToolStripStatusLabel. OS - Windows 7, framework 2.0.
Usually all displayed normal, but sometimes ToolStripStatusLabel looks like black box:
I read that windows bug, but how I can fix it?
This is an obscure bug, triggered when you display the form with the Windows toolbar overlapping your StatusStrip. Moving the window away from the toolbar doesn't get the ToolStripItems on the status strip repainted properly. You'll find a bit of background in this forum post. There was a weak promise for a future fix for it, no idea if that ever happened. Probably not if you are running this on Win7.
You'll need to pay more attention to the position of the window, making sure that parts of it don't disappear underneath the toolbar. In general something you'd always consider, extra important as long as this bug doesn't get fixed. If you don't want to nail down the startup position (you ought to, users tend to like a window getting redisplayed where they last moved it) then simply change the form's StartPosition property to "CenterScreen".
This bug has never been fixed. It was in framework 2 and is still in framework 4.
The answer from Hans is a copy of the answer in social.msdn.microsoft.com.
But it is not helpful for me because "CenterScreen" does not solve the problem.
The cause of the problem is not the Windows Taskbar. The cause is a bug that does not draw the StatusStrip when the main Form is behind ANY other window at the first moment of drawing the StatusStrip. But this will also happen when you start the new process with Process.Start() from another process and the new process opens behind the window of another process.
I found a much better solution than the one proposed by Microsoft.
First I tried with
statusStrip.Invalidate();
but it does not work. So we need a stronger way to force Windows to redraw the StatusStrip. Important: The redrawing must happen when the Form with the StatusStrip is ALREADY in foreground! This is so easy that I don't understand why Microsoft does not suggest this method.
Timer mi_StatusTimer = new Timer();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
mi_StatusTimer.Interval = 500;
mi_StatusTimer.Tick += new EventHandler(OnTimerBugFix);
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
mi_StatusTimer.Start();
}
void OnTimerBugFix(object sender, EventArgs e)
{
mi_StatusTimer.Stop();
statusStrip.Hide();
Application.DoEvents();
statusStrip.Show();
}

Binding height, width, etc to a variable

I don't know if I'm just not understanding what I've found so far, or if it really is too complex to apply to such a simple idea as it seems. I'm trying to bind a button's height and width to variables that are stored in user settings. I can't seem to get this to work, or rather I simply don't how to, as in what commands to use. The issue lies in not knowing what to put in the Binding field of the xaml. If anyone could point to a guide that involves just this, could explain what to do I would be very appreciative.
Edit: I've solved the problem of binding the variable, it now saves to the User setting file when it should. Now I'm having an issue with the value stored in user setting beig overwritten every time the program loads with the default value. I am running this through VS debug menu selection, so I suppose the issue could lie there, but I've tried publishing it and running and still getting the same results. Any ideas?
Assuming by 'User Settings' you mean the built-in Settings not a custom implementation:
See http://blogs.windowsclient.net/bragi/archive/2008/07/03/using-settings-in-wpf-or-how-to-store-retrieve-window-pos-and-loc.aspx for an example of this - essentially you want to set up TwoWay bindings to Properties.Settings.Default: note that you have to define the settings in advance using the Settings UI, and you have to call Properties.Settings.Default.Save() when the app exits to persist the settings.
I'm posting this answer so that hopefully somebody else can read it and avoid such a ridiculous problem. First off, as far as the initial question, Staurt answered it quite nicely. But my edit above brought up a new but related problem. I ended up fixing it on accident.
The whole purpose of this was that I have a slider bar that adjusts the size of a shortcut button dock. The slider worked, but as I said above it would reset itself every time I reloaded. The issue in this case was that I have the buttons set to resize as the slider moves, so I used the slider_ValueChanged event as you can see here:
private void iconSizeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
try
{
Properties.Settings.Default.iconHeight = Convert.ToInt32(iconSizeSlider.Value);
Properties.Settings.Default.iconWidth = Convert.ToInt32(iconSizeSlider.Value * 1.3);
Properties.Settings.Default.Save();
//iconWidth.Text = buttonWidth.ToString();
//ButtonRefresh();
}
catch (FormatException)
{
}
}
While trying to use the Run To Cursor part of VS2010, I got tired of having to F11 through a multitude of loading steps, so as a debugging tool I added a bool fullyInitialized flag. This solved the problem completely. Apparently (which I didn't realize before), when the slider was first initialized it considered the value to have changed, so when it ran through the ValueChanged method, it reset everything to default. So adding a simple conditional around the try-catch to check for the fullyInitialized flag solved everything. Hopefully this helps somebody else.

Maintaining tab order when controls are disabled and enabled again at unpredictable times

So let me warn you; I am asking for a way to make a total hack work somewhat better. I admit that it is a hack and am certainly open to different takes on the problem as a whole. That said, I need to get this in soon if I want to make code cutoff and we have a somewhat aggressive release date.
As such, I will not be able to make large changes immediately, but I can easily do so for the first patch to this software. So, short and long term solutions are welcome, but a short term solution (if possible) is preferable.
Ok, so here is the issue; I have an application that send commands to a robotic hardware device. After a command is sent that requires a wait (for example, a physical motion that takes an unknown amount of time) the UI goes into a "Busy State". When this occurs all controls that would issue another blocking command are disabled as the machine cannot process them.
When the UI comes out of a busy state all controls are once again enabled, but the tab order is not maintained for obvious reasons. This makes tabbing through input areas impossible and, seeing as I myself use the keyboard almost exclusively, is not acceptable. My current scheme to "solve" this problem is to:
At startup, register to the GotFocus event for each control of interest in the application. This is made difficult due to the fact that this is a WPF MVVM app and also because some controls are created dynamically. Nevertheless, I can search the visual and/or logical trees and get by...
In the GotFocus event, save a reference to the control.
When we exit a busy state, attempt to set focus to the saved control.
This works... kinda sorta. The issue at hand (as I can think of more fail scenarios...) is that this logic will blow away a context menu if it was open and another. Consider:
Focus is in a text area.
User right clicks another control. This control does not get focus (even if I try to set it in a mouse handler).
System goes into a busy state as the right click performed a move.
When the busy state ends, text area is given focus and the context menu closes.
(Now I realize that you may say that performing a move on a right click and also displaying a context menu is a bad idea, but the context menu commands are non-blocking, enabled, and it has a domain specific use that is convenient.)
So there it is. I can't even get focus in a right click, and setting focus to the menu itself doesn't work either. I'm just curious if anyone has a better scheme for something like this. I realize it is awkward and a very narrow circumstance. Thanks for any help you can offer in advance, I will be playing around with this some more...
Interesting question and sadly i cannot think of an immediate solution to the problem, i would try to work around the issue by not blocking the application at all if this is such a frequent thing.
If the roboter can only take one command at a time it might work to just implement a command-queue so that the interface can still be used and while the roboter is busy issued commands get deferred. In terms of usability it might be a good idea to make this queue very visible so it is apparent that the command has been issued and will be taken into account.
How about instead of actually disabling controls you just do a check when they are invoked if they should send the command or not in the beginning of their event handlers. eg:
if(!bControlsEnabled) { return; }
You could also change the controls style when they should be "disabled".
Implement a custom behavior that listens when your control's enabled changes, saves the focused element and refocuses that element when enabled changes back to true:
public class KeyboardFocus
{
public static bool GetReturnFocusAfterEnable(DependencyObject obj)
{
return (bool)obj.GetValue(ReturnFocusAfterEnableProperty);
}
public static void SetReturnFocusAfterEnable(DependencyObject obj, bool value)
{
obj.SetValue(ReturnFocusAfterEnableProperty, value);
}
private static Dictionary<object, IInputElement> values = new Dictionary<object, IInputElement>();
// Using a DependencyProperty as the backing store for ReturnFocusAfterEnable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ReturnFocusAfterEnableProperty =
DependencyProperty.RegisterAttached("ReturnFocusAfterEnable", typeof(bool), typeof(KeyboardFocus), new UIPropertyMetadata(false, PropertyChangedCallback));
static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged += (element_IsEnabledChanged);
}
}
else
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged -= (element_IsEnabledChanged);
}
}
}
static void element_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (values.ContainsKey(sender))
{
Keyboard.Focus(values[sender]);
values.Remove(sender);
}
}
else
{
values[sender] = Keyboard.FocusedElement;
}
}
}
This could then be implemented in XAML as such:
<Grid Name="layout" local:KeyboardFocus.ReturnFocusAfterEnable="True">
This works in my test setup, but if you press a button before disabling your stuff, that button would have keyboard focus at the time the thing gets disabled, and this fails.
This solution is independant of your architecture, and does not require code in the form. It's a bit quick and dirty but it does the job.
Workaround for this :-Instead of disabling the control You should use XamBusyIndicator as a parent of that control and set the property for IsBusy. If IsBusy is true it will disable the child control and also TabIndex property will run in a sensible manner.
For example:-
<igComboEditor:XamBusyIndicator IsBusy="{Binding IsBusy}" Height="22">
<igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:CustomBusyAnimation DataTemplate="{StaticResource CustomBusyIndicatorTemplate}" />
</igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:XamComboEditor Width="125" VerticalAlignment="Bottom" Style="{StaticResource XamComboEditorStyle}"
ItemsSource="{Binding DataList}" IsEditable="False"
SelectedItem="{Binding SelectedData}">
</igComboEditor:XamComboEditor>
</igComboEditor:XamBusyIndicator>
I would
OnCommandSentThatRequiresAWait Event Call SaveStateAndDisableUI()
SaveStateAndDisableUI()
{
Foreach control in controlsCollection
{
switch(controlType)
{
for each control extract and save all you need.
if it's a menu check if it is opened up and so on,
does control have focus,
where is the caret located in case of textbox etc
}
}
save position of mouse x,y
save position of form, state like if it is minimized, maximized
}
Corresponding RestoreState() should restore everything back up.

NavigationWindow history is not saving states properly

I have a NavigationWindow (window1) and a custom navigationstate.
What I currently am using to do my navigation is as such:
a function (navigate(string,bool) ) which takes the location (a URL) that I want to go to, plus a boolean which defines if I should make a Back entry (i.e. I've gone into a folder)
A seperate function which ties into my NavigationService (allowing me to go back/forth within my history)
My problem though becomes that when I navigate Back, I start overriding my history!
Here's my NavigationService_Navigating(...) (which gets called when I push the back/forth button)
void NavigationService_Navigating(object sender, NavigatingCancelEventArgs e)
{
try // If something goes wrong, just bail.
{
// If we're going backwards, we want to remember the current location.
if (e.NavigationMode == NavigationMode.Back) { e.ContentStateToSave = new GopherNavState(cLocation); }
// use our internal navigation to move to the location, but dont create a back entry.
navigate((e.TargetContentState as GopherNavState).tLocation, false);
}
catch
{ } // ...
}
the problem occurs sporatically. I'll create 3/4 entries in my back, go back and see that my history is full of the page I'm currently looking at.
I've tried everything, but I still cant get it right.
I've found the source of my heartache: the history menu. Turns out, the fact I was using the chrome from the NavigationWindow was causing my headaches.
To fix this, I've simply turned off navigation controls within the window and made my own (buttons that have the command BrowseBack and BrowseForward).

Categories