Saving WPF WIndow and Position - wpf

What would be the best way to save the window position and size in a WPF app?
Currently, I'm saving the window size and position of a WPF App. Here are the events I handle:
SourceInitialized : The saved info is loaded on to the window
WindowClosing : The current info is saved to the backing store
(I copied this from an example).
The problem is, when the window is minimized and restored, the settings from the last WindowClosing is retrieved.
Now, the StateChanged event fire AFTER the window has minimized, so it does not seem to be what i need.
Thanks

Do yourself and your users a favor and use the LocationChanged event and the SizeChanged event to save the settings at that time. There's nothing more annoying than an application that gets amnesia if the process exits abnormally and settings don't get saved (cough...explorer...cough...)
Then just check to make sure the WindowState == Normal before saving the settings. Obviously its pointless to save the position of a minimized or maximized window.
As for when to load the settings, well that you can just do in the constructor after the InitializeComponent call or you can use the Initialized event. No real reason to use the SourceInitialized event unless you are doing something with the HWND directly which shouldn't be necessary.

Use the WindowInteropHelper object to get the window handle and use Screen.FromHandle method to get the actual screen the window is on. When saving make sure to also save the screen bounds just in case it does not exist any more.
One caveat when restoring the screen to its former state is it has to be done after the window handle is created so can't do it in the constructor else won't work properly in multiple monitor situations. Try doing it on the SourceInitialized callback

Are you doing this via databinding? That is the way I do my window sizing and position. I typically have a UserConfig.xml file that is saved in the Users Profile. Then I create elements in there as I databind them in the program. I have the Application.xaml resource dictionary refer to that file, and all of the settings I want set to XPaths inf the XML. Then I just save the in memory xml document on exit. Only one event to handle, no mess, no fuss.
And you can expand it to encompass as many settings as you like in regard to the UI. Adding plumbing settings is a little more difficult, but not terribly so.

I have a solution for saving Size and State, you can extend it to also save the Position. It's done using a Behavior. Simply Binding the Width and Height did not work as expected, because it would overwrite the "Normal" state's size with the maximized sizes. That's why there are some extra checks like if(state == normal)
There is a Config Property on my Window's DataContext.
You'll need a reference to System.Windows.Interactivity to do that.
public class MainWindowSaveStateBehavior : Behavior<Window>
{
public Config Config
{
get { return (Config)GetValue(ConfigProperty); }
set { SetValue(ConfigProperty, value); }
}
public static readonly DependencyProperty ConfigProperty =
DependencyProperty.Register("Config", typeof(Config), typeof(MainWindowSaveStateBehavior), new PropertyMetadata(Config_Changed));
private static void Config_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var b = d as MainWindowSaveStateBehavior;
if(e.NewValue != null) b.LoadSettings();
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SizeChanged += Window_SizeChanged;
AssociatedObject.StateChanged += Window_StateChanged;
LoadSettings();
}
bool _initialized = false;
private void Window_StateChanged(object sender, EventArgs e)
{
SaveSettings();
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
SaveSettings();
}
private void LoadSettings()
{
if (Config == null) return;
AssociatedObject.Width = Config.WindowWidth;
AssociatedObject.Height = Config.WindowHeight;
AssociatedObject.WindowState = Config.WindowState;
_initialized = true;
}
private void SaveSettings()
{
if (Config == null || !_initialized) return;
Config.WindowState = AssociatedObject.WindowState;
if(AssociatedObject.WindowState == WindowState.Normal)
{
Config.WindowWidth = AssociatedObject.Width;
Config.WindowHeight = AssociatedObject.Height;
}
}
}
In Xaml use the behavior by adding the namespaces
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:b="<the namespace your behavior lives in>"
And then attach the Behavior
<i:Interaction.Behaviors>
<b:MainWindowSaveStateBehavior Config="{Binding Config}" />
</i:Interaction.Behaviors>
You then just have to Load and Save the Config in your DataContext on startup/shutdown.

