Advisable to touch the codes in Designer.cs file? - winforms

Is it advisable for me to touch the designer.cs file for my windows form application? As I am referencing the labels to my settings file, I plan to code it in such a way that if my settings file shows
int startup = 0;
the code handler for my combo box selection would be
if (settingObject.bootOnStartup == 0)
{
comboStartup.SelectedIndex = 0;
}
else
{
comboStartup.SelectedIndex = 1;
}
it does works for its function but it sort of crashes the design window.

Don't modify the .designer.cs files, ever. Whatever you do in it, it will be overwritten the next time you edit your Form in the designer, so you will have to do it again. I don't see any reason not to put this code in the Form constructor or the Load event...

Your last sentence is the answer. If anything needs special treatment, do it in the user part of the partial class. Even if in your case (I'm speculating) this would require creating a ComboBox manually before the designer code runs.
The only time I touch the designer code is when I simply and quickly want to change something that I'm sure will survive the code generation of VS, e.g. a property name in a binding.

The Designer.cs file tells you not to modify:
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent( ) {
/* ...control customization from designer... */
}
If you have modifications then make those in your Form constructor after InitializeComponent:
public MainForm( ) {
InitializeComponent( );
if (settingObject.bootOnStartup == 0) {
comboStartup.SelectedIndex = 0;
} else {
comboStartup.SelectedIndex = 1;
}
}
... or, on a this.Load event:
public MainForm( ) {
InitializeComponent( );
this.Load += (s, e) => {
if (settingObject.bootOnStartup == 0) {
comboStartup.SelectedIndex = 0;
} else {
comboStartup.SelectedIndex = 1;
}
};
}

Related

Scrolling in Sticky Notes

I got the following sticky note example:
If the sticky note has more than 9 rows, the additional rows are not visible.
I'm able to navigate through the note with my arrow keys. If I'm going to scroll with the mouse wheel, it seems to ignore the popup and just changes the page.
Is it possible to activate scrolling for sticky note popups?
Edit:The solution outlined below will soon be available as part of the samples included in the PDFTron SDK download. In the meanwhile, I hope that the below solution helps.
Yes, it is possible to activate scrolling for sticky notes.
The problem is most apparent when using the single page view. It appears to work as expected in continuous mode.
However it is not as simple as setting VerticalScrollVisibility = ScrollBarVisibility.Auto;. There are a few files that need to be modified to get this working.
The good news is that we can get the expected behaviour by modifying the code in the provided samples.
Solution
The solution is to add some handling for the PreviewMouseWheel event coming from the PDFViewWPF class.
In the downloaded samples, the following changes were made to get things running as expected:
Add a method to handle the PreviewMouseWheel event in the NoteHost class (Samples/PDFViewWPFTools/CS/Utilities/NoteHost.cs)
internal void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var originalSource = (UIElement)e.OriginalSource;
if (originalSource.IsDescendantOf(mNoteBorder) && mTextBox.IsFocused)
{
mTextBox.ScrollToVerticalOffset(mTextBox.VerticalOffset - e.Delta);
e.Handled = true;
}
}
Also make sure to add mTextBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; in the NoteHost.CreateNoteAndArrow() method, after the mTextBox object is instantiated (~line 183).
Next, edit the NoteManager class - Samples/PDFViewWPFTools/CS/Utilities/NoteManager.cs - and add a HandlePreviewMouseWheel method. This will internally call the HandlePreviewMouseWheel on each displayed (opened) note and break at the first one where the event gets handled.
internal void HandlePreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
foreach(var note in mActiveNotes)
{
note.Value.HandlePreviewMouseWheel(sender, e);
if(e.Handled)
{
break;
}
}
}
Next, edit the ToolManager class to ensure that the note manager gets a chance to handle the PreviewMouseWheel before attempting a page change. Open Samples/PDFViewWPFTools/CS/ToolManager.cs and navigate to the PDFView_PreviewMouseWheel. The existing method should look like this:
private void PDFView_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
if (mCurrentTool != null && _IsEnabled)
{
ToolManager.ToolType prev_tm = mCurrentTool.ToolMode;
ToolManager.ToolType next_tm;
while (true)
{
mCurrentTool.PreviewMouseWheelHandler(sender, e);
next_tm = mCurrentTool.NextToolMode;
if (prev_tm != next_tm)
{
mCurrentTool = CreateTool(next_tm, mCurrentTool);
prev_tm = next_tm;
}
else
{
break;
}
}
}
}
Replace it with the below code:
private void PDFView_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
if (mCurrentTool != null && _IsEnabled)
{
ToolManager.ToolType prev_tm = mCurrentTool.ToolMode;
ToolManager.ToolType next_tm;
while (true)
{
mNoteManager.HandlePreviewMouseWheel(sender, e);
if (!e.Handled)
{
mCurrentTool.PreviewMouseWheelHandler(sender, e);
next_tm = mCurrentTool.NextToolMode;
if (prev_tm != next_tm)
{
mCurrentTool = CreateTool(next_tm, mCurrentTool);
prev_tm = next_tm;
}
else
{
break;
}
}
else
{
break;
}
}
}
}
By doing the above, we are giving the NoteManager a chance to handle the PreviewMouseWheel before doing anything else with it.
Another point to note is that we have to now "do the scrolling" in code, using the mTextBox.ScrollToVerticalOffset method in the NoteHost class.