I liked CodeWarriors answer. I use TwoWay binding to apps settings:
Height="{Binding Source={x:Static p:Settings.Default}, Path=WindowHeight, Mode=TwoWay}"
Width="{Binding Source={x:Static p:Settings.Default}, Path=WindowWidth, Mode=TwoWay}"
Top="{Binding Source={x:Static p:Settings.Default}, Path=WindowTop, Mode=TwoWay}"
Left="{Binding Source={x:Static p:Settings.Default}, Path=WindowLeft, Mode=TwoWay}"
where p - project's properties namespace.

Related

WPF Richtextbox Bindable in .net 4.5

So I'm trying to use David Veeneman's Bindable WPF RichTextBox here in my .net 4.5 project. After adding the control and the ValueConverter in my code I noticed only the the public object Convert() will be triggered but the public object ConvertBack() not.
After reading the comments to this project I changed following parts of the control source code.
private static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var thisControl = (EcoRichTextBox)d;
if (thisControl.m_InternalUpdatePending > 0)
{
thisControl.m_InternalUpdatePending--;
return;
}
// Changed:
try
{
thisControl.TextBox.Document = (e.NewValue == null) ? new FlowDocument() : (FlowDocument)e.NewValue;
}
catch { }
thisControl.m_TextHasChanged = false;
}
And this Event Handler:
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
// Set the TextChanged flag
m_TextHasChanged = true;
// Changed:
Document = TextBox.Document;
}
Now the the both method of the ValueConverter worked fine but events like private void OnNormalTextClick(object sender, RoutedEventArgs e) causes a FatalExecutionEngineError on Runtime.
So i wonder if there are major changes form WPF 3.5 to 4.5?
Or anybody have an idea to work around this?
Update
Binding in XAML
<uc:FsRichTextBox Margin="5"
Document="{Binding Path=Ereignis.Bericht,
Converter={StaticResource flowDocumentConverter},
UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}" />
I ran the demo you linked here in VS2015 with target framework 4.0 and 4.5. It will not update when I take out the two way data binding.
Add to your RTB. Two way data binding and a name:
Mode=TwoWay
x:Name="EditBox"
I think rather than managing the text change yourself here, remove this:
// Changed:
Document = TextBox.Document;
Use an event handler to update the data.
Then in your event handler that is managing your updates (I am assuming a button click? And allow this to manage the update.
this.EditBox.UpdateDocumentBindings();
The x:name attribute is valuable.
This is all found in the source code.
If you can be more clear about how your project is arranged I can provide more detail. But for starters, I would do this. Stick more closely to the provided example.

Deferred loading of XAML

A project I'm working on has some rather complex XAML that is noticeably affecting visual performance. Quite a few controls are collapsed for the initial state; however, since their XAML is parsed and visual /logical trees built, it's very slow to show what amounts to an almost blank object.
It looks like (and would like confirmation here) that using a ContentControl with an initial state of Collapsed and then embedding the desired control as a DataTemplate for that ContentControl, will defer loading of the desired control in the DataTemplate until the ContentControl is made visible.
I've built a generic DeferredContentControl that listens for the LayoutUpdated event of the main UI control (in general whatever element it is that I want to appear quickly), and when the first LayoutUpdated event of that UIElement fires, I used the Dispatcher to flip the visibility of the DeferredContentControl to true, which causes the control in the DeferredContentControl's DataTemplate to instantiate. By the time the user has reacted to the initial view of the screen (which is now fast), the "slow to load" (but still collapsed) control in the data template is ready.
Does this seem like a sound approach? any pitfalls? It seems to work well in testing both for Silverlight and WPF, and while it doesn't make things any faster it gives the perception of being as much as 50% snappier in my specific scenario.
I had the same problem (in a Silverlight project), and solved it in nearly the same way. It proved to be working as expected, have not encountered any pitfalls yet.
When you need to control the point in time when xaml is parsed and view elements are instantiated you can always use DataTemplates (not necessarily in cunjuction with ContentControl). You can call DataTemplate.LoadContent() to instatiate it, you don't have to switch the visibility of a ContentControl (although internally this will result in such a LoadContent call).
Have a look at my implementation if you want, it can even display a static text message while the heavier VisualTree is build:
<DeferredContent HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<DeferredContent.DeferredContentTemplate>
<DataTemplate>
<MyHeavyView/>
</DataTemplate>
</Controls:DeferredContent.DeferredContentTemplate>
<TextBlock Text="Loading content..."/>
</Controls:DeferredContent>
and the code
public class DeferredContent : ContentPresenter
{
public DataTemplate DeferredContentTemplate
{
get { return (DataTemplate)GetValue(DeferredContentTemplateProperty); }
set { SetValue(DeferredContentTemplateProperty, value); }
}
public static readonly DependencyProperty DeferredContentTemplateProperty =
DependencyProperty.Register("DeferredContentTemplate",
typeof(DataTemplate), typeof(DeferredContent), null);
public DeferredContent()
{
Loaded += HandleLoaded;
}
private void HandleLoaded(object sender, RoutedEventArgs e)
{
Loaded -= HandleLoaded;
Deployment.Current.Dispatcher.BeginInvoke(ShowDeferredContent);
}
public void ShowDeferredContent()
{
if (DeferredContentTemplate != null)
{
Content = DeferredContentTemplate.LoadContent();
RaiseDeferredContentLoaded();
}
}
private void RaiseDeferredContentLoaded()
{
var handlers = DeferredContentLoaded;
if (handlers != null)
{
handlers( this, new RoutedEventArgs() );
}
}
public event EventHandler<RoutedEventArgs> DeferredContentLoaded;
}

Control Initialization Order Fiasco

Consider the following code:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<Slider ValueChanged="slider_ValueChanged/>
<TextBox x:Name="counter"/>
</StackPanel>
</Window>
and
namespace Project1
{
public partial class Window1 : Window
{
public MainWindow() { InitializeComponent(); }
void slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
counter.Text = e.NewValue.ToString();
}
}
}
Slider will raise its ValueChanged event during initialization while counter is still null.
This is an example of a larger problem that I've been running into using WPF, that UI events can fire at any time, and that there is no single place where I can put my initialization code so that it's guaranteed to run after all the pointers owned by the WPF system have been initialized but before any UI events have fired.
What is the most elegant way to deal with this? The fact that this specific example should use data binding is beside the point.
There are many ways to deal with this, depending on your situation
First off, you could simply recognize the fact that the object might not be initialized and check for that before processing. For example,
if (counter.Text != null)
counter.Text = e.NewValue.ToString();
Second, you could attach your events in the Loaded event of the object so they don't fire until after the object has been initialized.
void Counter_Loaded(object sender, EventArgs e)
{
slider.ValueChanged += Slider_ValueChanged;
}
void Counter_Unloaded(object sender, EventArgs e)
{
slider.ValueChanged -= Slider_ValueChanged;
}
And last of all, you can use WPF's Dispatcher to run events on the UI thread at a different DispatcherPriority. The default is Normal, which runs after Loaded, Render, and DataBind operations
Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
new Action(delegate() { counter.Text = e.NewValue.ToString(); }));
The true answer to this question is to use the MVVM pattern where window code behind files contain little to no initialization code.
In this pattern, the UI is connected to the rest of the code with data binding only. You write special view-model classes that implement INotifyPropertyChanged and take your business logic and expose it as a series of properties that UI binds to.
Naturally, you fully control how your view-models initialize.