Remove a ScreenSpaceLines3D Object from a ViewPort?

I am making a 3D Game with WPF in VB, and I am using a ScrennSpaceLines3D Object I found
http://3dtools.codeplex.com/releases/view/2058
but when I try to remove a line I added to the viewport by using
mainViewport.Children.RemoveAt(i)
it gives a NullExceptionError. I have read that this is because it does not totally come off the rendering queue. There have been fixes for c#, but I have yet to find one that works with VB. Is there a way to make this work or possibly draw a line in 3D space some other way? I find it quite ridiculous that VB doesn't even have a way to easily draw 3D lines...
Remove ScreenSpaceLines3D :
foreach (ScreenSpaceLines3D line3D in lines3DList)
{
lines3D.Points.Clear(); // Very importante
_viewport3D.Children.Remove(lines3D);
}
I'm a bit late to the party but i'm having the same issues.
The access violation occurs because each instance registers an event handler to the Rendering event of the composition target
public ScreenSpaceLines3D()
{
...
CompositionTarget.Rendering += OnRender; // <-- this line
}
but forgets to remove it when the instance is removed from the scene.
So to fix this you need to touch the source code:
public ScreenSpaceLines3D()
{
...
// event registration removed
}
private bool AttachedToCompositionTargetRendering { get; set; }
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
var parent = VisualTreeHelper.GetParent(this);
if (parent == null)
{
if (AttachedToCompositionTargetRendering)
{
CompositionTarget.Rendering -= OnRender;
AttachedToCompositionTargetRendering = false;
}
}
else
{
if (!AttachedToCompositionTargetRendering)
{
CompositionTarget.Rendering += OnRender;
AttachedToCompositionTargetRendering = true;
}
}
}

reactivate exiting window using WindowManager

I am using WPF with the currently latest and greatest version of Caliburn.Micro (1.4.1). I use IWindowManager.ShowWindow(...) to open an new modeless window:
private void OpenOrReactivateInfoView()
{
if(this.infoViewModel == null)
{
this.infoViewModel = new InfoViewModel();
}
this.windowManager.ShowWindow(this.infoViewModel);
}
Instead of opening a new window each time when OpenOrReactivateInfoView() is called, I would like to check whether the window ist still open and if it is, the existing window should just regain focus.
What would we be a good Calibrun.Micro-way to solve this? I sure would like to avoid keeping a reference to the window (or any UIElement for that matter) itself in the viewmodel. Also note that this is a common behavior for a lot of modeless dialogs, so it is preferred solve this in a generic reusable way.
Does Caliburn.Micro already have means for this built in?
The WindowManager source code always creates a new window, so what you really want to do is only use the WindowManager.ShowWindow method if you actually intend to create a new window.
The first thing you want to do is hold a persistent reference to your view model like this:
private readonly InfoViewModel infoViewModel = new InfoViewModel();
private void OpenOrReactivateInfoView()
{
this.windowManager.ShowWindow(this.infoViewModel);
}
Then, in your view model, create a method called Focus or whatever you want like this:
public void Focus()
{
var window = GetView() as Window;
if (window != null) window.Activate();
}
Then revisit your OpenOrReactivateInfoView() method make a slight adjustment like this:
private void OpenOrReactivateInfoView()
{
if (!this.infoViewModel.IsActive)
this.windowManager.ShowWindow(this.infoViewModel);
else
this.infoViewModel.Focus();
}
This method worked for me.
A fairly straightforward way to keep track of your windows without actually
having to implement IViewAware would be to keep a dictionary of weak references
to your ViewModels and Views that go together and then checking if you already
have an existing Window or not. Could be implemented either as a decorator to
the WindowManager, subclass or extension.
Something as simple as the following should do the trick assuming you don't
actually plan on opening enough windows that even the dead WeakReferences
would impact performance. If it is going to be long running it shouldn't be
that hard to implement some sort of cleanup.
public class MyFancyWindowManager : WindowManager
{
IDictionary<WeakReference, WeakReference> windows = new Dictionary<WeakReference, WeakReference>();
public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
NavigationWindow navWindow = null;
if (Application.Current != null && Application.Current.MainWindow != null)
{
navWindow = Application.Current.MainWindow as NavigationWindow;
}
if (navWindow != null)
{
var window = CreatePage(rootModel, context, settings);
navWindow.Navigate(window);
}
else
{
var window = GetExistingWindow(rootModel);
if (window == null)
{
window = CreateWindow(rootModel, false, context, settings);
windows.Add(new WeakReference(rootModel), new WeakReference(window));
window.Show();
}
else
{
window.Focus();
}
}
}
protected virtual Window GetExistingWindow(object model)
{
if(!windows.Any(d => d.Key.IsAlive && d.Key.Target == model))
return null;
var existingWindow = windows.Single(d => d.Key.Target == model).Value;
return existingWindow.IsAlive ? existingWindow.Target as Window : null;
}
}
I have come up with this extension method. It works but I am not particulary happy with it, it is still somewhat hackish.
It is clearly a designsmell that this extension has to make so many assumption about the model (do you see also those nasty exceptions?).
using System;
using System.Collections.Generic;
using Caliburn.Micro;
public static class WindowManagerExtensions
{
/// <summary>
/// Shows a non-modal window for the specified model or refocuses the exsiting window.
/// </summary>
/// <remarks>
/// If the model is already associated with a view and the view is a window that window will just be refocused
/// and the parameter <paramref name="settings"/> is ignored.
/// </remarks>
public static void FocusOrShowWindow(this IWindowManager windowManager,
object model,
object context = null,
IDictionary<string, object> settings = null)
{
var activate = model as IActivate;
if (activate == null)
{
throw new ArgumentException(
string.Format("An instance of type {0} is required", typeof (IActivate)), "model");
}
var viewAware = model as IViewAware;
if (viewAware == null)
{
throw new ArgumentException(
string.Format("An instance of type {0} is required", typeof (IViewAware)), "model");
}
if (!activate.IsActive)
{
windowManager.ShowWindow(model, context, settings);
return;
}
var view = viewAware.GetView(context);
if (view == null)
{
throw new InvalidOperationException("View aware that is active must have an attached view.");
}
var focus = view.GetType().GetMethod("Focus");
if (focus == null)
{
throw new InvalidOperationException("Attached view requires to have a Focus method");
}
focus.Invoke(view, null);
}
}

How to create input gesture for page/window from an user control