How can I undo a TextBox's text changes caused by a binding?

I have a TextBox to which i bound a string, if i now edit the text manually i will be able to undo those changes via TextBox.Undo(), if however i change the string and the TextBox's text is updated, i cannot undo those changes and the TextBox.CanUndo property will always be false.
I suppose this might have to do with the complete replacement of the text rather than a modification of it.
Any ideas on how i can get this to work?
I was facing the same issue (needed to accept input upon Enter and revert to original value upon Escape) and was able to handle it this way:
Set UpdateSourceTrigger of your TextBox.Text binding to Explicit.
Handle KeyDown event of your TextBox and put the following code in there:
if (e.Key == Key.Enter || e.Key == Key.Escape)
{
BindingExpression be = ((TextBox)sender).GetBindingExpression(TextBox.TextProperty);
if (e.Key == Key.Enter)
{
if (be != null) be.UpdateSource();
}
else if (e.Key == Key.Escape)
{
if (be != null) be.UpdateTarget(); //cancels newly supplied value and reverts to the original value
}
}
I found this solution to be very elegant because it can be used in DataTemplates too. For example in my case I used it to allow in-place editing of ListBox items.
OK, started to leave a comment and realized it was an answer :)
TextBox.Undo() is intended to undo a user's interaction with the text box not a value change in the property it's bound to. A change in the property the text box is bound to will just update the value of the TextBox, this is a different change than a user edit via focus/keyboard. If you need to Undo changes to your bound properties you probably need to investigate adding an Undo/Redo stack to your application.
Assign directly to the TextBox:
textBox.SelectAll();
textBox.SelectedText = newText;
The TextBox will apply the changes to the internal undo stack if they are applied in such a way that they appear to have come from the user, like so:
Clipboard.SetText("NewTextHere");
TextBox.Paste();
It's a terrible workaround, as it kills whatever the user has on the clipboard (the restoring of which is pessimistically discussed here: How do I backup and restore the system clipboard in C#?) but I thought it might be worth having posted nonetheless.
So, I think the ViewModel Undo/Redo article is a good one, but it's as much as about the ViewModel pattern as it is about how to write custom Undo/Redo functionality. Also, in response to confusedGeek, I think there could be examples where undoing changes in your model, not just in your individual controls is appropriate (say you had a textbox and a slider both bound to the sample property, you want to undo a change regardless of which control made it, so we're talking about app level undo instead of control level).
So given that, here is a simple, if not somewhat kludgey example of doing precisely what you ask using a CommandBinding and a simplistic undo stack:
public partial class MainWindow : Window
{
public static readonly DependencyProperty MyStringProperty =
DependencyProperty.Register("MyString", typeof(String), typeof(MainWindow), new UIPropertyMetadata(""));
// The undo stack
Stack<String> previousStrings = new Stack<String>();
String cur = ""; // The current textbox value
Boolean ignore = false; // flag to ignore our own "undo" changes
public String MyString
{
get { return (String)GetValue(MyStringProperty); }
set { SetValue(MyStringProperty, value); }
}
public MainWindow()
{
InitializeComponent();
this.LayoutRoot.DataContext = this;
// Using the TextChanged event to add things to our undo stack
// This is a kludge, we should probably observe changes to the model, not the UI
this.Txt.TextChanged += new TextChangedEventHandler(Txt_TextChanged);
// Magic for listening to Ctrl+Z
CommandBinding cb = new CommandBinding();
cb.Command = ApplicationCommands.Undo;
cb.CanExecute += delegate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
};
cb.Executed += delegate(object sender, ExecutedRoutedEventArgs e)
{
if (previousStrings.Count > 0)
{
ignore = true;
this.Txt.Text = previousStrings.Pop();
ignore = false;
}
e.Handled = true;
};
this.CommandBindings.Add(cb);
}
void Txt_TextChanged(object sender, TextChangedEventArgs e)
{
if (!ignore)
{
previousStrings.Push(cur);
}
cur = this.Txt.Text;
}
private void SetStr_Click(object sender, RoutedEventArgs e)
{
this.MyString = "A Value";
}
}
And here is the XAML:
<Window x:Class="TestUndoBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Name="LayoutRoot">
<TextBox Name="Txt" Text="{Binding Path=MyString, Mode=TwoWay}" />
<Button Name="SetStr" Click="SetStr_Click">Set to "A Value"</Button>
</StackPanel>
</Window>
In this example the behavior is slightly different than typical TextBox undo behavior because 1) I'm ignoring selection, and 2) I'm not grouping multiple keystrokes into a single undo step, both of which are things you would want to consider in a real app, but should be relatively straightforward to implement yourself.

How can I toggle the main menu visibility using the Alt key in WPF?

I'd like the main menu in my WPF app to behave like the main menu in IE8:
it's not visible when the app starts
pressing and releasing Alt makes it visible
pressing and releasing Alt again makes it invisible again
repeat until bored
How can I do this? Does it have to be code?
Added in response to answers submitted, because I'm still having trouble:
My Shell code-behind now looks like this:
public partial class Shell : Window
{
public static readonly DependencyProperty IsMainMenuVisibleProperty;
static Shell()
{
FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
metadata.DefaultValue = false;
IsMainMenuVisibleProperty = DependencyProperty.Register(
"IsMainMenuVisible", typeof(bool), typeof(Shell), metadata);
}
public Shell()
{
InitializeComponent();
this.PreviewKeyUp += new KeyEventHandler(Shell_PreviewKeyUp);
}
void Shell_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.SystemKey == Key.LeftAlt || e.SystemKey == Key.RightAlt)
{
if (IsMainMenuVisible == true)
IsMainMenuVisible = false;
else
IsMainMenuVisible = true;
}
}
public bool IsMainMenuVisible
{
get { return (bool)GetValue(IsMainMenuVisibleProperty); }
set { SetValue(IsMainMenuVisibleProperty, value); }
}
}
You can use the PreviewKeyDown event on the window. To detect the Alt key you will need to check the SystemKey property of the KeyEventArgs, as opposed to the Key property which you normally use for most other keys.
You can use this event to set a bool value which has been declared as a DependencyProperty in the windows code behind.
The menu's Visibility property can then be bound to this property using the BooleanToVisibilityConverter.
<Menu
Visibility={Binding Path=IsMenuVisibile,
RelativeSource={RelativeSource AncestorType=Window},
Converter={StaticResource BooleanToVisibilityConverter}}
/>
I just came across this problem myself. I tried hooking into the PreviewKeyDown event, but found it to be unreliable. Instead I found the InputManager class where you can hook into the EnterMenuMode from managed code. The manager exposes two events, for enter and exit. The trick is to not collapse the menu, but set it's container height to zero when it is to be hidden. To show it, simply clear the local value and it will take its previous height.
From my TopMenu user control:
public TopMenu()
{
InitializeComponent();
InputManager.Current.EnterMenuMode += OnEnterMenuMode;
InputManager.Current.LeaveMenuMode += OnLeaveMenuMode;
Height = 0;
}
private void OnLeaveMenuMode(object sender, System.EventArgs e)
{
Height = 0;
}
private void OnEnterMenuMode(object sender, System.EventArgs e)
{
ClearValue(HeightProperty);
}
I'd try looking into handling the PreviewKeyDown event on your window. I'm not sure if pressing Alt triggers this event or not, but if it does, then I'd toggle a bool which is bound to the visibility of the main menu of the window.
If PreviewKeyDown doesn't work, I'm not sure what else to try. You could look into getting at the actual Windows messages sent to your window, but that could get messy very quickly.
It would be better to use GetKeyboardState with VK_MENU to handle both left and right Alt, to mimic the behavior of IE / Windows Explorer (Vista+) you'll need to track the previously focused element to store focus, on a VK_MENU press whilst the focused element is within your main menu. You also want to be doing this work on PreviewKeyUp (not down).
See my answer to the following thread:
How to make WPF MenuBar visibile when ALT-key is pressed?
There I describe how to solve your problem with the class InputManager (from namespace System.Windows.Input).
You can register the classes events EnterMenuMode and LeaveMenuMode.

Resources