I have a reusable usercontrol that uses a few commands and corresponding keyboard gestures,
(specifically Escape and Ctrl+1...Ctrl+9)
Now as I use this usercontrol in multiple locations I'd like to define the input gestures in the usercontrol, which works fine as long as the focus is within the usercontrol. However, I'd need it to work as long as focus is within the current page/window.
How can I do it, or do I really have to do command/input bindings on every page?
You could handle the Loaded event of the UserControl and walk up the logical tree to find the owning page/window, then you can add the bindings there.
e.g.
public partial class Bogus : UserControl
{
public Bogus()
{
Loaded += (s, e) => { HookIntoWindow(); };
InitializeComponent();
}
private void HookIntoWindow()
{
var current = this.Parent;
while (!(current is Window) && current is FrameworkElement)
{
current = ((FrameworkElement)current).Parent;
}
if (current != null)
{
var window = current as Window;
// Add input bindings
var command = new AlertCommand();
window.InputBindings.Add(new InputBinding(command, new KeyGesture(Key.D1, ModifierKeys.Control)));
}
}
}

WPF doesn't honor Textbox.MinLines for Auto height calculation

I want to have a TextBox which Height grows as Iam entering lines of Text.
I've set the Height property to "Auto", and so far the growing works.
Now I want that the TextBox's Height should be at least 5 lines.
Now I've set the MinLines property to "5" but if I start the app the TextBox's height is still one line.
Try setting the MinHeight property.
A hack to make the MinLines property work
public class TextBoxAdv : TextBox
{
bool loaded = false;
/// <summary>
/// Constructor
/// </summary>
public TextBoxAdv()
{
Loaded += new RoutedEventHandler( Control_Loaded );
SetResourceReference( StyleProperty, typeof( TextBox ) );
}
void Control_Loaded( object sender, RoutedEventArgs e )
{
if( !loaded )
{
loaded = true;
string text = Text;
Text = "Text";
UpdateLayout();
Text = text;
}
}
}
I propose a different solution that properly respects the MinLines property, rather than forcing you to use MinHeight.
First, start with a convenience method to allow you to Post an action to the window loop. (I'm including both one where you need to pass state and one where you don't.)
public static class Globals {
public static void Post(Action callback){
if(SynchronizationContext.Current is SynchronizationContext currentContext)
currentContext.Post( _ => callback(), null);
else{
callback();
}
}
public static void Post<TState>(TState state, Action<TState> callback){
if(SynchronizationContext.Current is SynchronizationContext currentContext)
currentContext.Post(_ => callback(state), null);
else{
callback(state);
}
}
}
Next, create an extension method for TextBox to 'initialize' the proper size based on MinLines. I put this in a Hacks class because to me, that's what this is and it clearly identifies the code as such.
public static void FixInitialMinLines(this TextBox textBox) {
Globals.Post(() => {
var textBinding = textBox.GetBindingExpression(TextBox.TextProperty)?.ParentBinding;
if (textBinding != null) {
BindingOperations.ClearBinding(textBox, TextBox.TextProperty);
textBox.UpdateLayout();
BindingOperations.SetBinding(textBox, TextBox.TextProperty, textBinding);
}
else {
var lastValue = textBox.Text;
textBox.Text = lastValue + "a";
textBox.UpdateLayout();
textBox.Text = lastValue;
}
});
}
The above code handles both bound and unbound TextBox controls, but rather than simply changing the value like other controls which may cascade that change down through the bindings, it first disconnects the binding, forces layout, then reconnects the binding, thus triggering the proper layout in the UI. This avoids unintentionally changing your bound sources should the binding be two-way.
Finally, simply call the extension method for every TextBox where MinLines is set. Thanks to the Post call in the extension method, You can call this immediately after InitializeComponent and it will still be executed after all other events have fired, including all layout and the Loaded event.
public partial class Main : Window {
public Main() {
InitializeComponent();
// Fix initial MinLines issue
SomeTextBoxWithMinLines.FixInitialMinLines();
}
...
}
Add the above code to your 'library' of functions and you can address the issue with a single line of code in all of your windows and controls. Enjoy!

Resources